Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 36 additions & 42 deletions .github/workflows/contributor-guidance.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
# This action requires the following secrets to be set on the repository:
# GH_USER_TOKEN: GitHub user token, used because FIRST_TIMER and
# FIRST_TIME_CONTRIBUTOR associations are only returned for
# privileged API calls

name: Contributor guidance

on:
Expand All @@ -9,8 +14,7 @@ permissions: {}
jobs:
contributor:
name: Resolve contributor status
# Use the event only to exclude trusted associations, mannequins, and bots.
# Resolve every potentially external contributor with a privileged API request.
# Avoid allocating a runner unless a welcome or scan is a plausible outcome.
if: >-
github.run_attempt == 1 &&
github.repository == 'nodejs/node' &&
Expand All @@ -22,52 +26,40 @@ jobs:
runs-on: ubuntu-slim
outputs:
is_first_time: ${{ steps.resolve.outputs.is_first_time }}
# The event association may not expose private organization membership,
# so gate the guidance job on the privileged API result.
should_scan: ${{ steps.resolve.outputs.should_scan }}
steps:
- name: Check author association
id: resolve
env:
EVENT_ASSOCIATION: ${{ github.event.pull_request.author_association }}
GH_TOKEN: ${{ secrets.GH_USER_TOKEN }}
NUMBER: ${{ github.event.pull_request.number }}
run: |
started_at=$SECONDS
# TODO: Remove the retries once privileged API requests are confirmed
# to return the association immediately.
for delay in 0 15 30 60; do
sleep "$delay"
association=$(gh api "/repos/$GITHUB_REPOSITORY/pulls/$NUMBER" \
--jq '.author_association')
elapsed=$((SECONDS - started_at))
echo "Author association after ${elapsed}s: $association"

case "$association" in
FIRST_TIMER|FIRST_TIME_CONTRIBUTOR)
echo 'is_first_time=true' >> "$GITHUB_OUTPUT"
echo 'should_scan=true' >> "$GITHUB_OUTPUT"
exit 0
;;
CONTRIBUTOR)
echo 'is_first_time=false' >> "$GITHUB_OUTPUT"
echo 'should_scan=true' >> "$GITHUB_OUTPUT"
exit 0
;;
NONE)
;;
COLLABORATOR|MANNEQUIN|MEMBER|OWNER)
echo 'is_first_time=false' >> "$GITHUB_OUTPUT"
echo 'should_scan=false' >> "$GITHUB_OUTPUT"
exit 0
;;
*)
echo "Unexpected author association: $association" >&2
exit 1
;;
esac
done
api_association=$(gh api "/repos/$GITHUB_REPOSITORY/pulls/$NUMBER" \
--jq '.author_association')
echo "API author association: $api_association"
echo "Event author association: $EVENT_ASSOCIATION"

# NONE is unresolved, not trusted. Scan without posting the welcome.
echo 'is_first_time=false' >> "$GITHUB_OUTPUT"
echo 'should_scan=true' >> "$GITHUB_OUTPUT"
case "$api_association" in
FIRST_TIMER|FIRST_TIME_CONTRIBUTOR)
echo 'is_first_time=true' >> "$GITHUB_OUTPUT"
echo 'should_scan=true' >> "$GITHUB_OUTPUT"
;;
CONTRIBUTOR|NONE)
echo 'is_first_time=false' >> "$GITHUB_OUTPUT"
echo 'should_scan=true' >> "$GITHUB_OUTPUT"
;;
COLLABORATOR|MANNEQUIN|MEMBER|OWNER)
echo 'is_first_time=false' >> "$GITHUB_OUTPUT"
echo 'should_scan=false' >> "$GITHUB_OUTPUT"
;;
Comment thread
panva marked this conversation as resolved.
*)
echo "Unexpected author association: $api_association" >&2
exit 1
;;
esac

guidance:
name: Apply contributor guidance
Expand All @@ -90,8 +82,12 @@ jobs:
scan-issues: false
auto-close: false
honeypot: false
# The privileged association check skips trusted authors before this job,
# so `trusted-author-associations` would be redundant here.
# trusted-author-associations: collaborator,member,owner

- name: Comment with contributor guidance
if: env.IS_FIRST_TIME == 'true' || env.ADD_CAUTION == 'true'
env:
ADD_CAUTION: >-
${{
Expand Down Expand Up @@ -137,10 +133,8 @@ jobs:
body="$WELCOME_MESSAGE"$'\n\n'"$CAUTION_MESSAGE"
elif [[ "$IS_FIRST_TIME" == "true" ]]; then
body="$WELCOME_MESSAGE"
elif [[ "$ADD_CAUTION" == "true" ]]; then
body="$CAUTION_MESSAGE"
else
exit 0
body="$CAUTION_MESSAGE"
fi

printf '%s\n' "$body" |
Expand Down