Skip to content

Commit 42ba3f8

Browse files
committed
CI: triage inconclusive PR author org checks
Query NVIDIA org membership directly for restricted-path PRs so the workflow can distinguish members, non-members, and inconclusive cases, label PRs that need manual review, and show the matched files. Keep the trigger on pull_request temporarily so the new check can be exercised before switching back to pull_request_target. Made-with: Cursor
1 parent f56a8d1 commit 42ba3f8

File tree

1 file changed

+147
-27
lines changed

1 file changed

+147
-27
lines changed

.github/workflows/pr-author-org-check.yml

Lines changed: 147 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
name: "CI: Check PR author organization for restricted paths"
55

66
on:
7+
# TODO BEFORE MERGING: change to pull_request_target
78
pull_request:
89
types:
910
- opened
@@ -17,32 +18,37 @@ jobs:
1718
if: github.repository_owner == 'NVIDIA'
1819
runs-on: ubuntu-latest
1920
permissions:
21+
issues: write
2022
pull-requests: read
2123
steps:
2224
- name: Check PR author organization for restricted paths
2325
env:
2426
# PR metadata inputs
2527
AUTHOR_ASSOCIATION: ${{ github.event.pull_request.author_association || 'NONE' }}
28+
LABELS: ${{ toJson(github.event.pull_request.labels) }}
2629
PR_AUTHOR: ${{ github.event.pull_request.user.login }}
2730
PR_NUMBER: ${{ github.event.pull_request.number }}
2831
PR_URL: ${{ github.event.pull_request.html_url }}
2932

3033
# API request context/auth
3134
GH_TOKEN: ${{ github.token }}
35+
ORG_NAME: NVIDIA
36+
ORG_REVIEW_LABEL: Check-PR-Author-ORG
3237
REPO: ${{ github.repository }}
3338
run: |
3439
if ! MATCHING_RESTRICTED_PATHS=$(
3540
gh api \
3641
--paginate \
3742
--jq '
3843
.[]
39-
| select(
40-
(.filename | startswith("cuda_bindings/"))
41-
or ((.previous_filename // "") | startswith("cuda_bindings/"))
44+
| if (.filename | startswith("cuda_bindings/"))
4245
or (.filename | startswith("cuda_python/"))
46+
then .filename
47+
elif ((.previous_filename // "") | startswith("cuda_bindings/"))
4348
or ((.previous_filename // "") | startswith("cuda_python/"))
44-
)
45-
| .filename
49+
then "\(.previous_filename) -> \(.filename)"
50+
else empty
51+
end
4652
' \
4753
"repos/$REPO/pulls/$PR_NUMBER/files"
4854
); then
@@ -64,47 +70,161 @@ jobs:
6470
TOUCHES_RESTRICTED_PATHS=true
6571
fi
6672
73+
HAS_ORG_REVIEW_LABEL=false
74+
while IFS= read -r label; do
75+
[ -n "$label" ] || continue
76+
if [ "$label" = "$ORG_REVIEW_LABEL" ]; then
77+
HAS_ORG_REVIEW_LABEL=true
78+
break
79+
fi
80+
done < <(echo "$LABELS" | jq -r '.[].name')
81+
82+
REVIEW_LABEL_ACTION="none"
83+
6784
write_matching_restricted_paths() {
6885
echo "- **Matched restricted paths**:"
6986
echo '```text'
7087
printf '%s\n' "$MATCHING_RESTRICTED_PATHS"
7188
echo '```'
7289
}
7390
74-
IS_ALLOWED=false
75-
case "$AUTHOR_ASSOCIATION" in
76-
MEMBER|OWNER)
77-
IS_ALLOWED=true
78-
;;
79-
esac
91+
write_review_label_action() {
92+
[ "$REVIEW_LABEL_ACTION" != "none" ] || return 0
93+
echo "- **Review label action**: $REVIEW_LABEL_ACTION \`$ORG_REVIEW_LABEL\`"
94+
}
95+
96+
ensure_org_review_label_present() {
97+
if [ "$HAS_ORG_REVIEW_LABEL" = "false" ]; then
98+
if ! gh issue edit "$PR_NUMBER" --repo "$REPO" --add-label "$ORG_REVIEW_LABEL" >/dev/null; then
99+
echo "::error::Failed to add label $ORG_REVIEW_LABEL."
100+
exit 1
101+
fi
102+
HAS_ORG_REVIEW_LABEL=true
103+
REVIEW_LABEL_ACTION="added"
104+
fi
105+
}
106+
107+
ensure_org_review_label_absent() {
108+
if [ "$HAS_ORG_REVIEW_LABEL" = "true" ]; then
109+
if ! gh issue edit "$PR_NUMBER" --repo "$REPO" --remove-label "$ORG_REVIEW_LABEL" >/dev/null; then
110+
echo "::error::Failed to remove label $ORG_REVIEW_LABEL."
111+
exit 1
112+
fi
113+
HAS_ORG_REVIEW_LABEL=false
114+
REVIEW_LABEL_ACTION="removed"
115+
fi
116+
}
117+
118+
if [ "$TOUCHES_RESTRICTED_PATHS" = "false" ]; then
119+
ensure_org_review_label_absent
120+
{
121+
echo "## PR Author Organization Check Passed"
122+
echo ""
123+
echo "- **Author**: $PR_AUTHOR"
124+
echo "- **Author association**: $AUTHOR_ASSOCIATION"
125+
echo "- **Touches restricted paths**: false"
126+
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
127+
write_review_label_action
128+
} >> "$GITHUB_STEP_SUMMARY"
129+
exit 0
130+
fi
80131
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."
132+
# Use curl here because the 204/404/302 HTTP status is the signal we
133+
# need. gh api treats 404 as a command failure, but 404 is an expected
134+
# "not a member" outcome for this check.
135+
if ! MEMBERSHIP_STATUS=$(
136+
curl --silent --show-error --output /dev/null --write-out '%{http_code}' \
137+
-H "Authorization: Bearer $GH_TOKEN" \
138+
-H "Accept: application/vnd.github+json" \
139+
-H "X-GitHub-Api-Version: 2022-11-28" \
140+
"https://api.github.com/orgs/$ORG_NAME/members/$PR_AUTHOR"
141+
); then
142+
echo "::error::Failed to query organization membership."
83143
{
84144
echo "## PR Author Organization Check Failed"
85145
echo ""
146+
echo "- **Error**: Failed to query organization membership."
86147
echo "- **Author**: $PR_AUTHOR"
87148
echo "- **Author association**: $AUTHOR_ASSOCIATION"
149+
echo "- **Organization membership query**: failed"
88150
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
89151
echo ""
90152
write_matching_restricted_paths
91153
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 ""
94154
echo "Please update the PR at: $PR_URL"
95155
} >> "$GITHUB_STEP_SUMMARY"
96156
exit 1
97157
fi
98158
99-
{
100-
echo "## PR Author Organization Check Passed"
101-
echo ""
102-
echo "- **Author**: $PR_AUTHOR"
103-
echo "- **Author association**: $AUTHOR_ASSOCIATION"
104-
echo "- **Touches restricted paths**: $TOUCHES_RESTRICTED_PATHS"
105-
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
106-
if [ "$TOUCHES_RESTRICTED_PATHS" = "true" ]; then
107-
echo ""
108-
write_matching_restricted_paths
109-
fi
110-
} >> "$GITHUB_STEP_SUMMARY"
159+
case "$MEMBERSHIP_STATUS" in
160+
204)
161+
ensure_org_review_label_absent
162+
{
163+
echo "## PR Author Organization Check Passed"
164+
echo ""
165+
echo "- **Author**: $PR_AUTHOR"
166+
echo "- **Author association**: $AUTHOR_ASSOCIATION"
167+
echo "- **Organization membership query**: \`204\` (member)"
168+
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
169+
echo ""
170+
write_matching_restricted_paths
171+
write_review_label_action
172+
} >> "$GITHUB_STEP_SUMMARY"
173+
;;
174+
302)
175+
ensure_org_review_label_present
176+
echo "::warning::Could not determine conclusively whether $PR_AUTHOR is a member of $ORG_NAME. Added label $ORG_REVIEW_LABEL for manual review."
177+
{
178+
echo "## PR Author Organization Check Inconclusive"
179+
echo ""
180+
echo "- **Author**: $PR_AUTHOR"
181+
echo "- **Author association**: $AUTHOR_ASSOCIATION"
182+
echo "- **Organization membership query**: \`302\` (inconclusive)"
183+
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
184+
echo ""
185+
write_matching_restricted_paths
186+
write_review_label_action
187+
echo ""
188+
echo "- **Next step**: Review whether the PR author should be allowed to modify these restricted paths."
189+
echo ""
190+
echo "Please update the PR at: $PR_URL"
191+
} >> "$GITHUB_STEP_SUMMARY"
192+
;;
193+
404)
194+
ensure_org_review_label_absent
195+
echo "::error::This PR failed the author organization check. See the job summary for details."
196+
{
197+
echo "## PR Author Organization Check Failed"
198+
echo ""
199+
echo "- **Author**: $PR_AUTHOR"
200+
echo "- **Author association**: $AUTHOR_ASSOCIATION"
201+
echo "- **Organization membership query**: \`404\` (not a member)"
202+
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
203+
echo ""
204+
write_matching_restricted_paths
205+
write_review_label_action
206+
echo ""
207+
echo "- **Policy**: See \`cuda_bindings/LICENSE\` and \`cuda_python/LICENSE\`. Only NVIDIA organization members may modify files under \`cuda_bindings/\` or \`cuda_python/\`."
208+
echo ""
209+
echo "Please update the PR at: $PR_URL"
210+
} >> "$GITHUB_STEP_SUMMARY"
211+
exit 1
212+
;;
213+
*)
214+
echo "::error::Unexpected response from organization membership query: HTTP $MEMBERSHIP_STATUS."
215+
{
216+
echo "## PR Author Organization Check Failed"
217+
echo ""
218+
echo "- **Error**: Unexpected response from organization membership query."
219+
echo "- **Author**: $PR_AUTHOR"
220+
echo "- **Author association**: $AUTHOR_ASSOCIATION"
221+
echo "- **Organization membership query**: \`$MEMBERSHIP_STATUS\`"
222+
echo "- **Restricted paths**: \`cuda_bindings/\`, \`cuda_python/\`"
223+
echo ""
224+
write_matching_restricted_paths
225+
echo ""
226+
echo "Please update the PR at: $PR_URL"
227+
} >> "$GITHUB_STEP_SUMMARY"
228+
exit 1
229+
;;
230+
esac

0 commit comments

Comments
 (0)