feat: add root checkout guard for contaminated control checkout (Closes #475)
Introduce root_checkout_guard helper and enforce it in verify_preflight_purity so author, reviewer, and merger mutations fail closed when the stable control checkout is on the wrong branch, detached, dirty, or diverged from prgs/master. Isolated branches/... worktrees and reconciler paths remain allowed. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
@@ -126,17 +126,37 @@ class TestRuntimeContextGuardAlignment(unittest.TestCase):
|
||||
with mock.patch.dict("os.environ", {"GITEA_TEST_PORCELAIN": ""}, clear=False):
|
||||
srv.verify_preflight_purity(worktree_path=BRANCHES_WORKTREE)
|
||||
|
||||
def test_stable_checkout_still_rejected(self):
|
||||
@mock.patch("gitea_mcp_server.root_checkout_guard.resolve_remote_master_sha", return_value="a" * 40)
|
||||
@mock.patch(
|
||||
"gitea_mcp_server.issue_lock_worktree.read_worktree_git_state",
|
||||
return_value={
|
||||
"current_branch": "master",
|
||||
"head_sha": "a" * 40,
|
||||
"porcelain_status": "",
|
||||
},
|
||||
)
|
||||
def test_stable_checkout_still_rejected(self, _git, _remote_sha):
|
||||
with mock.patch.object(srv, "PROJECT_ROOT", CONTROL_ROOT):
|
||||
with mock.patch.dict("os.environ", {"GITEA_TEST_PORCELAIN": ""}, clear=False):
|
||||
with self.assertRaises(RuntimeError) as ctx:
|
||||
srv.verify_preflight_purity()
|
||||
self.assertIn("stable control checkout", str(ctx.exception))
|
||||
|
||||
@mock.patch("gitea_mcp_server.root_checkout_guard.resolve_remote_master_sha", return_value="a" * 40)
|
||||
@mock.patch(
|
||||
"gitea_mcp_server.issue_lock_worktree.read_worktree_git_state",
|
||||
return_value={
|
||||
"current_branch": "master",
|
||||
"head_sha": "a" * 40,
|
||||
"porcelain_status": "",
|
||||
},
|
||||
)
|
||||
@mock.patch("os.path.isdir", return_value=True)
|
||||
@mock.patch("os.path.exists", return_value=True)
|
||||
@mock.patch("subprocess.run")
|
||||
def test_non_branches_worktree_rejected(self, mock_run, *_exists):
|
||||
def test_non_branches_worktree_rejected(
|
||||
self, mock_run, mock_exists, mock_isdir, _git, _remote_sha,
|
||||
):
|
||||
outside = "/tmp/outside-repo-checkout"
|
||||
mock_run.return_value = MagicMock(
|
||||
returncode=0,
|
||||
|
||||
Reference in New Issue
Block a user