From c297507785b8823c4f6fee1cfeee4192a55425dc Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 2 Sep 2026 14:21:00 +0200 Subject: [PATCH 1/3] tools: refine contributor guidance workflow Use the Node.js GitHub bot token for association checks and route the welcome and caution messages independently. Signed-off-by: Filip Skokan --- ...ntributor.yml => contributor-guidance.yml} | 69 +++++++++++-------- 1 file changed, 39 insertions(+), 30 deletions(-) rename .github/workflows/{first-time-contributor.yml => contributor-guidance.yml} (70%) diff --git a/.github/workflows/first-time-contributor.yml b/.github/workflows/contributor-guidance.yml similarity index 70% rename from .github/workflows/first-time-contributor.yml rename to .github/workflows/contributor-guidance.yml index f825cd77dc05..15cfadeffb53 100644 --- a/.github/workflows/first-time-contributor.yml +++ b/.github/workflows/contributor-guidance.yml @@ -1,4 +1,4 @@ -name: Welcome first-time contributors +name: Contributor guidance on: pull_request_target: @@ -10,31 +10,38 @@ jobs: first_time_contributor: name: Is first-time contributor # GitHub can report first-time contributors as NONE in the event payload. + # npm-cli-bot and nodejs-github-bot report MEMBER and are excluded below. if: >- github.run_attempt == 1 && github.repository == 'nodejs/node' && + github.event.pull_request.user.login != 'dependabot[bot]' && (github.event.pull_request.author_association == 'FIRST_TIMER' || github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' || github.event.pull_request.author_association == 'NONE') runs-on: ubuntu-slim - permissions: - pull-requests: read outputs: - eligible: >- + is_first_time: >- ${{ github.event.pull_request.author_association != 'NONE' || - steps.recheck.outputs.eligible == 'true' + steps.recheck.outputs.is_first_time == 'true' + }} + resolved: >- + ${{ + github.event.pull_request.author_association != 'NONE' || + steps.recheck.outputs.resolved == 'true' }} steps: - name: Recheck contributor eligibility id: recheck if: github.event.pull_request.author_association == 'NONE' env: - GH_TOKEN: ${{ github.token }} + GH_TOKEN: ${{ secrets.GH_USER_TOKEN }} NUMBER: ${{ github.event.pull_request.number }} run: | started_at=$SECONDS - for delay in 15 30 60 120; do + # 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') @@ -43,23 +50,26 @@ jobs: case "$association" in FIRST_TIMER|FIRST_TIME_CONTRIBUTOR) - echo 'eligible=true' >> "$GITHUB_OUTPUT" + echo 'is_first_time=true' >> "$GITHUB_OUTPUT" + echo 'resolved=true' >> "$GITHUB_OUTPUT" exit 0 ;; NONE) ;; *) - echo 'eligible=false' >> "$GITHUB_OUTPUT" + echo 'is_first_time=false' >> "$GITHUB_OUTPUT" + echo 'resolved=true' >> "$GITHUB_OUTPUT" exit 0 ;; esac done - echo 'eligible=false' >> "$GITHUB_OUTPUT" + echo 'is_first_time=false' >> "$GITHUB_OUTPUT" + echo 'resolved=false' >> "$GITHUB_OUTPUT" agentscan: needs: first_time_contributor - if: needs.first_time_contributor.outputs.eligible == 'true' + if: needs.first_time_contributor.outputs.resolved == 'true' runs-on: ubuntu-slim permissions: contents: read @@ -67,6 +77,13 @@ jobs: scan_outcome: ${{ steps.scan.outcome }} classification: ${{ steps.scan.outputs.classification }} community_flagged: ${{ steps.scan.outputs['community-flagged'] }} + caution: >- + ${{ + steps.scan.outcome == 'success' && + (steps.scan.outputs.classification == 'mixed' || + steps.scan.outputs.classification == 'automation' || + steps.scan.outputs['community-flagged'] == 'true') + }} steps: - name: Scan contributor activity id: scan @@ -85,18 +102,20 @@ jobs: needs: - first_time_contributor - agentscan - if: needs.first_time_contributor.outputs.eligible == 'true' + if: >- + needs.first_time_contributor.outputs.is_first_time == 'true' || + (needs.first_time_contributor.outputs.resolved == 'true' && + needs.agentscan.outputs.caution == 'true') runs-on: ubuntu-slim permissions: pull-requests: write steps: - - name: Welcome first-time contributor + - name: Comment with contributor guidance env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} NUMBER: ${{ github.event.pull_request.number }} - AGENTSCAN_OUTCOME: ${{ needs.agentscan.outputs.scan_outcome }} - AGENTSCAN_CLASSIFICATION: ${{ needs.agentscan.outputs.classification }} - AGENTSCAN_COMMUNITY_FLAGGED: ${{ needs.agentscan.outputs.community_flagged }} + IS_FIRST_TIME: ${{ needs.first_time_contributor.outputs.is_first_time }} + ADD_CAUTION: ${{ needs.agentscan.outputs.caution }} WELCOME_MESSAGE: >2- Welcome to Node.js, and thank you for your first contribution! @@ -128,20 +147,10 @@ jobs: [automation policy](https://github.com/nodejs/node/blob/HEAD/CONTRIBUTING.md#automation-and-bots) for additional context. run: | - add_caution=false - if [[ "$AGENTSCAN_OUTCOME" == "success" ]]; then - case "$AGENTSCAN_CLASSIFICATION" in - mixed|automation) - add_caution=true - ;; - esac - if [[ "$AGENTSCAN_COMMUNITY_FLAGGED" == "true" ]]; then - add_caution=true - fi - fi - - if [[ "$add_caution" == "true" ]]; then + if [[ "$IS_FIRST_TIME" == "true" && "$ADD_CAUTION" == "true" ]]; then printf '%s\n\n%s\n' "$WELCOME_MESSAGE" "$CAUTION_MESSAGE" - else + elif [[ "$IS_FIRST_TIME" == "true" ]]; then printf '%s\n' "$WELCOME_MESSAGE" + elif [[ "$ADD_CAUTION" == "true" ]]; then + printf '%s\n' "$CAUTION_MESSAGE" fi | gh pr comment "$NUMBER" --repo "$GITHUB_REPOSITORY" --body-file - From 69d708c4981425909dcb8e46f8750a6ac4549867 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 2 Sep 2026 16:00:39 +0200 Subject: [PATCH 2/3] fixup! tools: refine contributor guidance workflow --- .github/workflows/contributor-guidance.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/contributor-guidance.yml b/.github/workflows/contributor-guidance.yml index 15cfadeffb53..521c87f36a9d 100644 --- a/.github/workflows/contributor-guidance.yml +++ b/.github/workflows/contributor-guidance.yml @@ -73,6 +73,7 @@ jobs: runs-on: ubuntu-slim permissions: contents: read + pull-requests: write outputs: scan_outcome: ${{ steps.scan.outcome }} classification: ${{ steps.scan.outputs.classification }} @@ -92,7 +93,7 @@ jobs: uses: MatteoGabriele/agentscan-action@98202262c925c508d4c1424b1dfbe17ee35b0c02 # v2.4.0 with: github-token: ${{ github.token }} - mode: silent + mode: labels scan-pull-requests: true scan-issues: false auto-close: false From 7888f3fff6190ef04c3560039bc3a280c3c02186 Mon Sep 17 00:00:00 2001 From: Filip Skokan Date: Wed, 2 Sep 2026 16:33:16 +0200 Subject: [PATCH 3/3] fixup! tools: refine contributor guidance workflow --- .github/workflows/contributor-guidance.yml | 104 ++++++++++----------- 1 file changed, 51 insertions(+), 53 deletions(-) diff --git a/.github/workflows/contributor-guidance.yml b/.github/workflows/contributor-guidance.yml index 521c87f36a9d..ca7fa1b2bbba 100644 --- a/.github/workflows/contributor-guidance.yml +++ b/.github/workflows/contributor-guidance.yml @@ -7,10 +7,10 @@ on: permissions: {} jobs: - first_time_contributor: - name: Is first-time contributor + contributor: + name: Resolve contributor status # GitHub can report first-time contributors as NONE in the event payload. - # npm-cli-bot and nodejs-github-bot report MEMBER and are excluded below. + # npm-cli-bot and nodejs-github-bot report MEMBER and are filtered here. if: >- github.run_attempt == 1 && github.repository == 'nodejs/node' && @@ -20,24 +20,33 @@ jobs: github.event.pull_request.author_association == 'NONE') runs-on: ubuntu-slim outputs: - is_first_time: >- - ${{ - github.event.pull_request.author_association != 'NONE' || - steps.recheck.outputs.is_first_time == 'true' - }} - resolved: >- - ${{ - github.event.pull_request.author_association != 'NONE' || - steps.recheck.outputs.resolved == 'true' - }} + is_first_time: ${{ steps.resolve.outputs.is_first_time }} + should_scan: ${{ steps.resolve.outputs.should_scan }} steps: - - name: Recheck contributor eligibility - id: recheck - if: github.event.pull_request.author_association == 'NONE' + - 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: | + echo "Author association from event: $EVENT_ASSOCIATION" + + case "$EVENT_ASSOCIATION" in + FIRST_TIMER|FIRST_TIME_CONTRIBUTOR) + echo 'is_first_time=true' >> "$GITHUB_OUTPUT" + echo 'should_scan=true' >> "$GITHUB_OUTPUT" + exit 0 + ;; + NONE) + ;; + *) + echo 'is_first_time=false' >> "$GITHUB_OUTPUT" + echo 'should_scan=false' >> "$GITHUB_OUTPUT" + exit 0 + ;; + esac + started_at=$SECONDS # TODO: Remove the retries once privileged API requests are confirmed # to return the association immediately. @@ -51,46 +60,36 @@ jobs: case "$association" in FIRST_TIMER|FIRST_TIME_CONTRIBUTOR) echo 'is_first_time=true' >> "$GITHUB_OUTPUT" - echo 'resolved=true' >> "$GITHUB_OUTPUT" + echo 'should_scan=true' >> "$GITHUB_OUTPUT" exit 0 ;; NONE) ;; *) echo 'is_first_time=false' >> "$GITHUB_OUTPUT" - echo 'resolved=true' >> "$GITHUB_OUTPUT" + echo 'should_scan=true' >> "$GITHUB_OUTPUT" exit 0 ;; esac done echo 'is_first_time=false' >> "$GITHUB_OUTPUT" - echo 'resolved=false' >> "$GITHUB_OUTPUT" + echo 'should_scan=false' >> "$GITHUB_OUTPUT" - agentscan: - needs: first_time_contributor - if: needs.first_time_contributor.outputs.resolved == 'true' + guidance: + name: Apply contributor guidance + needs: contributor + if: needs.contributor.outputs.should_scan == 'true' runs-on: ubuntu-slim permissions: contents: read pull-requests: write - outputs: - scan_outcome: ${{ steps.scan.outcome }} - classification: ${{ steps.scan.outputs.classification }} - community_flagged: ${{ steps.scan.outputs['community-flagged'] }} - caution: >- - ${{ - steps.scan.outcome == 'success' && - (steps.scan.outputs.classification == 'mixed' || - steps.scan.outputs.classification == 'automation' || - steps.scan.outputs['community-flagged'] == 'true') - }} steps: - name: Scan contributor activity - id: scan + id: agentscan # The welcome should still be posted if this advisory scan fails. continue-on-error: true - uses: MatteoGabriele/agentscan-action@98202262c925c508d4c1424b1dfbe17ee35b0c02 # v2.4.0 + uses: MatteoGabriele/agentscan-action@8112fb79b33fafb8506159df20129a34209ac410 # v2.5.0 with: github-token: ${{ github.token }} mode: labels @@ -99,24 +98,18 @@ jobs: auto-close: false honeypot: false - comment: - needs: - - first_time_contributor - - agentscan - if: >- - needs.first_time_contributor.outputs.is_first_time == 'true' || - (needs.first_time_contributor.outputs.resolved == 'true' && - needs.agentscan.outputs.caution == 'true') - runs-on: ubuntu-slim - permissions: - pull-requests: write - steps: - name: Comment with contributor guidance env: + ADD_CAUTION: >- + ${{ + steps.agentscan.outcome == 'success' && + (steps.agentscan.outputs.classification == 'mixed' || + steps.agentscan.outputs.classification == 'automation' || + steps.agentscan.outputs['community-flagged'] == 'true') + }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IS_FIRST_TIME: ${{ needs.contributor.outputs.is_first_time }} NUMBER: ${{ github.event.pull_request.number }} - IS_FIRST_TIME: ${{ needs.first_time_contributor.outputs.is_first_time }} - ADD_CAUTION: ${{ needs.agentscan.outputs.caution }} WELCOME_MESSAGE: >2- Welcome to Node.js, and thank you for your first contribution! @@ -149,9 +142,14 @@ jobs: for additional context. run: | if [[ "$IS_FIRST_TIME" == "true" && "$ADD_CAUTION" == "true" ]]; then - printf '%s\n\n%s\n' "$WELCOME_MESSAGE" "$CAUTION_MESSAGE" + body="$WELCOME_MESSAGE"$'\n\n'"$CAUTION_MESSAGE" elif [[ "$IS_FIRST_TIME" == "true" ]]; then - printf '%s\n' "$WELCOME_MESSAGE" + body="$WELCOME_MESSAGE" elif [[ "$ADD_CAUTION" == "true" ]]; then - printf '%s\n' "$CAUTION_MESSAGE" - fi | gh pr comment "$NUMBER" --repo "$GITHUB_REPOSITORY" --body-file - + body="$CAUTION_MESSAGE" + else + exit 0 + fi + + printf '%s\n' "$body" | + gh pr comment "$NUMBER" --repo "$GITHUB_REPOSITORY" --body-file -