fix(worker): clean up orphaned worktree when the DB row insert fails
If WorktreeAddAsync succeeds but the worktrees-row insert throws, the worktree was left on disk and branch undeleted with nothing tracking it. Wrap the insert in try/catch and best-effort remove the worktree+branch (non-cancellable) before rethrowing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -158,6 +158,37 @@ public class WorktreeManagerTests : IDisposable
|
||||
Assert.Null(row);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CreateAsync_DbInsertFails_RemovesOrphanedWorktreeAndBranch()
|
||||
{
|
||||
if (!GitAvailable) { Assert.True(true, "git not available -- skipping"); return; }
|
||||
|
||||
var repo = CreateRepo();
|
||||
var (task, list) = MakeEntities(repo.RepoDir);
|
||||
|
||||
// Seed the list but NOT the task: the worktrees-row insert references
|
||||
// tasks(task_id) and fails the FK after `git worktree add` has succeeded.
|
||||
var db = new DbFixture();
|
||||
_dbFixtures.Add(db);
|
||||
using (var seedCtx = db.CreateContext())
|
||||
await new ListRepository(seedCtx).AddAsync(list);
|
||||
|
||||
var cfg = new WorkerConfig { WorktreeRootStrategy = "sibling" };
|
||||
var mgr = new WorktreeManager(
|
||||
new GitService(), db.CreateFactory(), cfg, NullLogger<WorktreeManager>.Instance);
|
||||
|
||||
await Assert.ThrowsAnyAsync<Exception>(
|
||||
() => mgr.CreateAsync(task, list, CancellationToken.None));
|
||||
|
||||
var branchName = $"claudedo/{task.Id.Replace("-", "")}";
|
||||
var branchList = GitRepoFixture.RunGit(repo.RepoDir, "branch", "--list", branchName);
|
||||
Assert.True(string.IsNullOrWhiteSpace(branchList),
|
||||
$"orphaned branch {branchName} should be cleaned up, got: {branchList}");
|
||||
|
||||
var worktreeList = GitRepoFixture.RunGit(repo.RepoDir, "worktree", "list");
|
||||
Assert.DoesNotContain(task.Id, worktreeList);
|
||||
}
|
||||
|
||||
private static (TaskEntity task, ListEntity list) MakeEntities(string workingDir)
|
||||
{
|
||||
var listId = Guid.NewGuid().ToString();
|
||||
|
||||
Reference in New Issue
Block a user