|
1 | 1 | # SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. |
2 | 2 | # SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | | -name: "CI: Check PR author organization for restricted paths" |
| 4 | +name: "CI: Check PR author signals for restricted paths" |
5 | 5 |
|
6 | 6 | on: |
| 7 | + # Label updates on fork PRs require pull_request_target permissions. |
| 8 | + # TODO BEFORE MERGING: change to pull_request_target |
7 | 9 | pull_request: |
8 | 10 | types: |
9 | 11 | - opened |
|
13 | 15 |
|
14 | 16 | jobs: |
15 | 17 | check-author-org: |
16 | | - name: PR author may modify restricted paths |
| 18 | + name: PR author signals recorded for restricted paths |
17 | 19 | if: github.repository_owner == 'NVIDIA' |
18 | 20 | runs-on: ubuntu-latest |
19 | 21 | permissions: |
| 22 | + issues: write |
20 | 23 | pull-requests: read |
21 | 24 | steps: |
22 | | - - name: Check PR author organization for restricted paths |
| 25 | + - name: Inspect PR author signals for restricted paths |
23 | 26 | env: |
24 | 27 | # PR metadata inputs |
25 | 28 | AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association || 'NONE' }} |
| 29 | + EXISTING_LABELS: ${{ toJson(github.event.pull_request.labels.*.name) }} |
26 | 30 | PR_AUTHOR: ${{ github.event.pull_request.user.login }} |
27 | 31 | PR_NUMBER: ${{ github.event.pull_request.number }} |
28 | 32 | PR_URL: ${{ github.event.pull_request.html_url }} |
29 | 33 |
|
| 34 | + # Workflow policy inputs |
| 35 | + PUBLIC_MEMBER_ORG: NVIDIA |
| 36 | + REVIEW_LABEL: Check-PR-author-ORG |
| 37 | + |
30 | 38 | # API request context/auth |
31 | 39 | GH_TOKEN: ${{ github.token }} |
| 40 | + GITHUB_API_URL: ${{ github.api_url }} |
32 | 41 | REPO: ${{ github.repository }} |
33 | 42 | run: | |
| 43 | + set -euo pipefail |
| 44 | +
|
34 | 45 | if ! MATCHING_RESTRICTED_PATHS=$( |
35 | 46 | gh api \ |
36 | 47 | --paginate \ |
@@ -71,40 +82,104 @@ jobs: |
71 | 82 | echo '```' |
72 | 83 | } |
73 | 84 |
|
74 | | - IS_ALLOWED=false |
| 85 | + HAS_TRUE_POSITIVE_SIGNAL=false |
| 86 | + LABEL_ACTION="not needed (no restricted paths)" |
| 87 | + PUBLIC_MEMBER_CHECK="not needed (no restricted paths)" |
| 88 | + TRUE_POSITIVE_SIGNALS="(none)" |
| 89 | +
|
75 | 90 | case "$AUTHOR_ASSOCIATION" in |
76 | | - COLLABORATOR|MEMBER|OWNER) |
77 | | - IS_ALLOWED=true |
| 91 | + MEMBER|OWNER) |
| 92 | + HAS_TRUE_POSITIVE_SIGNAL=true |
| 93 | + LABEL_ACTION="not needed (author association is a true positive)" |
| 94 | + PUBLIC_MEMBER_CHECK="skipped (author association is a true positive)" |
| 95 | + TRUE_POSITIVE_SIGNALS="author_association:$AUTHOR_ASSOCIATION" |
78 | 96 | ;; |
79 | 97 | esac |
80 | 98 |
|
81 | | - if [ "$TOUCHES_RESTRICTED_PATHS" = "true" ] && [ "$IS_ALLOWED" = "false" ]; then |
82 | | - echo "::error::This PR failed the author organization check. See the job summary for details." |
83 | | - { |
84 | | - echo "## PR Author Organization Check Failed" |
85 | | - echo "" |
86 | | - echo "- **Author**: $PR_AUTHOR" |
87 | | - echo "- **Author association**: $AUTHOR_ASSOCIATION" |
88 | | - echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" |
89 | | - echo "" |
90 | | - write_matching_restricted_paths |
91 | | - echo "" |
92 | | - echo "- **Policy**: See \`cuda_bindings/LICENSE\` and \`cuda_python/LICENSE\`. Only NVIDIA organization members may modify files under \`cuda_bindings/\` or \`cuda_python/\`." |
93 | | - echo "" |
94 | | - echo "Please update the PR at: $PR_URL" |
95 | | - } >> "$GITHUB_STEP_SUMMARY" |
96 | | - exit 1 |
| 99 | + if [ "$TOUCHES_RESTRICTED_PATHS" = "true" ] && [ "$HAS_TRUE_POSITIVE_SIGNAL" = "false" ]; then |
| 100 | + PUBLIC_MEMBER_STATUS=$(curl \ |
| 101 | + --silent \ |
| 102 | + --show-error \ |
| 103 | + --output /dev/null \ |
| 104 | + --write-out '%{http_code}' \ |
| 105 | + -H "Authorization: Bearer $GH_TOKEN" \ |
| 106 | + -H "Accept: application/vnd.github+json" \ |
| 107 | + -H "X-GitHub-Api-Version: 2022-11-28" \ |
| 108 | + "$GITHUB_API_URL/orgs/$PUBLIC_MEMBER_ORG/public_members/$PR_AUTHOR") |
| 109 | +
|
| 110 | + case "$PUBLIC_MEMBER_STATUS" in |
| 111 | + 204) |
| 112 | + HAS_TRUE_POSITIVE_SIGNAL=true |
| 113 | + LABEL_ACTION="not needed (public org membership is a true positive)" |
| 114 | + PUBLIC_MEMBER_CHECK="204 (public member)" |
| 115 | + TRUE_POSITIVE_SIGNALS="public_org_membership:$PUBLIC_MEMBER_ORG" |
| 116 | + ;; |
| 117 | + 404) |
| 118 | + PUBLIC_MEMBER_CHECK="404 (not a public member)" |
| 119 | + ;; |
| 120 | + *) |
| 121 | + echo "::error::Failed to determine whether the PR author is a public $PUBLIC_MEMBER_ORG member." |
| 122 | + { |
| 123 | + echo "## PR Author Organization Check Failed" |
| 124 | + echo "" |
| 125 | + echo "- **Error**: Unexpected HTTP status from \`/orgs/$PUBLIC_MEMBER_ORG/public_members/$PR_AUTHOR\`: \`$PUBLIC_MEMBER_STATUS\`." |
| 126 | + echo "- **Author**: $PR_AUTHOR" |
| 127 | + echo "- **Author association**: $AUTHOR_ASSOCIATION" |
| 128 | + echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" |
| 129 | + echo "" |
| 130 | + write_matching_restricted_paths |
| 131 | + echo "" |
| 132 | + echo "Please update the PR at: $PR_URL" |
| 133 | + } >> "$GITHUB_STEP_SUMMARY" |
| 134 | + exit 1 |
| 135 | + ;; |
| 136 | + esac |
| 137 | + fi |
| 138 | +
|
| 139 | + LABEL_ALREADY_PRESENT=false |
| 140 | + if jq -e --arg label "$REVIEW_LABEL" '.[] == $label' <<<"$EXISTING_LABELS" >/dev/null; then |
| 141 | + LABEL_ALREADY_PRESENT=true |
| 142 | + fi |
| 143 | +
|
| 144 | + if [ "$TOUCHES_RESTRICTED_PATHS" = "true" ] && [ "$HAS_TRUE_POSITIVE_SIGNAL" = "false" ]; then |
| 145 | + if [ "$LABEL_ALREADY_PRESENT" = "true" ]; then |
| 146 | + LABEL_ACTION="already present" |
| 147 | + elif ! gh issue edit "$PR_NUMBER" --repo "$REPO" --add-label "$REVIEW_LABEL"; then |
| 148 | + echo "::error::Failed to add the $REVIEW_LABEL label." |
| 149 | + { |
| 150 | + echo "## PR Author Organization Check Failed" |
| 151 | + echo "" |
| 152 | + echo "- **Error**: Failed to add the \`$REVIEW_LABEL\` label." |
| 153 | + echo "- **Author**: $PR_AUTHOR" |
| 154 | + echo "- **Author association**: $AUTHOR_ASSOCIATION" |
| 155 | + echo "- **Public $PUBLIC_MEMBER_ORG membership check**: $PUBLIC_MEMBER_CHECK" |
| 156 | + echo "" |
| 157 | + write_matching_restricted_paths |
| 158 | + echo "" |
| 159 | + echo "Please update the PR at: $PR_URL" |
| 160 | + } >> "$GITHUB_STEP_SUMMARY" |
| 161 | + exit 1 |
| 162 | + else |
| 163 | + LABEL_ACTION="added" |
| 164 | + fi |
97 | 165 | fi |
98 | 166 |
|
99 | 167 | { |
100 | | - echo "## PR Author Organization Check Passed" |
| 168 | + echo "## PR Author Organization Check Completed" |
101 | 169 | echo "" |
102 | 170 | echo "- **Author**: $PR_AUTHOR" |
103 | 171 | echo "- **Author association**: $AUTHOR_ASSOCIATION" |
104 | 172 | echo "- **Touches restricted paths**: $TOUCHES_RESTRICTED_PATHS" |
105 | 173 | echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`" |
| 174 | + echo "- **Public $PUBLIC_MEMBER_ORG membership check**: $PUBLIC_MEMBER_CHECK" |
| 175 | + echo "- **True positive signals**: $TRUE_POSITIVE_SIGNALS" |
| 176 | + echo "- **Label action**: $LABEL_ACTION" |
106 | 177 | if [ "$TOUCHES_RESTRICTED_PATHS" = "true" ]; then |
107 | 178 | echo "" |
108 | 179 | write_matching_restricted_paths |
109 | 180 | fi |
| 181 | + if [ "$TOUCHES_RESTRICTED_PATHS" = "true" ] && [ "$HAS_TRUE_POSITIVE_SIGNAL" = "false" ]; then |
| 182 | + echo "" |
| 183 | + echo "- **Manual follow-up**: No true positive signal was found, so \`$REVIEW_LABEL\` is required." |
| 184 | + fi |
110 | 185 | } >> "$GITHUB_STEP_SUMMARY" |
0 commit comments