[CI] Fix duplicate job names that bypass branch protection (#22001)
This commit is contained in:
@@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
lint:
|
auto-tune-lint:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -410,7 +410,7 @@ jobs:
|
|||||||
cd python
|
cd python
|
||||||
python3 sglang/multimodal_gen/test/run_suite.py --suite 8-npu
|
python3 sglang/multimodal_gen/test/run_suite.py --suite 8-npu
|
||||||
|
|
||||||
pr-test-finish:
|
pr-test-npu-finish:
|
||||||
needs:
|
needs:
|
||||||
[
|
[
|
||||||
check-changes,
|
check-changes,
|
||||||
|
|||||||
@@ -81,6 +81,12 @@ repos:
|
|||||||
language: system
|
language: system
|
||||||
files: ^\.github/CI_PERMISSIONS\.json$
|
files: ^\.github/CI_PERMISSIONS\.json$
|
||||||
pass_filenames: false
|
pass_filenames: false
|
||||||
|
- id: check-workflow-job-names
|
||||||
|
name: check for duplicate workflow job names
|
||||||
|
entry: python3 scripts/ci/check_workflow_job_names.py
|
||||||
|
language: system
|
||||||
|
files: ^\.github/workflows/.*\.yml$
|
||||||
|
pass_filenames: false
|
||||||
- repo: https://github.com/lycheeverse/lychee.git
|
- repo: https://github.com/lycheeverse/lychee.git
|
||||||
rev: lychee-v0.22.0
|
rev: lychee-v0.22.0
|
||||||
hooks:
|
hooks:
|
||||||
|
|||||||
Executable
+58
@@ -0,0 +1,58 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Check that required status check job names are unique across workflows.
|
||||||
|
|
||||||
|
Duplicate job names on the same commit allow a passing job in one workflow
|
||||||
|
to satisfy a required status check meant for a different workflow, bypassing
|
||||||
|
branch protection.
|
||||||
|
|
||||||
|
See: https://github.com/sgl-project/sglang/pull/20208 for an example where
|
||||||
|
pr-test-npu.yml's "pr-test-finish" job (which passed) caused GitHub to treat
|
||||||
|
the required "pr-test-finish" check (from pr-test.yml, which failed) as met.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import sys
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# Job names used as required status checks in branch protection.
|
||||||
|
# These MUST be unique across all workflow files.
|
||||||
|
PROTECTED_JOB_NAMES = {
|
||||||
|
"pr-test-finish",
|
||||||
|
"lint",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
workflows = sorted(glob.glob(".github/workflows/*.yml"))
|
||||||
|
job_to_files: dict[str, list[str]] = defaultdict(list)
|
||||||
|
|
||||||
|
for wf in workflows:
|
||||||
|
with open(wf) as f:
|
||||||
|
data = yaml.safe_load(f)
|
||||||
|
if not data or "jobs" not in data:
|
||||||
|
continue
|
||||||
|
for job in data["jobs"]:
|
||||||
|
if job in PROTECTED_JOB_NAMES:
|
||||||
|
job_to_files[job].append(wf)
|
||||||
|
|
||||||
|
duplicates = {job: files for job, files in job_to_files.items() if len(files) > 1}
|
||||||
|
|
||||||
|
if not duplicates:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
print("ERROR: Required status check job names must be unique across workflows.")
|
||||||
|
print("Duplicates allow branch protection bypass via auto-merge.\n")
|
||||||
|
for job, files in sorted(duplicates.items()):
|
||||||
|
print(f" Job '{job}' appears in:")
|
||||||
|
for f in files:
|
||||||
|
print(f" - {f}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
print("Fix: rename the job in non-primary workflows to avoid collision.")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
Reference in New Issue
Block a user