test(plan): add Behave scenarios for stale worktree cleanup (#7271)
CI / push-validation (pull_request) Successful in 23s
CI / helm (pull_request) Successful in 33s
CI / build (pull_request) Successful in 3m50s
CI / lint (pull_request) Successful in 4m4s
CI / quality (pull_request) Successful in 4m13s
CI / typecheck (pull_request) Successful in 4m39s
CI / security (pull_request) Successful in 4m47s
CI / integration_tests (pull_request) Successful in 8m26s
CI / e2e_tests (pull_request) Successful in 8m36s
CI / unit_tests (pull_request) Successful in 8m47s
CI / docker (pull_request) Successful in 1m39s
CI / coverage (pull_request) Successful in 15m1s
CI / docker (push) Blocked by required conditions
CI / status-check (push) Blocked by required conditions
CI / coverage (push) Blocked by required conditions
CI / benchmark-regression (push) Waiting to run
CI / benchmark-publish (push) Waiting to run
CI / status-check (pull_request) Successful in 5s
CI / push-validation (push) Successful in 42s
CI / helm (push) Successful in 1m10s
CI / build (push) Successful in 3m46s
CI / lint (push) Successful in 3m55s
CI / quality (push) Successful in 4m17s
CI / typecheck (push) Successful in 4m28s
CI / e2e_tests (push) Successful in 7m17s
CI / integration_tests (push) Successful in 7m50s
CI / unit_tests (push) Successful in 8m52s
CI / security (push) Failing after 15m17s
CI / benchmark-regression (pull_request) Has been cancelled
CI / benchmark-publish (pull_request) Has been cancelled
CI / push-validation (pull_request) Successful in 23s
CI / helm (pull_request) Successful in 33s
CI / build (pull_request) Successful in 3m50s
CI / lint (pull_request) Successful in 4m4s
CI / quality (pull_request) Successful in 4m13s
CI / typecheck (pull_request) Successful in 4m39s
CI / security (pull_request) Successful in 4m47s
CI / integration_tests (pull_request) Successful in 8m26s
CI / e2e_tests (pull_request) Successful in 8m36s
CI / unit_tests (pull_request) Successful in 8m47s
CI / docker (pull_request) Successful in 1m39s
CI / coverage (pull_request) Successful in 15m1s
CI / docker (push) Blocked by required conditions
CI / status-check (push) Blocked by required conditions
CI / coverage (push) Blocked by required conditions
CI / benchmark-regression (push) Waiting to run
CI / benchmark-publish (push) Waiting to run
CI / status-check (pull_request) Successful in 5s
CI / push-validation (push) Successful in 42s
CI / helm (push) Successful in 1m10s
CI / build (push) Successful in 3m46s
CI / lint (push) Successful in 3m55s
CI / quality (push) Successful in 4m17s
CI / typecheck (push) Successful in 4m28s
CI / e2e_tests (push) Successful in 7m17s
CI / integration_tests (push) Successful in 7m50s
CI / unit_tests (push) Successful in 8m52s
CI / security (push) Failing after 15m17s
CI / benchmark-regression (pull_request) Has been cancelled
CI / benchmark-publish (pull_request) Has been cancelled
Add 3 Behave scenarios covering GitWorktreeSandbox.cleanup_stale(): - Removes existing branch and worktree directory - Idempotent when no branch exists (returns False) - create() succeeds after cleanup_stale removes stale branch ISSUES CLOSED: #7271
This commit was merged in pull request #10798.
This commit is contained in:
@@ -16,6 +16,12 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Stale worktree branch cleanup on re-execute** (#7271): Re-executing a plan
|
||||
after a failed attempt or diff review crashed with `fatal: a branch named
|
||||
'cleveragents/plan-<id>' already exists`. Added `GitWorktreeSandbox.cleanup_stale()`
|
||||
classmethod that idempotently removes stale worktree directories and branches
|
||||
before creating a fresh sandbox.
|
||||
|
||||
- **Worktree sandbox cleanup on plan cancel** (#9230): `plan cancel` now removes
|
||||
the git worktree branch and directory created during execute, preventing resource
|
||||
leaks from dangling worktrees. Delegates to `GitWorktreeSandbox.cleanup_stale()`
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
@sandbox-reexecute-cleanup
|
||||
Feature: Stale worktree branch cleanup before re-execute (#7271)
|
||||
Verifies that re-executing a plan cleans up the stale worktree
|
||||
branch from the previous execution before creating a fresh sandbox.
|
||||
|
||||
Scenario: cleanup_stale removes existing branch and worktree for srec
|
||||
Given a temp git repo with a worktree branch for plan "01TESTREEXEC000000000000" for srec
|
||||
When I call cleanup_stale for plan "01TESTREEXEC000000000000" for srec
|
||||
Then the branch "cleveragents/plan-01TESTREEXEC000000000000" should not exist for srec
|
||||
And the worktree directory should not exist for srec
|
||||
|
||||
Scenario: cleanup_stale is idempotent when no branch exists for srec
|
||||
Given a temp git repo without any worktree branches for srec
|
||||
When I call cleanup_stale for plan "01TESTNOEXIST00000000000" for srec
|
||||
Then cleanup_stale should return False for srec
|
||||
|
||||
Scenario: create succeeds after cleanup_stale removes stale branch for srec
|
||||
Given a temp git repo with a worktree branch for plan "01TESTRECREATE0000000000" for srec
|
||||
When I call cleanup_stale for plan "01TESTRECREATE0000000000" for srec
|
||||
And I create a fresh sandbox for plan "01TESTRECREATE0000000000" for srec
|
||||
Then the fresh sandbox should be a directory for srec
|
||||
And the branch "cleveragents/plan-01TESTRECREATE0000000000" should exist for srec
|
||||
@@ -0,0 +1,132 @@
|
||||
"""Steps for sandbox_reexecute_cleanup.feature."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
|
||||
from behave import given, then, when
|
||||
|
||||
|
||||
def _git(args: list[str], cwd: str) -> subprocess.CompletedProcess[str]:
|
||||
return subprocess.run(
|
||||
["git", *args],
|
||||
cwd=cwd,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
timeout=10,
|
||||
)
|
||||
|
||||
|
||||
@given('a temp git repo with a worktree branch for plan "{plan_id}" for srec')
|
||||
def step_create_repo_with_branch(context: object, plan_id: str) -> None:
|
||||
d = tempfile.mkdtemp(prefix="srec-")
|
||||
context.add_cleanup(shutil.rmtree, d, True)
|
||||
_git(["init", "-q", "-b", "main"], d)
|
||||
_git(["config", "user.name", "T"], d)
|
||||
_git(["config", "user.email", "t@t"], d)
|
||||
_git(["config", "commit.gpgsign", "false"], d)
|
||||
Path(d, "file.py").write_text("content\n")
|
||||
_git(["add", "."], d)
|
||||
_git(["commit", "-q", "-m", "init"], d)
|
||||
|
||||
# Create a worktree branch (simulating a previous execute)
|
||||
branch = f"cleveragents/plan-{plan_id}"
|
||||
wt_dir = tempfile.mkdtemp(prefix="srec-wt-")
|
||||
context.add_cleanup(shutil.rmtree, wt_dir, True)
|
||||
_git(["worktree", "add", "-b", branch, wt_dir, "HEAD"], d)
|
||||
|
||||
context.srec_repo = d
|
||||
context.srec_wt_dir = wt_dir
|
||||
context.srec_branch = branch
|
||||
|
||||
|
||||
@given("a temp git repo without any worktree branches for srec")
|
||||
def step_create_clean_repo(context: object) -> None:
|
||||
d = tempfile.mkdtemp(prefix="srec-clean-")
|
||||
context.add_cleanup(shutil.rmtree, d, True)
|
||||
_git(["init", "-q", "-b", "main"], d)
|
||||
_git(["config", "user.name", "T"], d)
|
||||
_git(["config", "user.email", "t@t"], d)
|
||||
_git(["config", "commit.gpgsign", "false"], d)
|
||||
Path(d, "file.py").write_text("content\n")
|
||||
_git(["add", "."], d)
|
||||
_git(["commit", "-q", "-m", "init"], d)
|
||||
context.srec_repo = d
|
||||
|
||||
|
||||
@when('I call cleanup_stale for plan "{plan_id}" for srec')
|
||||
def step_call_cleanup_stale(context: object, plan_id: str) -> None:
|
||||
from cleveragents.infrastructure.sandbox.git_worktree import (
|
||||
GitWorktreeSandbox,
|
||||
)
|
||||
|
||||
context.srec_cleanup_result = GitWorktreeSandbox.cleanup_stale(
|
||||
context.srec_repo,
|
||||
plan_id,
|
||||
)
|
||||
|
||||
|
||||
@when('I create a fresh sandbox for plan "{plan_id}" for srec')
|
||||
def step_create_fresh_sandbox(context: object, plan_id: str) -> None:
|
||||
from cleveragents.infrastructure.sandbox.git_worktree import (
|
||||
GitWorktreeSandbox,
|
||||
)
|
||||
|
||||
sandbox = GitWorktreeSandbox(
|
||||
resource_id="res-srec-test",
|
||||
original_path=context.srec_repo,
|
||||
)
|
||||
ctx = sandbox.create(plan_id)
|
||||
context.srec_fresh_sandbox = ctx.sandbox_path
|
||||
context.srec_fresh_sandbox_obj = sandbox
|
||||
context.add_cleanup(sandbox.cleanup)
|
||||
|
||||
|
||||
@then('the branch "{branch_name}" should not exist for srec')
|
||||
def step_branch_not_exists(context: object, branch_name: str) -> None:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--verify", f"refs/heads/{branch_name}"],
|
||||
cwd=context.srec_repo,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=10,
|
||||
)
|
||||
assert result.returncode != 0, f"Branch {branch_name} still exists"
|
||||
|
||||
|
||||
@then('the branch "{branch_name}" should exist for srec')
|
||||
def step_branch_exists(context: object, branch_name: str) -> None:
|
||||
result = subprocess.run(
|
||||
["git", "rev-parse", "--verify", f"refs/heads/{branch_name}"],
|
||||
cwd=context.srec_repo,
|
||||
capture_output=True,
|
||||
check=False,
|
||||
timeout=10,
|
||||
)
|
||||
assert result.returncode == 0, f"Branch {branch_name} does not exist"
|
||||
|
||||
|
||||
@then("the worktree directory should not exist for srec")
|
||||
def step_worktree_gone(context: object) -> None:
|
||||
assert not os.path.exists(context.srec_wt_dir), (
|
||||
f"Worktree directory still exists: {context.srec_wt_dir}"
|
||||
)
|
||||
|
||||
|
||||
@then("cleanup_stale should return False for srec")
|
||||
def step_cleanup_returned_false(context: object) -> None:
|
||||
assert context.srec_cleanup_result is False, (
|
||||
f"Expected False but got {context.srec_cleanup_result}"
|
||||
)
|
||||
|
||||
|
||||
@then("the fresh sandbox should be a directory for srec")
|
||||
def step_fresh_sandbox_is_dir(context: object) -> None:
|
||||
assert os.path.isdir(context.srec_fresh_sandbox), (
|
||||
f"Fresh sandbox is not a directory: {context.srec_fresh_sandbox}"
|
||||
)
|
||||
Reference in New Issue
Block a user