feat(ui): external services tab with Azure DevOps PAT, draft PR button and link in company repos

This commit is contained in:
mika kuns
2026-10-01 12:13:57 +02:00
parent fed0b6ac51
commit 4976ded7ae
9 changed files with 247 additions and 15 deletions
@@ -676,6 +676,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
Merge.SyncTaskContext(row.Id, row.Title, row.IsPlanningParent);
Merge.SyncWorktree(WorktreePath, WorktreeBaseCommit, WorktreeHeadCommit,
WorktreeStateLabel, _listWorkingDir);
Merge.PullRequestUrl = entity.Worktree?.PullRequestUrl;
await AgentSettings.LoadForTaskAsync(entity, ct);
ct.ThrowIfCancellationRequested();
@@ -728,6 +729,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
foreach (var b in targets.LocalBranches) Merge.MergeTargetBranches.Add(b);
Merge.SelectedMergeTarget = targets.DefaultBranch;
Merge.TargetDirty = targets.TargetDirty;
Merge.IsCompanyRepo = targets.IsCompanyRepo;
}
}
await Merge.RefreshMergePreviewAsync();
@@ -779,6 +781,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
Merge.MergeTargetBranches.Add(b);
Merge.SelectedMergeTarget = targets.DefaultBranch;
Merge.TargetDirty = targets.TargetDirty;
Merge.IsCompanyRepo = targets.IsCompanyRepo;
}
}
}
@@ -826,6 +829,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
Merge.MergeTargetBranches.Add(b);
Merge.SelectedMergeTarget = targets.DefaultBranch;
Merge.TargetDirty = targets.TargetDirty;
Merge.IsCompanyRepo = targets.IsCompanyRepo;
}
}
}
@@ -939,6 +943,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
Merge.SyncWorktree(WorktreePath, WorktreeBaseCommit, WorktreeHeadCommit,
WorktreeStateLabel, _listWorkingDir);
Merge.PullRequestUrl = entity.Worktree?.PullRequestUrl;
}
catch { /* best-effort refresh */ }
}
@@ -1261,7 +1266,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
{
if (Task is null || !_worker.IsConnected) return;
var taskId = Task.Id;
using var op = ApproveOp.Begin(Loc.T("ops.merge.merging"));
// Company repo: approve = verify + push + draft PR; nothing merges locally.
var company = Merge.IsCompanyRepo;
using var op = ApproveOp.Begin(Loc.T(company ? "ops.merge.publishing" : "ops.merge.merging"));
// Subscribed only for the duration of the call: the worker broadcasts to every
// client, and a VM left on this event would keep reacting to merges for other tasks.
void OnOperationProgress(string opKey, string phase, int current, int total)
@@ -1276,7 +1283,14 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
try
{
var hasChildren = Subtasks.Count > 0 || ChildOutcomes.Count > 0;
var result = await _worker.ApproveReviewAsync(taskId, Merge.SelectedMergeTarget ?? "");
var result = await _worker.ApproveReviewAsync(taskId, company ? "" : Merge.SelectedMergeTarget ?? "");
if (company)
{
if (result?.PullRequestUrl is { } url) Merge.PullRequestUrl = url;
else if (result?.Status == "verify_failed") ErrorReported?.Invoke(result.ErrorMessage ?? Loc.T("vm.detailsIsland.verifyFailed"));
return;
}
// Nothing landed -- the target working dir has uncommitted changes (often another
// session's WIP in the shared main checkout). Offer the one-shot stash retry; git
@@ -1304,7 +1318,9 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
}
catch (Exception ex)
{
if (Dialogs != null)
// Company-repo failures (no PAT, push rejected, "gepusht, PR fehlgeschlagen") go to the footer strip.
if (company) ErrorReported?.Invoke(ex.Message);
else if (Dialogs != null)
await Dialogs.ShowErrorAsync(ex.Message);
}
finally