fix: close ungated CLI merge bypasses in review_pr.py and merge_pr.py (#16)
Reviewer found the MCP merge surface is gated but two local CLI scripts remain ungated merge paths that LLM automations in this project have been using. Close them (Option B — minimal safe fix; full gated CLI merge left to a follow-up): - review_pr.py: `--merge` now fails closed BEFORE any API call with a clear message directing callers to the gated `gitea_merge_pr` MCP workflow. The review-only path is unchanged. The merge execution block was removed. - merge_pr.py: main() is now a fail-closed no-op — reads no credentials and makes no merge API call; prints that merge is only available via the gated workflow. - README: the `review_pr.py` row and Quick Examples no longer advertise a CLI `--merge` path; added an audit-logging clarification that #16 returns structured gate/merge results but does not add durable audit logging, which is tracked by #18. Tests updated/added: - test_review_pr.py: `--merge` fails closed with no API call; message points to the gated workflow. - test_merge_pr.py: merge fails closed with no API call, even with --force/--do/--title/--message; message points to the gated workflow. - test_mcp_server.py: README no longer advertises the ungated CLI merge example. The gated MCP `gitea_merge_pr` is unchanged and still gated; `gitea_review_pr` still fails closed on merge=True; `gitea_submit_pr_review` still cannot merge. No secrets, auth headers, raw env, or credential paths are exposed. No Jenkins/Ops/GlitchTip/Release/deploy/CI behavior added. #17/#18 not started. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+22
-25
@@ -1,7 +1,13 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Merge a Gitea pull request.
|
||||
"""Merge a Gitea pull request — DISABLED (#16).
|
||||
|
||||
Usage:
|
||||
Direct CLI merge is a fail-closed no-op. LLM automations in this project were
|
||||
using this script as an ungated merge bypass, so it no longer performs a merge.
|
||||
Merge is only available through the gated `gitea_merge_pr` MCP workflow (#16),
|
||||
which enforces identity/profile/eligibility, explicit confirmation, expected
|
||||
head SHA checking, and self-merge protection.
|
||||
|
||||
Usage (fails closed):
|
||||
merge_pr.py --pr-number 84 --do squash --remote dadeschools
|
||||
"""
|
||||
import os
|
||||
@@ -29,29 +35,20 @@ def main(argv=None):
|
||||
parser.add_argument("--force", action="store_true", help="Force merge, ignoring status checks.")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
host, org, repo = resolve_remote(args)
|
||||
auth = get_auth_header(host)
|
||||
if not auth:
|
||||
print(f"Could not get credentials for {host}.", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
url = f"{repo_api_url(host, org, repo)}/pulls/{args.pr_number}/merge"
|
||||
payload = {
|
||||
"Do": args.do,
|
||||
"force_merge": args.force,
|
||||
}
|
||||
if args.title:
|
||||
payload["MergeTitleField"] = args.title
|
||||
if args.message:
|
||||
payload["MergeMessageField"] = args.message
|
||||
|
||||
try:
|
||||
api_request("POST", url, auth, payload)
|
||||
print(f"Successfully merged PR #{args.pr_number} using '{args.do}' method.")
|
||||
return 0
|
||||
except Exception as e:
|
||||
print(f"Error merging PR #{args.pr_number}: {e}", file=sys.stderr)
|
||||
return 1
|
||||
# Fail closed: direct CLI merge is disabled (#16). No credentials are read
|
||||
# and no merge API call is made. Merge is only available through the gated
|
||||
# `gitea_merge_pr` MCP workflow, which enforces identity/profile/eligibility,
|
||||
# explicit confirmation, expected head SHA checking, and self-merge
|
||||
# protection.
|
||||
print(
|
||||
f"Direct CLI merge is disabled (#16). PR #{args.pr_number} was NOT "
|
||||
"merged. Merge is only available through the gated workflow (MCP tool "
|
||||
"'gitea_merge_pr'), which enforces identity/profile/eligibility, "
|
||||
"explicit confirmation, expected head SHA checking, and self-merge "
|
||||
"protection.",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 2
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user