From dccb7bf59d2ad2f008abff37aadcb44c08990ad5 Mon Sep 17 00:00:00 2001 From: John Myers Date: Wed, 18 Mar 2026 09:25:57 -0700 Subject: [PATCH] fix(ci): split vouch gate into two steps with separate tokens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ORG_READ_TOKEN (read:org PAT) was being used for all API calls, including closing PRs and posting comments, which it lacks permissions for. Split into two steps: 1. Org membership check — uses ORG_READ_TOKEN exclusively 2. VOUCHED.td check + close — uses default GITHUB_TOKEN (has repo write) Step 2 is skipped entirely if step 1 confirms org membership. --- .github/workflows/vouch-check.yml | 49 ++++++++++++------------------- 1 file changed, 19 insertions(+), 30 deletions(-) diff --git a/.github/workflows/vouch-check.yml b/.github/workflows/vouch-check.yml index 8ac0f133..178512b5 100644 --- a/.github/workflows/vouch-check.yml +++ b/.github/workflows/vouch-check.yml @@ -13,24 +13,15 @@ jobs: if: github.repository_owner == 'NVIDIA' runs-on: ubuntu-latest steps: - - name: Check if contributor is vouched + - name: Check org membership + id: org-check + if: ${{ secrets.ORG_READ_TOKEN != '' }} uses: actions/github-script@v7 with: - github-token: ${{ secrets.ORG_READ_TOKEN || secrets.GITHUB_TOKEN }} + github-token: ${{ secrets.ORG_READ_TOKEN }} + result-encoding: string script: | const author = context.payload.pull_request.user.login; - const authorType = context.payload.pull_request.user.type; - - // Skip bots (dependabot, renovate, github-actions, etc.). - if (authorType === 'Bot') { - console.log(`${author} is a bot. Skipping vouch check.`); - return; - } - - // Check org membership. Requires a token with read:org scope - // (ORG_READ_TOKEN secret). The default GITHUB_TOKEN cannot see org - // membership, so author_association and orgs.checkMembershipForUser - // both return NONE/404 for private members. try { const { status } = await github.rest.orgs.checkMembershipForUser({ org: context.repo.owner, @@ -38,29 +29,27 @@ jobs: }); if (status === 204 || status === 302) { console.log(`${author} is an org member. Skipping vouch check.`); - return; + return 'skip'; } } catch (e) { if (e.status !== 404) { console.log(`Org membership check error (status=${e.status}): ${e.message}`); } } + return ''; - // Check collaborator status — direct collaborators bypass. - try { - const { status } = await github.rest.repos.checkCollaborator({ - owner: context.repo.owner, - repo: context.repo.repo, - username: author, - }); - if (status === 204) { - console.log(`${author} is a repo collaborator. Skipping vouch check.`); - return; - } - } catch (e) { - if (e.status !== 404) { - console.log(`Collaborator check error (status=${e.status}): ${e.message}`); - } + - name: Check if contributor is vouched + if: steps.org-check.outputs.result != 'skip' + uses: actions/github-script@v7 + with: + script: | + const author = context.payload.pull_request.user.login; + const authorType = context.payload.pull_request.user.type; + + // Skip bots (dependabot, renovate, github-actions, etc.). + if (authorType === 'Bot') { + console.log(`${author} is a bot. Skipping vouch check.`); + return; } // Check the VOUCHED.td file on the dedicated "vouched" branch.