Re-land #23109: rebase-required mode + fix for grep-no-match abort (#24180)

This commit is contained in:
Alison Shao
2026-05-08 15:28:57 -07:00
committed by GitHub
parent fa8985486e
commit aefd8e257f
3 changed files with 276 additions and 26 deletions
+132
View File
@@ -13,6 +13,126 @@ from github import Auth, Github
PERMISSIONS_FILE_PATH = ".github/CI_PERMISSIONS.json"
MAINTENANCE_ISSUE_NUMBER = 21065
def _check_rebase_gate(gh_repo, pr, token):
"""
Pre-dispatch gate mirroring `.github/actions/check-maintenance/action.yml`.
Without this, /rerun-stage and /rerun-test would dispatch a workflow_run
on a PR that's behind a required base, the action would catch it, and
every job in the run would fail at the gate — wasting runner time and
producing N error annotations instead of one comment. Pre-checking here
short-circuits the dispatch and posts a single explanatory comment.
Mirrors the action's two independent modes driven by issue #21065:
(1) Full-pause: maintenance issue is OPEN
(2) Rebase-required: issue body contains `MIN_BASE_SHA: <sha>`
Both bypassed by the `bypass-maintenance` PR label.
Returns (allowed: bool, message: Optional[str]). When allowed=False,
caller MUST post `message` to the PR and skip dispatch.
Fail-open on API errors (matches the action's behavior).
"""
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
repo_full_name = gh_repo.full_name
try:
issue_resp = requests.get(
f"https://api.github.com/repos/{repo_full_name}/issues/{MAINTENANCE_ISSUE_NUMBER}",
headers=headers,
timeout=15,
)
if issue_resp.status_code != 200:
print(
f"check_rebase_gate: issue fetch returned {issue_resp.status_code}; fail-open"
)
return True, None
issue_data = issue_resp.json()
except Exception as e:
print(f"check_rebase_gate: issue fetch failed ({e}); fail-open")
return True, None
issue_state = (issue_data.get("state") or "").lower()
issue_body = issue_data.get("body") or ""
min_base_sha = None
# First MIN_BASE_SHA: <sha> line wins. Match the action's parser:
# tolerate optional backticks and either ':' or '=' separator.
for line in issue_body.replace("\r", "").split("\n"):
m = re.match(
r"^\s*`?MIN_BASE_SHA`?\s*[:=]\s*`?([A-Fa-f0-9]+)`?",
line,
)
if m:
candidate = m.group(1)
if 7 <= len(candidate) <= 40:
min_base_sha = candidate
break
gate_active = (issue_state == "open") or bool(min_base_sha)
if not gate_active:
return True, None
bypass = any(
(lbl.name if hasattr(lbl, "name") else lbl.get("name")) == "bypass-maintenance"
for lbl in pr.get_labels()
)
if bypass:
print("check_rebase_gate: PR has bypass-maintenance label; allowing dispatch")
return True, None
if issue_state == "open":
msg = (
"## ⚠️ CI Maintenance Mode is Active\n"
"The CI infrastructure is currently under maintenance. "
"All PR CI runs are paused until maintenance is complete. "
"**Merging non-CI-fix PRs is prohibited during maintenance mode.**\n\n"
f"Follow [issue #{MAINTENANCE_ISSUE_NUMBER}]"
f"(https://github.com/{repo_full_name}/issues/{MAINTENANCE_ISSUE_NUMBER}) "
"for status updates. Re-run was not dispatched."
)
return False, msg
# MIN_BASE_SHA set, issue not OPEN — check rebase status.
pr_head_sha = pr.head.sha
try:
compare_resp = requests.get(
f"https://api.github.com/repos/{repo_full_name}/compare/{min_base_sha}...{pr_head_sha}",
headers=headers,
timeout=15,
)
if compare_resp.status_code != 200:
print(
f"check_rebase_gate: compare API returned {compare_resp.status_code}; fail-open"
)
return True, None
status = compare_resp.json().get("status", "unknown")
except Exception as e:
print(f"check_rebase_gate: compare API failed ({e}); fail-open")
return True, None
if status in ("ahead", "identical"):
return True, None
msg = (
"## ⚠️ Rebase Required Before Re-run\n"
f"A major update has landed on `main`. Your PR is `{status}` relative "
f"to required base commit `{min_base_sha[:12]}`.\n\n"
"**Re-run was not dispatched.** What to do:\n"
"- Rebase your branch onto the latest `main` and push again\n"
f"- Follow [issue #{MAINTENANCE_ISSUE_NUMBER}]"
f"(https://github.com/{repo_full_name}/issues/{MAINTENANCE_ISSUE_NUMBER}) for context\n"
"- CI-fix PRs may request the `bypass-maintenance` label to skip this check"
)
return False, msg
def find_workflow_run_url(
gh_repo,
workflow_id,
@@ -345,6 +465,12 @@ def handle_rerun_stage(
)
return False
allowed, gate_msg = _check_rebase_gate(gh_repo, pr, token)
if not allowed:
comment.create_reaction("confused")
pr.create_issue_comment(gate_msg)
return False
try:
# Get the appropriate workflow based on stage type
workflow_name = "PR Test (AMD)" if is_amd_stage else "PR Test"
@@ -938,6 +1064,12 @@ def handle_rerun_test(
)
return False
allowed, gate_msg = _check_rebase_gate(gh_repo, pr, token)
if not allowed:
comment.create_reaction("confused")
pr.create_issue_comment(gate_msg)
return False
# Phase 1: Resolve all specs
resolved = []
resolve_failures = []