Skip to content

Commit 1d87dcf

Browse files
rparolinclaudeleofang
authored
Add CI check to enforce labels and milestone on PRs (#1815)
* Add CI check to enforce labels and milestone on PRs Adds a GitHub Actions workflow that blocks PRs from merging unless they have at least one label and a milestone assigned. Bot and draft PRs are exempted. This replaces the manual process of tagging PRs. Closes #1025 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Remove invalid milestoned/demilestoned activity types pull_request_target does not support these events; milestone changes are captured by the existing 'edited' trigger. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Update .github/workflows/pr-metadata-check.yml Co-authored-by: Leo Fang <leo80042@gmail.com> * Address PR review feedback for pr-metadata-check workflow - Fix case sensitivity: 'nvidia' → 'NVIDIA' (job never ran as-is) - Re-add milestoned/demilestoned activity types (valid for pull_request_target) - Update copyright year to include 2026 - Add PR metadata guidance to AGENTS.md for label/milestone enforcement Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * linter * Enhance PR metadata check with structured label validation Inspired by the RAPIDS label checker, this strengthens the PR metadata check to require both a module label and a type label, block merge- preventing labels, and render results as a GitHub Job Summary. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Leo Fang <leo80042@gmail.com>
1 parent 8432c9d commit 1d87dcf

2 files changed

Lines changed: 121 additions & 0 deletions

File tree

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# SPDX-License-Identifier: Apache-2.0
4+
5+
name: "CI: Enforce label/milestone on PRs"
6+
7+
on:
8+
pull_request_target:
9+
types:
10+
- opened
11+
- edited
12+
- synchronize
13+
- labeled
14+
- unlabeled
15+
- reopened
16+
- ready_for_review
17+
18+
jobs:
19+
check-metadata:
20+
name: PR has labels and milestone
21+
if: github.repository_owner == 'NVIDIA'
22+
runs-on: ubuntu-latest
23+
steps:
24+
- name: Check for labels and milestone
25+
env:
26+
LABELS: ${{ toJson(github.event.pull_request.labels) }}
27+
MILESTONE: ${{ github.event.pull_request.milestone && github.event.pull_request.milestone.title || '' }}
28+
PR_URL: ${{ github.event.pull_request.html_url }}
29+
IS_BOT: ${{ github.actor == 'dependabot[bot]' || github.actor == 'pre-commit-ci[bot]' || github.actor == 'copy-pr-bot[bot]' }}
30+
IS_DRAFT: ${{ github.event.pull_request.draft }}
31+
run: |
32+
if [ "$IS_BOT" = "true" ] || [ "$IS_DRAFT" = "true" ]; then
33+
echo "Skipping check for bot or draft PR."
34+
exit 0
35+
fi
36+
37+
LABEL_NAMES=$(echo "$LABELS" | jq -r '.[].name')
38+
ERRORS=""
39+
40+
# Module labels identify which package the PR touches.
41+
MODULE_LABELS="cuda.bindings cuda.core cuda.pathfinder"
42+
HAS_MODULE=false
43+
for label in $LABEL_NAMES; do
44+
for mod in $MODULE_LABELS; do
45+
if [ "$label" = "$mod" ]; then
46+
HAS_MODULE=true
47+
break 2
48+
fi
49+
done
50+
done
51+
52+
if [ "$HAS_MODULE" = "false" ]; then
53+
ERRORS="${ERRORS}- **Missing module label**: add at least one of: \`cuda.bindings\`, \`cuda.core\`, \`cuda.pathfinder\`.\n"
54+
fi
55+
56+
# Type labels categorize the kind of change.
57+
TYPE_LABELS="bug enhancement feature documentation test example CI/CD packaging dependencies performance experiment RFC support P0 P1 P2"
58+
HAS_TYPE=false
59+
for label in $LABEL_NAMES; do
60+
for typ in $TYPE_LABELS; do
61+
if [ "$label" = "$typ" ]; then
62+
HAS_TYPE=true
63+
break 2
64+
fi
65+
done
66+
done
67+
68+
if [ "$HAS_TYPE" = "false" ]; then
69+
ERRORS="${ERRORS}- **Missing type label**: add at least one of: \`bug\`, \`enhancement\`, \`feature\`, \`documentation\`, \`test\`, \`example\`, \`CI/CD\`, \`packaging\`, \`dependencies\`, \`performance\`, \`experiment\`, \`RFC\`, \`support\`, \`P0\`, \`P1\`, \`P2\`.\n"
70+
fi
71+
72+
if [ -z "$MILESTONE" ]; then
73+
ERRORS="${ERRORS}- **Missing milestone**: assign a milestone to this PR.\n"
74+
fi
75+
76+
# Block PRs with labels that indicate they are not ready to merge.
77+
BLOCKED_PATTERNS="blocked DO NOT MERGE do not merge"
78+
for label in $LABEL_NAMES; do
79+
for pattern in $BLOCKED_PATTERNS; do
80+
if echo "$label" | grep -qi "$pattern"; then
81+
ERRORS="${ERRORS}- **Blocked label detected**: label \`$label\` prevents merging. Remove it when the PR is ready.\n"
82+
break
83+
fi
84+
done
85+
done
86+
87+
if [ -n "$ERRORS" ]; then
88+
echo "::error::This PR is missing required metadata. See the job summary for details."
89+
{
90+
echo "## PR Metadata Check Failed"
91+
echo ""
92+
printf "$ERRORS"
93+
echo ""
94+
echo "Please update the PR at: $PR_URL"
95+
} >> "$GITHUB_STEP_SUMMARY"
96+
exit 1
97+
fi
98+
99+
LABEL_LIST=$(echo "$LABEL_NAMES" | paste -sd ', ' -)
100+
{
101+
echo "## PR Metadata Check Passed"
102+
echo ""
103+
echo "- **Labels**: $LABEL_LIST"
104+
echo "- **Milestone**: $MILESTONE"
105+
} >> "$GITHUB_STEP_SUMMARY"

AGENTS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@ guide for package-specific conventions and workflows.
1212
- `cuda_core/`: High-level Pythonic CUDA APIs built on top of bindings.
1313
- `cuda_python/`: Metapackage and docs aggregation.
1414

15+
# Pull requests
16+
17+
When creating pull requests with `gh pr create`, always assign at least one
18+
label and a milestone. CI enforces this via the `pr-metadata-check` workflow
19+
and will block PRs that are missing labels or a milestone. Use `--label` and
20+
`--milestone` flags, for example:
21+
22+
```
23+
gh pr create --title "..." --body "..." --label "bug" --milestone "v1.0"
24+
```
25+
26+
If you are unsure which label or milestone to use, check the existing labels
27+
and milestones on the repository with `gh label list` and `gh api
28+
repos/{owner}/{repo}/milestones --jq '.[].title'`, and pick the best match.
29+
30+
1531
# General
1632

1733
- When searching for text or files, prefer using `rg` or `rg --files`

0 commit comments

Comments
 (0)