feat: thread worktree_path through gitea_create_issue (#450)
Hardens #274 branches-only guard for gitea_create_issue: - verify_preflight_purity: validate resolved worktree exists, is a directory, and belongs to the target repository before author mutations. - gitea_create_issue: add worktree_path threaded into preflight guard. - tests/test_create_issue_workspace_guard.py: stable-control-checkout rejection and invalid-path fail-closed cases (PROJECT_ROOT simulated as control checkout). Preserves WIP commit 8530109 implementation; test fix completes stable-checkout path. Closes #450
This commit is contained in:
+44
-4
@@ -440,10 +440,48 @@ def verify_preflight_purity(remote: str | None = None, worktree_path: str | None
|
||||
"Pre-flight order violation: Task capability (gitea_resolve_task_capability) has not been resolved (fail closed)"
|
||||
)
|
||||
|
||||
if worktree_path:
|
||||
dirty_files = sorted(_parse_porcelain_entries(_get_workspace_porcelain(worktree_path)))
|
||||
workspace = author_mutation_worktree.resolve_mutation_workspace(
|
||||
worktree_path,
|
||||
PROJECT_ROOT,
|
||||
active_worktree_env=os.environ.get(ACTIVE_WORKTREE_ENV),
|
||||
author_worktree_env=os.environ.get(AUTHOR_WORKTREE_ENV),
|
||||
)
|
||||
real_workspace = os.path.realpath(workspace)
|
||||
real_root = os.path.realpath(PROJECT_ROOT)
|
||||
|
||||
if real_workspace != real_root:
|
||||
if not _preflight_in_test_mode():
|
||||
if not os.path.exists(real_workspace):
|
||||
raise RuntimeError(
|
||||
f"Branches-only mutation guard (#274): worktree path '{workspace}' does not exist (fail closed)"
|
||||
)
|
||||
if not os.path.isdir(real_workspace):
|
||||
raise RuntimeError(
|
||||
f"Branches-only mutation guard (#274): worktree path '{workspace}' is not a directory (fail closed)"
|
||||
)
|
||||
try:
|
||||
res = subprocess.run(
|
||||
["git", "-C", real_workspace, "rev-parse", "--git-common-dir"],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True,
|
||||
)
|
||||
common_dir = os.path.realpath(res.stdout.strip())
|
||||
expected_dir = os.path.realpath(os.path.join(real_root, ".git"))
|
||||
if common_dir != expected_dir:
|
||||
raise RuntimeError(
|
||||
f"Branches-only mutation guard (#274): worktree '{workspace}' does not belong to the target repository '{PROJECT_ROOT}' (fail closed)"
|
||||
)
|
||||
except Exception as e:
|
||||
if isinstance(e, RuntimeError):
|
||||
raise e
|
||||
raise RuntimeError(
|
||||
f"Branches-only mutation guard (#274): worktree '{workspace}' is not a valid git repository (fail closed)"
|
||||
)
|
||||
|
||||
dirty_files = sorted(_parse_porcelain_entries(_get_workspace_porcelain(workspace)))
|
||||
if dirty_files:
|
||||
details = _preflight_workspace_details(worktree_path, dirty_files)
|
||||
details = _preflight_workspace_details(workspace, dirty_files)
|
||||
raise RuntimeError(
|
||||
"Pre-flight order violation: Active task workspace has tracked "
|
||||
"file edits before mutation (fail closed). "
|
||||
@@ -972,6 +1010,7 @@ def gitea_create_issue(
|
||||
repo: str | None = None,
|
||||
allow_duplicate_override: bool = False,
|
||||
split_from_issue: int | None = None,
|
||||
worktree_path: str | None = None,
|
||||
) -> dict:
|
||||
"""Create a new issue on a Gitea repository.
|
||||
|
||||
@@ -984,6 +1023,7 @@ def gitea_create_issue(
|
||||
repo: Override the repository name.
|
||||
allow_duplicate_override: Operator-approved split after duplicate found.
|
||||
split_from_issue: Existing duplicate issue number when overriding.
|
||||
worktree_path: Optional path to verify branches-only guard.
|
||||
|
||||
Returns:
|
||||
dict with 'number' of the created issue ('url' only with the reveal opt-in).
|
||||
@@ -1010,7 +1050,7 @@ def gitea_create_issue(
|
||||
)
|
||||
if blocked:
|
||||
return blocked
|
||||
verify_preflight_purity(remote)
|
||||
verify_preflight_purity(remote, worktree_path=worktree_path)
|
||||
base = repo_api_url(h, o, r)
|
||||
open_issues = api_get_all(f"{base}/issues?state=open&type=issues", auth)
|
||||
closed_issues = api_get_all(
|
||||
|
||||
Reference in New Issue
Block a user