From e305a774beed889263bf7554fd2dc92a8946e6db Mon Sep 17 00:00:00 2001 From: qwen-code-dev-bot Date: Wed, 28 Oct 2026 10:19:10 +0800 Subject: [PATCH] fix(release): use the run-scoped token for the publish checkout (#15728) * fix(release): stop authenticating the publish checkout with the bot PAT (#15726) The publish job's read-only checkout used secrets.CI_BOT_PAT while every other checkout in the release workflow uses the run-scoped token. Two release runs failed at exactly that step with "could not read Username for 'https://github.com': terminal prompts disabled" (git exit 128), and both passed on re-run. The bot PAT is already exported to the push and release-creation steps that write, so the read-only fetch does not need it. * test(ci): correct the release workspace wipe rationale (#15728) Co-authored-by: Qwen-Coder --------- Co-authored-by: qwen-code-dev-bot Co-authored-by: Qwen-Coder --- .github/workflows/release.yml | 2 +- scripts/tests/release-workflow.test.js | 30 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d2ce75f4d6b..b982eeb95e7 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -656,7 +656,7 @@ jobs: - name: 'Checkout' uses: 'actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10' # v6.0.3 with: - token: '${{ secrets.CI_BOT_PAT }}' + # Read-only fetch; the bot PAT is only exported to the write steps. ref: '${{ needs.prepare.outputs.release_sha }}' fetch-depth: 0 persist-credentials: false diff --git a/scripts/tests/release-workflow.test.js b/scripts/tests/release-workflow.test.js index 8b8388cc70b..ac1113c379c 100644 --- a/scripts/tests/release-workflow.test.js +++ b/scripts/tests/release-workflow.test.js @@ -295,12 +295,12 @@ describe('CUA release workflow', () => { // The canonical workspace restoration script shared by all nine // 'Restore workspace ownership' copies in release.yml. The full wipe (vs // serve-ab.yml's keep-and-scrub) is deliberate on this lane: release -// checkouts carry CI_BOT_PAT and the npm OIDC id-token, so no pre-existing -// repo state may survive into them. Pin the WHOLE body by equality, the way -// review-worktree-cleanup-workflow.test.js pins its sweep copies: a -// commented-out find, an inserted early exit, or a uniformly dropped -// ownership ladder all ship green under substring pins, and each of those -// mutants reopens the incident class this step exists for. +// checkouts persist credentials and the publish job can mint the npm OIDC +// id-token, so no pre-existing repo state may survive into them. Pin the +// WHOLE body by equality, the way review-worktree-cleanup-workflow.test.js +// pins its sweep copies: a commented-out find, an inserted early exit, or a +// uniformly dropped ownership ladder all ship green under substring pins, +// and each of those mutants reopens the incident class this step exists for. const canonicalWipe = `set -uo pipefail # Reject symlinked or non-canonical paths before recursive changes. RWS="\${RUNNER_WORKSPACE:?}" @@ -3476,6 +3476,24 @@ exit 0`, ); }); + it('reads the release tree with the run-scoped token, not the bot PAT', () => { + // The publish checkout is read-only and the repository is public, so + // authenticating it with the long-lived bot PAT adds an auth dependency + // without adding access: a transient PAT rejection failed the release at + // Checkout (#15726). The PAT stays exported to the steps that write. + const steps = releaseYaml.jobs.publish.steps; + const checkout = steps.find((step) => step.name === 'Checkout'); + expect(checkout.with.token).toBeUndefined(); + expect(checkout.with['persist-credentials']).toBe(false); + expect( + steps.find((step) => step.id === 'push_release_branch').env.CI_BOT_PAT, + ).toBe('${{ secrets.CI_BOT_PAT }}'); + expect( + steps.find((step) => step.name === 'Create GitHub Release and Tag').env + .GITHUB_TOKEN, + ).toBe('${{ secrets.CI_BOT_PAT }}'); + }); + it('serializes publish jobs per release tag', () => { // The pre-push re-validation below is only sound while at most one // publish job pushes and publishes a given version at a time; --force