diff --git a/scripts/gh-merge-green b/scripts/gh-merge-green index 0edda04cc813..c7f44fed7271 100755 --- a/scripts/gh-merge-green +++ b/scripts/gh-merge-green @@ -139,6 +139,13 @@ if [ -n "$override_reason" ]; then 'explain the concrete red check and why this exact head is safe to merge' fi fi +skip_vercel=0 +if [ -n "$override_reason" ] && [[ "$override_reason" =~ [Vv]ercel ]]; then + # Vercel is an optional preview deployment status in this repository. An + # explicit, auditable override may waive that one external status when it is + # pending or red; all other pending statuses remain merge blockers. + skip_vercel=1 +fi if [ "$fix" -eq 1 ] && [ -n "$override_reason" ]; then refuse \ 'not green: --override cannot be combined with --main-fix' \ @@ -477,6 +484,10 @@ if [ "$has_ci" -eq 0 ]; then while IFS=$'\t' read -r name st co; do [ -n "$name" ] || continue reported=$((reported + 1)) + if [ "$skip_vercel" -eq 1 ] && [ "$name" = Vercel ]; then + echo "note: --override waived Vercel $st/$co status" >&2 + continue + fi if [ "$st" != completed ]; then refuse \ "not green: '$name' is $st/$co on $sha (no ci-status workflow on base $base)" \ diff --git a/tests/test_gh_merge_green.py b/tests/test_gh_merge_green.py index acb0a854c258..e62160db7d23 100644 --- a/tests/test_gh_merge_green.py +++ b/tests/test_gh_merge_green.py @@ -686,7 +686,7 @@ class WorkflowPresenceRegression(unittest.TestCase): """Repositories without the aggregate workflow use all exact-head verdicts.""" def run_case(self, *, workflow=False, probe_status=404, checks=None, statuses=None, app_workflow=False, files=None, workflow_body=None, - raw_content=None, app_workflow_body=None): + raw_content=None, app_workflow_body=None, extra_args=()): with tempfile.TemporaryDirectory() as directory: directory = Path(directory) marker = directory / "merged" @@ -706,6 +706,8 @@ def run_case(self, *, workflow=False, probe_status=404, checks=None, statuses=No with open(os.environ['QUERIES'], 'a') as f: f.write(' '.join(a) + '\n') if a[:2] == ['pr', 'view']: print(json.dumps({'headRefOid': x['head'], 'baseRefName': 'main', 'state': 'OPEN'})) + elif a[:2] == ['pr', 'comment']: + pass elif a[:2] == ['pr', 'merge']: Path(os.environ['MERGE_MARKER']).touch() elif a[0] == 'api' and any('/contents/' in arg for arg in a): @@ -731,7 +733,7 @@ def run_case(self, *, workflow=False, probe_status=404, checks=None, statuses=No sys.exit(2) """)) gh.chmod(0o755) - result = subprocess.run([str(ROOT / 'scripts/gh-merge-green'), 'manaflow-ai/cmuxterm-hq#1254', '--squash'], + result = subprocess.run([str(ROOT / 'scripts/gh-merge-green'), 'manaflow-ai/cmuxterm-hq#1254', *extra_args, '--squash'], env={**os.environ, 'PATH': str(directory) + os.pathsep + os.environ['PATH'], 'FIXTURE': str(fixture), 'MERGE_MARKER': str(marker), 'QUERIES': str(queries), 'GH_MERGE_GREEN_NO_AUTO_UPDATE': '1'}, capture_output=True, text=True) return result, marker.exists(), queries.read_text() @@ -763,6 +765,18 @@ def test_no_ci_workflow_refuses_pending_status_context(self): self.assertNotEqual(result.returncode, 0) self.assertFalse(merged) + def test_vercel_override_waives_only_the_named_external_status(self): + result, merged, _ = self.run_case( + statuses=[{'id': 2, 'context': 'Vercel', 'state': 'pending'}], + extra_args=("--override", "Vercel preview is unrelated to this CLI change")) + self.assertEqual(result.returncode, 0, result.stderr) + self.assertTrue(merged) + result, merged, _ = self.run_case( + statuses=[{'id': 2, 'context': 'review', 'state': 'pending'}], + extra_args=("--override", "Vercel preview is unrelated to this CLI change")) + self.assertNotEqual(result.returncode, 0) + self.assertFalse(merged) + def test_workflow_probe_failure_is_not_absence(self): for code in (403, 500): result, merged, _ = self.run_case(probe_status=code)