Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/auto-tune.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:

jobs:
lint:
auto-tune-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
2 changes: 1 addition & 1 deletion .github/workflows/pr-test-npu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ jobs:
cd python
python3 sglang/multimodal_gen/test/run_suite.py --suite 8-npu

pr-test-finish:
pr-test-npu-finish:
needs:
[
check-changes,
Expand Down
6 changes: 6 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ repos:
language: system
files: ^\.github/CI_PERMISSIONS\.json$
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
rev: lychee-v0.22.0
hooks:
Expand Down
58 changes: 58 additions & 0 deletions scripts/ci/check_workflow_job_names.py
Original file line number Diff line number Diff line change
@@ -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())
Loading