From 027970e94593c79d14183ec172e89d6ea82825de Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Mar 2026 15:03:14 -0700 Subject: [PATCH 1/6] 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) --- .github/workflows/pr-metadata-check.yml | 56 +++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/pr-metadata-check.yml diff --git a/.github/workflows/pr-metadata-check.yml b/.github/workflows/pr-metadata-check.yml new file mode 100644 index 0000000000..619376ecd0 --- /dev/null +++ b/.github/workflows/pr-metadata-check.yml @@ -0,0 +1,56 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: "CI: Enforce label/milestone on PRs" + +on: + pull_request_target: + types: + - opened + - edited + - synchronize + - labeled + - unlabeled + - milestoned + - demilestoned + +jobs: + check-metadata: + name: PR has labels and milestone + if: github.repository_owner == 'nvidia' + runs-on: ubuntu-latest + steps: + - name: Check for labels and milestone + env: + LABELS: ${{ toJson(github.event.pull_request.labels) }} + MILESTONE: ${{ github.event.pull_request.milestone && github.event.pull_request.milestone.title || '' }} + PR_URL: ${{ github.event.pull_request.html_url }} + IS_BOT: ${{ github.actor == 'dependabot[bot]' || github.actor == 'pre-commit-ci[bot]' || github.actor == 'copy-pr-bot[bot]' }} + IS_DRAFT: ${{ github.event.pull_request.draft }} + run: | + if [ "$IS_BOT" = "true" ] || [ "$IS_DRAFT" = "true" ]; then + echo "Skipping check for bot or draft PR." + exit 0 + fi + + LABEL_COUNT=$(echo "$LABELS" | jq 'length') + ERRORS="" + + if [ "$LABEL_COUNT" -eq 0 ]; then + ERRORS="${ERRORS} - Missing labels: add at least one label to categorize this PR.\n" + fi + + if [ -z "$MILESTONE" ]; then + ERRORS="${ERRORS} - Missing milestone: assign a milestone to this PR.\n" + fi + + if [ -n "$ERRORS" ]; then + echo "::error::This PR is missing required metadata:" + printf "$ERRORS" + echo "" + echo "Please update the PR at: $PR_URL" + exit 1 + fi + + echo "PR has $LABEL_COUNT label(s) and milestone '$MILESTONE'." From 9bcad259f1494dc889a4725b4b4739b9d1e9155d Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Mar 2026 15:04:51 -0700 Subject: [PATCH 2/6] 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) --- .github/workflows/pr-metadata-check.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/pr-metadata-check.yml b/.github/workflows/pr-metadata-check.yml index 619376ecd0..e4f37dd462 100644 --- a/.github/workflows/pr-metadata-check.yml +++ b/.github/workflows/pr-metadata-check.yml @@ -12,8 +12,6 @@ on: - synchronize - labeled - unlabeled - - milestoned - - demilestoned jobs: check-metadata: From 81daf169fd9f7fc3538508cfbbe94c4ef5c84066 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Mar 2026 16:34:11 -0700 Subject: [PATCH 3/6] Update .github/workflows/pr-metadata-check.yml Co-authored-by: Leo Fang --- .github/workflows/pr-metadata-check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr-metadata-check.yml b/.github/workflows/pr-metadata-check.yml index e4f37dd462..7247a07a98 100644 --- a/.github/workflows/pr-metadata-check.yml +++ b/.github/workflows/pr-metadata-check.yml @@ -17,7 +17,7 @@ jobs: check-metadata: name: PR has labels and milestone if: github.repository_owner == 'nvidia' - runs-on: ubuntu-latest + if: github.repository_owner == 'NVIDIA' steps: - name: Check for labels and milestone env: From 387b7a987b1205e70df0666a345e98e9068e74ee Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Mar 2026 16:48:18 -0700 Subject: [PATCH 4/6] Address PR review feedback for pr-metadata-check workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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) --- .github/workflows/pr-metadata-check.yml | 6 ++++-- AGENTS.md | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-metadata-check.yml b/.github/workflows/pr-metadata-check.yml index 7247a07a98..814f9aaa53 100644 --- a/.github/workflows/pr-metadata-check.yml +++ b/.github/workflows/pr-metadata-check.yml @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: Copyright (c) 2024-2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. # # SPDX-License-Identifier: Apache-2.0 @@ -12,12 +12,14 @@ on: - synchronize - labeled - unlabeled + - milestoned + - demilestoned jobs: check-metadata: name: PR has labels and milestone - if: github.repository_owner == 'nvidia' if: github.repository_owner == 'NVIDIA' + runs-on: ubuntu-latest steps: - name: Check for labels and milestone env: diff --git a/AGENTS.md b/AGENTS.md index dd62ad5a95..a4450fbc66 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,6 +12,22 @@ guide for package-specific conventions and workflows. - `cuda_core/`: High-level Pythonic CUDA APIs built on top of bindings. - `cuda_python/`: Metapackage and docs aggregation. +# Pull requests + +When creating pull requests with `gh pr create`, always assign at least one +label and a milestone. CI enforces this via the `pr-metadata-check` workflow +and will block PRs that are missing labels or a milestone. Use `--label` and +`--milestone` flags, for example: + +``` +gh pr create --title "..." --body "..." --label "bug" --milestone "v1.0" +``` + +If you are unsure which label or milestone to use, check the existing labels +and milestones on the repository with `gh label list` and `gh api +repos/{owner}/{repo}/milestones --jq '.[].title'`, and pick the best match. + + # General - When searching for text or files, prefer using `rg` or `rg --files` From 0ae7729d3428809b58426c296f67423b9caefd88 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Mar 2026 16:58:59 -0700 Subject: [PATCH 5/6] linter --- .github/workflows/pr-metadata-check.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr-metadata-check.yml b/.github/workflows/pr-metadata-check.yml index 814f9aaa53..205fdd6c36 100644 --- a/.github/workflows/pr-metadata-check.yml +++ b/.github/workflows/pr-metadata-check.yml @@ -12,8 +12,8 @@ on: - synchronize - labeled - unlabeled - - milestoned - - demilestoned + # - milestoned + # - demilestoned jobs: check-metadata: From 8e56c4e4668e56384eb9dbcf9731fcdecd3b2069 Mon Sep 17 00:00:00 2001 From: Rob Parolin Date: Tue, 24 Mar 2026 17:23:37 -0700 Subject: [PATCH 6/6] 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) --- .github/workflows/pr-metadata-check.yml | 71 +++++++++++++++++++++---- 1 file changed, 60 insertions(+), 11 deletions(-) diff --git a/.github/workflows/pr-metadata-check.yml b/.github/workflows/pr-metadata-check.yml index 205fdd6c36..dd6971a544 100644 --- a/.github/workflows/pr-metadata-check.yml +++ b/.github/workflows/pr-metadata-check.yml @@ -12,8 +12,8 @@ on: - synchronize - labeled - unlabeled - # - milestoned - # - demilestoned + - reopened + - ready_for_review jobs: check-metadata: @@ -34,23 +34,72 @@ jobs: exit 0 fi - LABEL_COUNT=$(echo "$LABELS" | jq 'length') + LABEL_NAMES=$(echo "$LABELS" | jq -r '.[].name') ERRORS="" - if [ "$LABEL_COUNT" -eq 0 ]; then - ERRORS="${ERRORS} - Missing labels: add at least one label to categorize this PR.\n" + # Module labels identify which package the PR touches. + MODULE_LABELS="cuda.bindings cuda.core cuda.pathfinder" + HAS_MODULE=false + for label in $LABEL_NAMES; do + for mod in $MODULE_LABELS; do + if [ "$label" = "$mod" ]; then + HAS_MODULE=true + break 2 + fi + done + done + + if [ "$HAS_MODULE" = "false" ]; then + ERRORS="${ERRORS}- **Missing module label**: add at least one of: \`cuda.bindings\`, \`cuda.core\`, \`cuda.pathfinder\`.\n" + fi + + # Type labels categorize the kind of change. + TYPE_LABELS="bug enhancement feature documentation test example CI/CD packaging dependencies performance experiment RFC support P0 P1 P2" + HAS_TYPE=false + for label in $LABEL_NAMES; do + for typ in $TYPE_LABELS; do + if [ "$label" = "$typ" ]; then + HAS_TYPE=true + break 2 + fi + done + done + + if [ "$HAS_TYPE" = "false" ]; then + 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" fi if [ -z "$MILESTONE" ]; then - ERRORS="${ERRORS} - Missing milestone: assign a milestone to this PR.\n" + ERRORS="${ERRORS}- **Missing milestone**: assign a milestone to this PR.\n" fi + # Block PRs with labels that indicate they are not ready to merge. + BLOCKED_PATTERNS="blocked DO NOT MERGE do not merge" + for label in $LABEL_NAMES; do + for pattern in $BLOCKED_PATTERNS; do + if echo "$label" | grep -qi "$pattern"; then + ERRORS="${ERRORS}- **Blocked label detected**: label \`$label\` prevents merging. Remove it when the PR is ready.\n" + break + fi + done + done + if [ -n "$ERRORS" ]; then - echo "::error::This PR is missing required metadata:" - printf "$ERRORS" - echo "" - echo "Please update the PR at: $PR_URL" + echo "::error::This PR is missing required metadata. See the job summary for details." + { + echo "## PR Metadata Check Failed" + echo "" + printf "$ERRORS" + echo "" + echo "Please update the PR at: $PR_URL" + } >> "$GITHUB_STEP_SUMMARY" exit 1 fi - echo "PR has $LABEL_COUNT label(s) and milestone '$MILESTONE'." + LABEL_LIST=$(echo "$LABEL_NAMES" | paste -sd ', ' -) + { + echo "## PR Metadata Check Passed" + echo "" + echo "- **Labels**: $LABEL_LIST" + echo "- **Milestone**: $MILESTONE" + } >> "$GITHUB_STEP_SUMMARY"