[no-ci] CI: Add restricted-paths-guard.yml #5
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| name: "CI: Check PR author organization for restricted paths" | |
| on: | |
| # TODO BEFORE MERGING: change to pull_request_target | |
| pull_request: | |
| types: | |
| - opened | |
| - synchronize | |
| - reopened | |
| - ready_for_review | |
| jobs: | |
| check-author-org: | |
| name: PR author may modify restricted paths | |
| if: github.repository_owner == 'NVIDIA' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| issues: write | |
| pull-requests: read | |
| steps: | |
| - name: Check PR author organization for restricted paths | |
| env: | |
| # PR metadata inputs | |
| AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association || 'NONE' }} | |
| LABELS: ${{ toJson(github.event.pull_request.labels) }} | |
| PR_AUTHOR: ${{ github.event.pull_request.user.login }} | |
| PR_NUMBER: ${{ github.event.pull_request.number }} | |
| PR_URL: ${{ github.event.pull_request.html_url }} | |
| # API request context/auth | |
| GH_TOKEN: ${{ github.token }} | |
| ORG_NAME: NVIDIA | |
| ORG_REVIEW_LABEL: Check-PR-Author-ORG | |
| REPO: ${{ github.repository }} | |
| run: | | |
| if ! MATCHING_RESTRICTED_PATHS=$( | |
| gh api \ | |
| --paginate \ | |
| --jq ' | |
| .[] | |
| | if (.filename | startswith("cuda_bindings/")) | |
| or (.filename | startswith("cuda_python/")) | |
| then .filename | |
| elif ((.previous_filename // "") | startswith("cuda_bindings/")) | |
| or ((.previous_filename // "") | startswith("cuda_python/")) | |
| then "\(.previous_filename) -> \(.filename)" | |
| else empty | |
| end | |
| ' \ | |
| "repos/$REPO/pulls/$PR_NUMBER/files" | |
| ); then | |
| echo "::error::Failed to inspect the PR file list." | |
| { | |
| echo "## PR Author Organization Check Failed" | |
| echo "" | |
| echo "- **Error**: Failed to inspect the PR file list." | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "" | |
| echo "Please update the PR at: $PR_URL" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| TOUCHES_RESTRICTED_PATHS=false | |
| if [ -n "$MATCHING_RESTRICTED_PATHS" ]; then | |
| TOUCHES_RESTRICTED_PATHS=true | |
| fi | |
| HAS_ORG_REVIEW_LABEL=false | |
| while IFS= read -r label; do | |
| [ -n "$label" ] || continue | |
| if [ "$label" = "$ORG_REVIEW_LABEL" ]; then | |
| HAS_ORG_REVIEW_LABEL=true | |
| break | |
| fi | |
| done < <(echo "$LABELS" | jq -r '.[].name') | |
| REVIEW_LABEL_ACTION="none" | |
| write_matching_restricted_paths() { | |
| echo "- **Matched restricted paths**:" | |
| echo '```text' | |
| printf '%s\n' "$MATCHING_RESTRICTED_PATHS" | |
| echo '```' | |
| } | |
| write_review_label_action() { | |
| [ "$REVIEW_LABEL_ACTION" != "none" ] || return 0 | |
| echo "- **Review label action**: $REVIEW_LABEL_ACTION \`$ORG_REVIEW_LABEL\`" | |
| } | |
| ensure_org_review_label_present() { | |
| if [ "$HAS_ORG_REVIEW_LABEL" = "false" ]; then | |
| if ! gh issue edit "$PR_NUMBER" --repo "$REPO" --add-label "$ORG_REVIEW_LABEL" >/dev/null; then | |
| echo "::error::Failed to add label $ORG_REVIEW_LABEL." | |
| exit 1 | |
| fi | |
| HAS_ORG_REVIEW_LABEL=true | |
| REVIEW_LABEL_ACTION="added" | |
| fi | |
| } | |
| ensure_org_review_label_absent() { | |
| if [ "$HAS_ORG_REVIEW_LABEL" = "true" ]; then | |
| if ! gh issue edit "$PR_NUMBER" --repo "$REPO" --remove-label "$ORG_REVIEW_LABEL" >/dev/null; then | |
| echo "::error::Failed to remove label $ORG_REVIEW_LABEL." | |
| exit 1 | |
| fi | |
| HAS_ORG_REVIEW_LABEL=false | |
| REVIEW_LABEL_ACTION="removed" | |
| fi | |
| } | |
| if [ "$TOUCHES_RESTRICTED_PATHS" = "false" ]; then | |
| ensure_org_review_label_absent | |
| { | |
| echo "## PR Author Organization Check Passed" | |
| echo "" | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "- **Touches restricted paths**: false" | |
| echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" | |
| write_review_label_action | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 0 | |
| fi | |
| # Use curl here because the 204/404/302 HTTP status is the signal we | |
| # need. gh api treats 404 as a command failure, but 404 is an expected | |
| # "not a member" outcome for this check. | |
| if ! MEMBERSHIP_STATUS=$( | |
| curl --silent --show-error --output /dev/null --write-out '%{http_code}' \ | |
| -H "Authorization: Bearer $GH_TOKEN" \ | |
| -H "Accept: application/vnd.github+json" \ | |
| -H "X-GitHub-Api-Version: 2022-11-28" \ | |
| "https://api.github.com/orgs/$ORG_NAME/members/$PR_AUTHOR" | |
| ); then | |
| echo "::error::Failed to query organization membership." | |
| { | |
| echo "## PR Author Organization Check Failed" | |
| echo "" | |
| echo "- **Error**: Failed to query organization membership." | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "- **Organization membership query**: failed" | |
| echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" | |
| echo "" | |
| write_matching_restricted_paths | |
| echo "" | |
| echo "Please update the PR at: $PR_URL" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| fi | |
| case "$MEMBERSHIP_STATUS" in | |
| 204) | |
| ensure_org_review_label_absent | |
| { | |
| echo "## PR Author Organization Check Passed" | |
| echo "" | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "- **Organization membership query**: \`204\` (member)" | |
| echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" | |
| echo "" | |
| write_matching_restricted_paths | |
| write_review_label_action | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| ;; | |
| 302) | |
| ensure_org_review_label_present | |
| echo "::warning::Could not determine conclusively whether $PR_AUTHOR is a member of $ORG_NAME. Added label $ORG_REVIEW_LABEL for manual review." | |
| { | |
| echo "## PR Author Organization Check Inconclusive" | |
| echo "" | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "- **Organization membership query**: \`302\` (inconclusive)" | |
| echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" | |
| echo "" | |
| write_matching_restricted_paths | |
| write_review_label_action | |
| echo "" | |
| echo "- **Next step**: Review whether the PR author should be allowed to modify these restricted paths." | |
| echo "" | |
| echo "Please update the PR at: $PR_URL" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| ;; | |
| 404) | |
| ensure_org_review_label_absent | |
| echo "::error::This PR failed the author organization check. See the job summary for details." | |
| { | |
| echo "## PR Author Organization Check Failed" | |
| echo "" | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "- **Organization membership query**: \`404\` (not a member)" | |
| echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" | |
| echo "" | |
| write_matching_restricted_paths | |
| write_review_label_action | |
| echo "" | |
| echo "- **Policy**: See \`cuda_bindings/LICENSE\` and \`cuda_python/LICENSE\`. Only NVIDIA organization members may modify files under \`cuda_bindings/\` or \`cuda_python/\`." | |
| echo "" | |
| echo "Please update the PR at: $PR_URL" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| ;; | |
| *) | |
| echo "::error::Unexpected response from organization membership query: HTTP $MEMBERSHIP_STATUS." | |
| { | |
| echo "## PR Author Organization Check Failed" | |
| echo "" | |
| echo "- **Error**: Unexpected response from organization membership query." | |
| echo "- **Author**: $PR_AUTHOR" | |
| echo "- **Author association**: $AUTHOR_ASSOCIATION" | |
| echo "- **Organization membership query**: \`$MEMBERSHIP_STATUS\`" | |
| echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" | |
| echo "" | |
| write_matching_restricted_paths | |
| echo "" | |
| echo "Please update the PR at: $PR_URL" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| exit 1 | |
| ;; | |
| esac |