From 70eaf9c5aa09c6f529db3aa47e464f474060c39e Mon Sep 17 00:00:00 2001 From: Oli Juhl <59018053+olivermrbl@users.noreply.github.com> Date: Thu, 11 Dec 2025 18:25:25 +0100 Subject: [PATCH] chore: Introduce OIDC auth for npm publishing (#14279) --- .github/workflows/snapshot-this.yml | 142 ------------------------- .github/workflows/trigger-release.yml | 144 ++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 149 deletions(-) delete mode 100644 .github/workflows/snapshot-this.yml diff --git a/.github/workflows/snapshot-this.yml b/.github/workflows/snapshot-this.yml deleted file mode 100644 index effab869e5..0000000000 --- a/.github/workflows/snapshot-this.yml +++ /dev/null @@ -1,142 +0,0 @@ -# Inspired from https://github.com/Shopify/quilt/blob/main/.github/workflows/snapit.yml -name: Snapshot This - -on: - issue_comment: - types: - - created - -concurrency: ${{ github.workflow }}-${{ github.ref }} - -jobs: - snapshot: - name: Snapshot Release - if: | - github.event.issue.pull_request && - github.event.comment.body == '/snapshot-this' - runs-on: ubuntu-latest - env: - TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} - TURBO_TEAM: ${{ secrets.TURBO_TEAM }} - NODE_OPTIONS: "--max_old_space_size=4096" - steps: - - name: Validate pull request - uses: actions/github-script@v6 - with: - script: | - try { - // Add a rocket reaction to the comment - await github.rest.reactions.createForIssueComment({ - ...context.repo, - comment_id: context.payload.comment.id, - content: 'rocket', - }) - - // Only allow comment creators who have "write" permissions to repo - const actorPermission = (await github.rest.repos.getCollaboratorPermissionLevel({ - ...context.repo, - username: context.actor - })).data.permission - const isPermitted = ['write', 'admin'].includes(actorPermission) - if (!isPermitted) { - const errorMessage = 'Only users with write permission to the respository can run /snapshot-this' - await github.rest.issues.createComment({ - ...context.repo, - issue_number: context.issue.number, - body: errorMessage, - }) - core.setFailed(errorMessage) - return; - } - - const pullRequest = await github.rest.pulls.get({ - ...context.repo, - pull_number: context.issue.number, - }) - // Pull request from fork - if (context.payload.repository.full_name !== pullRequest.data.head.repo.full_name) { - const errorMessage = '`/snapshot-this` is not supported on pull requests from forked repositories.' - await github.rest.issues.createComment({ - ...context.repo, - issue_number: context.issue.number, - body: errorMessage, - }) - core.setFailed(errorMessage) - } - } catch (err) { - core.setFailed(`Request failed with error ${err}`) - } - - name: Checkout pull request branch - uses: actions/checkout@v3 - with: - ref: ${{ format('refs/pull/{0}/merge', github.event.issue.number) }} - - # Because changeset entries are consumed and removed on the - # 'changeset-release/main' branch, we need to reset the files - # so the following 'changeset version --snapshot' command will - # regenerate the package version bumps with the snapshot releases - - name: Reset changeset entries on changeset-release/main branch - run: | - if [[ $(git branch --show-current) == 'changeset-release/main' ]]; then - git checkout origin/main -- .changeset - fi - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 20 - cache: "yarn" - - - name: Install dependencies - uses: ./.github/actions/cache-deps - with: - extension: snapshot-this - skip-build: "true" - - - name: Build Packages - shell: "bash" - run: yarn build - - - name: Create an .npmrc - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - cat << EOF > "$HOME/.npmrc" - //registry.npmjs.org/:_authToken=$NPM_TOKEN - EOF - - - name: Create and publish snapshot release - uses: actions/github-script@v6 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - script: | - await exec.exec('yarn run changeset version --snapshot snapshot') - const {stdout} = await exec.getExecOutput('yarn run release:snapshot') - const newTags = Array - .from(stdout.matchAll(/New tag:\s+([^\s\n]+)/g)) - .map(([_, tag]) => tag) - if (newTags.length) { - const multiple = newTags.length > 1 - const body = ( - `#### :rocket: A snapshot release has been made for this PR\n\n` + - `Test the snapshot${multiple ? 's' : ''} by updating your \`package.json\` ` + - `with the newly published version${multiple ? 's' : ''}:\n` + - newTags.map(tag => ( - '```sh\n' + - `yarn add ${tag}\n` + - '```' - )).join('\n') + - `\n\n> Latest commit: ${context.sha}` - - ) - await github.rest.issues.createComment({ - ...context.repo, - issue_number: context.issue.number, - body, - }) - await github.rest.reactions.createForIssueComment({ - ...context.repo, - comment_id: context.payload.comment.id, - content: 'hooray', - }) - } diff --git a/.github/workflows/trigger-release.yml b/.github/workflows/trigger-release.yml index df1cde2712..8c51c57796 100644 --- a/.github/workflows/trigger-release.yml +++ b/.github/workflows/trigger-release.yml @@ -23,12 +23,20 @@ on: - ".github/**" schedule: - cron: "0 */3 * * *" + issue_comment: + types: + - created concurrency: ${{ github.workflow }}-${{ github.ref }} +permissions: + pull-requests: write # to be able to comment on released pull requests + id-token: write # to enable use of OIDC for trusted publishing and npm provenance + jobs: release: name: Trigger Preview Release + if: github.event_name != 'issue_comment' runs-on: ubuntu-latest steps: - name: Checkout Repo @@ -39,13 +47,8 @@ jobs: with: node-version: 20 - - name: Creating .npmrc - run: | - cat << EOF > "$HOME/.npmrc" - //registry.npmjs.org/:_authToken=$NPM_TOKEN - EOF - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Ensure npm 11.5.1+ for OIDC + run: npm install -g npm@latest - name: Install Dependencies run: yarn @@ -92,3 +95,130 @@ jobs: env: SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_RELEASE }} SLACK_WEBHOOK_TYPE: INCOMING_WEBHOOK + + snapshot: + name: Snapshot Release + if: | + github.event.issue.pull_request && + github.event.comment.body == '/snapshot-this' + runs-on: ubuntu-latest + env: + TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} + TURBO_TEAM: ${{ secrets.TURBO_TEAM }} + NODE_OPTIONS: "--max_old_space_size=4096" + steps: + - name: Validate pull request + uses: actions/github-script@v6 + with: + script: | + try { + // Add a rocket reaction to the comment + await github.rest.reactions.createForIssueComment({ + ...context.repo, + comment_id: context.payload.comment.id, + content: 'rocket', + }) + + // Only allow comment creators who have "write" permissions to repo + const actorPermission = (await github.rest.repos.getCollaboratorPermissionLevel({ + ...context.repo, + username: context.actor + })).data.permission + const isPermitted = ['write', 'admin'].includes(actorPermission) + if (!isPermitted) { + const errorMessage = 'Only users with write permission to the respository can run /snapshot-this' + await github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body: errorMessage, + }) + core.setFailed(errorMessage) + return; + } + + const pullRequest = await github.rest.pulls.get({ + ...context.repo, + pull_number: context.issue.number, + }) + // Pull request from fork + if (context.payload.repository.full_name !== pullRequest.data.head.repo.full_name) { + const errorMessage = '`/snapshot-this` is not supported on pull requests from forked repositories.' + await github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body: errorMessage, + }) + core.setFailed(errorMessage) + } + } catch (err) { + core.setFailed(`Request failed with error ${err}`) + } + - name: Checkout pull request branch + uses: actions/checkout@v3 + with: + ref: ${{ format('refs/pull/{0}/merge', github.event.issue.number) }} + + # Because changeset entries are consumed and removed on the + # 'changeset-release/main' branch, we need to reset the files + # so the following 'changeset version --snapshot' command will + # regenerate the package version bumps with the snapshot releases + - name: Reset changeset entries on changeset-release/main branch + run: | + if [[ $(git branch --show-current) == 'changeset-release/main' ]]; then + git checkout origin/main -- .changeset + fi + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "yarn" + + - name: Ensure npm 11.5.1+ for OIDC + run: npm install -g npm@latest + + - name: Install dependencies + uses: ./.github/actions/cache-deps + with: + extension: snapshot-this + skip-build: "true" + + - name: Build Packages + shell: "bash" + run: yarn build + + - name: Create and publish snapshot release + uses: actions/github-script@v6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + script: | + await exec.exec('yarn run changeset version --snapshot snapshot') + const {stdout} = await exec.getExecOutput('yarn run release:snapshot') + const newTags = Array + .from(stdout.matchAll(/New tag:\s+([^\s\n]+)/g)) + .map(([_, tag]) => tag) + if (newTags.length) { + const multiple = newTags.length > 1 + const body = ( + `#### :rocket: A snapshot release has been made for this PR\n\n` + + `Test the snapshot${multiple ? 's' : ''} by updating your \`package.json\` ` + + `with the newly published version${multiple ? 's' : ''}:\n` + + newTags.map(tag => ( + '```sh\n' + + `yarn add ${tag}\n` + + '```' + )).join('\n') + + `\n\n> Latest commit: ${context.sha}` + + ) + await github.rest.issues.createComment({ + ...context.repo, + issue_number: context.issue.number, + body, + }) + await github.rest.reactions.createForIssueComment({ + ...context.repo, + comment_id: context.payload.comment.id, + content: 'hooray', + }) + }