From cb1e55687b01aa448e339ac575dee937752c9cbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= Date: Wed, 17 Jun 2026 14:18:24 +0200 Subject: [PATCH 1/3] ci: publish PR canaries via OIDC by folding them into release.yml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm trusted publishing allows exactly one trusted publisher per package (one repo + one workflow file), and checkly's slot is release.yml — which is why the normal release publishes via OIDC + --provenance. release-canary.yml was a separate workflow using a classic NODE_AUTH_TOKEN. That token was retired when the package moved to OIDC, so a separate workflow could no longer authenticate and the canary has failed on every PR since. Fold the canary job into release.yml so it inherits the trusted publisher: - add a `pull_request: [labeled]` trigger; the release-event jobs now gate on `github.event_name == 'release'` (PR events cascade-skip via needs: validate-tag). - the canary job publishes 0.0.0-pr.. with id-token + --provenance (no token), tagged `experimental` — or a `canary:` label's tag, applied at publish time (trusted publishing authenticates `npm publish`, not a separate `npm dist-tag add`). - remove release-canary.yml. --- .github/workflows/release-canary.yml | 62 ------------------------- .github/workflows/release.yml | 69 ++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 62 deletions(-) delete mode 100644 .github/workflows/release-canary.yml diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml deleted file mode 100644 index 26d230c15..000000000 --- a/.github/workflows/release-canary.yml +++ /dev/null @@ -1,62 +0,0 @@ -name: release-canary - -on: - pull_request: - types: - - labeled - -jobs: - publish: - if: ${{ github.event.label.name == 'build' }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - - uses: pnpm/action-setup@v5 - with: - version: 10 - - uses: actions/setup-node@v4 - with: - node-version: '24.x' - registry-url: 'https://registry.npmjs.org' - cache: "pnpm" - # Extract the dynamic value from the canary label if present - - name: Extract CANARY_TAG - id: extract-canary - run: | - export LABELS_JSON='${{ toJson(github.event.pull_request.labels) }}' - CANARY_TAG=$(node -e " - const labels = JSON.parse(process.env.LABELS_JSON || '[]'); - const canaryLabel = labels.find(label => label.name.startsWith('canary:')); - if (canaryLabel) console.log(canaryLabel.name.split(':')[1]); - ") - echo "CANARY_TAG=$CANARY_TAG" >> $GITHUB_ENV - # Ensure that the README is published with the package - - run: rm -f packages/cli/README.md && cp README.md packages/cli - - run: echo "PR_VERSION=0.0.0-pr.${{github.event.pull_request.number}}.$(git rev-parse --short HEAD)" >> $GITHUB_ENV - - run: pnpm install --frozen-lockfile - - name: Set version, pack, and publish - run: | - pnpm version ${{ env.PR_VERSION }} --no-git-tag-version - pnpm pack - npm publish checkly-*.tgz --tag experimental - if [[ -n "$CANARY_TAG" ]]; then - echo "Publishing with additional tag: $CANARY_TAG" - npm dist-tag add checkly@$PR_VERSION $CANARY_TAG - fi - working-directory: packages/cli - env: - CANARY_TAG: ${{ env.CANARY_TAG }} - PR_VERSION: ${{ env.PR_VERSION }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: PR Preview Release Published - hide_and_recreate: true - hide_classify: "OUTDATED" - message: | - 🎉 Experimental release successfully published [on npm](https://npmjs.com/package/checkly/v/${{env.PR_VERSION}}) - ``` - npm install checkly@${{env.PR_VERSION}} - ``` diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8497d9224..b3d02b455 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,8 +2,18 @@ name: Publish Package to npmjs on: release: types: [published] + # PR canary builds: adding the `build` label to a PR publishes an experimental + # build of the PR branch to npm. This lives in release.yml (rather than a + # separate workflow) so it authenticates via the npm OIDC trusted publisher — + # npm allows only ONE trusted publisher (repo + workflow file) per package, and + # that slot is release.yml. + pull_request: + types: [labeled] jobs: validate-tag: + # Release-event path only. PR canaries run the `canary` job below; the other + # release jobs cascade-skip on PR events via their `needs: validate-tag`. + if: ${{ github.event_name == 'release' }} runs-on: ubuntu-latest steps: - uses: actions-ecosystem/action-regex-match@v2 @@ -183,3 +193,62 @@ jobs: else echo "Skipping: $NEW_TAG is not newer than current latest $CURRENT_LATEST" fi + + # PR canary: publish an experimental build of the PR branch, triggered by the + # `build` label. Authenticates via the SAME npm OIDC trusted publisher as the + # release jobs above (id-token + --provenance, no NPM_TOKEN). Replaces the old + # release-canary.yml, which used a classic NPM_TOKEN that was retired when the + # package moved to OIDC — so it had been failing for every PR. + canary: + if: ${{ github.event_name == 'pull_request' && github.event.label.name == 'build' }} + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + pull-requests: write + steps: + - uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + - uses: pnpm/action-setup@v5 + with: + version: 10 + - uses: actions/setup-node@v4 + with: + node-version: '24.x' + cache: "pnpm" + # A `canary:` label publishes under instead of `experimental`. + # (Trusted publishing authenticates `npm publish` only, so we set the tag at + # publish time rather than with a separate, unauthenticated `npm dist-tag add`.) + - name: Extract CANARY_TAG + run: | + export LABELS_JSON='${{ toJson(github.event.pull_request.labels) }}' + CANARY_TAG=$(node -e " + const labels = JSON.parse(process.env.LABELS_JSON || '[]'); + const canaryLabel = labels.find(label => label.name.startsWith('canary:')); + if (canaryLabel) console.log(canaryLabel.name.split(':')[1]); + ") + echo "CANARY_TAG=$CANARY_TAG" >> $GITHUB_ENV + # Ensure that the README is published with the package + - run: rm -f packages/cli/README.md && cp README.md packages/cli + - run: echo "PR_VERSION=0.0.0-pr.${{ github.event.pull_request.number }}.$(git rev-parse --short HEAD)" >> $GITHUB_ENV + - run: pnpm install --frozen-lockfile + - name: Set version, pack, and publish (OIDC trusted publishing) + run: | + pnpm version ${{ env.PR_VERSION }} --no-git-tag-version + pnpm pack + npm publish checkly-*.tgz --provenance --tag "${CANARY_TAG:-experimental}" + working-directory: packages/cli + env: + CANARY_TAG: ${{ env.CANARY_TAG }} + PR_VERSION: ${{ env.PR_VERSION }} + - uses: marocchino/sticky-pull-request-comment@v2 + with: + header: PR Preview Release Published + hide_and_recreate: true + hide_classify: "OUTDATED" + message: | + 🎉 Experimental release successfully published [on npm](https://npmjs.com/package/checkly/v/${{ env.PR_VERSION }}) + ``` + npm install checkly@${{ env.PR_VERSION }} + ``` From e655dc32b81a31abedb10b0a4cfc714ce45245ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= Date: Wed, 17 Jun 2026 14:21:46 +0200 Subject: [PATCH 2/3] ci: name canary vs release runs distinctly in the Actions list Add a run-name expression so PR-label canary runs render as 'Canary build - PR # ()' and release-event runs as 'Release ', instead of the default commit-subject run name. --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b3d02b455..60623ad59 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,4 +1,7 @@ name: Publish Package to npmjs +# Name each run for its trigger, so canary (PR-label) runs are distinguishable +# from real releases in the Actions list. +run-name: "${{ github.event_name == 'pull_request' && format('Canary build - PR #{0} ({1})', github.event.pull_request.number, github.event.pull_request.head.ref) || format('Release {0}', github.event.release.tag_name) }}" on: release: types: [published] From 43a7b7499975fc1470ad92167edf2bb519730874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20P=C3=A9rez?= Date: Wed, 17 Jun 2026 14:28:44 +0200 Subject: [PATCH 3/3] ci: trigger canary via workflow_dispatch instead of a PR label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A pull_request:labeled trigger fired release.yml on every label on every PR (jobs skipped, but the release workflow was still invoked). Drop it: the canary is now a manual workflow_dispatch (`gh workflow run release.yml --ref `, optional `-f tag=`), publishing 0.0.0-canary. via the same OIDC trusted publisher. release.yml now triggers only on a real release or an explicit dispatch — never on labels. The build-label mechanism / release-canary.yml are gone. --- .github/workflows/release.yml | 81 ++++++++++++++--------------------- 1 file changed, 32 insertions(+), 49 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 60623ad59..a380732ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,21 +1,24 @@ name: Publish Package to npmjs -# Name each run for its trigger, so canary (PR-label) runs are distinguishable +# Name each run for its trigger, so manual canary builds are distinguishable # from real releases in the Actions list. -run-name: "${{ github.event_name == 'pull_request' && format('Canary build - PR #{0} ({1})', github.event.pull_request.number, github.event.pull_request.head.ref) || format('Release {0}', github.event.release.tag_name) }}" +run-name: "${{ github.event_name == 'workflow_dispatch' && format('Canary build - {0}', github.ref_name) || format('Release {0}', github.event.release.tag_name) }}" on: release: types: [published] - # PR canary builds: adding the `build` label to a PR publishes an experimental - # build of the PR branch to npm. This lives in release.yml (rather than a - # separate workflow) so it authenticates via the npm OIDC trusted publisher — - # npm allows only ONE trusted publisher (repo + workflow file) per package, and - # that slot is release.yml. - pull_request: - types: [labeled] + # Manual canary builds: dispatch this workflow on a branch to publish an + # experimental build of that branch. It lives here (not a separate workflow) so + # it authenticates via the npm OIDC trusted publisher — npm allows only ONE + # trusted publisher (repo + workflow file) per package, and that slot is release.yml. + workflow_dispatch: + inputs: + tag: + description: npm dist-tag for the canary build + required: false + default: experimental jobs: validate-tag: - # Release-event path only. PR canaries run the `canary` job below; the other - # release jobs cascade-skip on PR events via their `needs: validate-tag`. + # Release-event path only. A manual dispatch runs the `canary` job below; the + # other release jobs cascade-skip on dispatch via their `needs: validate-tag`. if: ${{ github.event_name == 'release' }} runs-on: ubuntu-latest steps: @@ -197,22 +200,19 @@ jobs: echo "Skipping: $NEW_TAG is not newer than current latest $CURRENT_LATEST" fi - # PR canary: publish an experimental build of the PR branch, triggered by the - # `build` label. Authenticates via the SAME npm OIDC trusted publisher as the - # release jobs above (id-token + --provenance, no NPM_TOKEN). Replaces the old - # release-canary.yml, which used a classic NPM_TOKEN that was retired when the - # package moved to OIDC — so it had been failing for every PR. + # Manual canary: dispatch this workflow on a branch to publish an experimental + # build of that branch. Authenticates via the SAME npm OIDC trusted publisher as + # the release jobs above (id-token + --provenance, no token). Trigger with + # `gh workflow run release.yml --ref ` (optionally `-f tag=`). + # Replaces the old build-label release-canary.yml, whose classic NPM_TOKEN was + # retired when the package moved to OIDC. canary: - if: ${{ github.event_name == 'pull_request' && github.event.label.name == 'build' }} + if: ${{ github.event_name == 'workflow_dispatch' }} runs-on: ubuntu-latest permissions: id-token: write - contents: read - pull-requests: write steps: - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.sha }} - uses: pnpm/action-setup@v5 with: version: 10 @@ -220,38 +220,21 @@ jobs: with: node-version: '24.x' cache: "pnpm" - # A `canary:` label publishes under instead of `experimental`. - # (Trusted publishing authenticates `npm publish` only, so we set the tag at - # publish time rather than with a separate, unauthenticated `npm dist-tag add`.) - - name: Extract CANARY_TAG - run: | - export LABELS_JSON='${{ toJson(github.event.pull_request.labels) }}' - CANARY_TAG=$(node -e " - const labels = JSON.parse(process.env.LABELS_JSON || '[]'); - const canaryLabel = labels.find(label => label.name.startsWith('canary:')); - if (canaryLabel) console.log(canaryLabel.name.split(':')[1]); - ") - echo "CANARY_TAG=$CANARY_TAG" >> $GITHUB_ENV # Ensure that the README is published with the package - run: rm -f packages/cli/README.md && cp README.md packages/cli - - run: echo "PR_VERSION=0.0.0-pr.${{ github.event.pull_request.number }}.$(git rev-parse --short HEAD)" >> $GITHUB_ENV + - run: echo "CANARY_VERSION=0.0.0-canary.$(git rev-parse --short HEAD)" >> $GITHUB_ENV - run: pnpm install --frozen-lockfile - name: Set version, pack, and publish (OIDC trusted publishing) run: | - pnpm version ${{ env.PR_VERSION }} --no-git-tag-version + pnpm version ${{ env.CANARY_VERSION }} --no-git-tag-version pnpm pack - npm publish checkly-*.tgz --provenance --tag "${CANARY_TAG:-experimental}" + npm publish checkly-*.tgz --provenance --tag '${{ inputs.tag }}' working-directory: packages/cli - env: - CANARY_TAG: ${{ env.CANARY_TAG }} - PR_VERSION: ${{ env.PR_VERSION }} - - uses: marocchino/sticky-pull-request-comment@v2 - with: - header: PR Preview Release Published - hide_and_recreate: true - hide_classify: "OUTDATED" - message: | - 🎉 Experimental release successfully published [on npm](https://npmjs.com/package/checkly/v/${{ env.PR_VERSION }}) - ``` - npm install checkly@${{ env.PR_VERSION }} - ``` + - name: Publish summary + run: | + { + echo "Published \`checkly@${{ env.CANARY_VERSION }}\` (dist-tag: \`${{ inputs.tag }}\`)" + echo '```' + echo "npm install checkly@${{ env.CANARY_VERSION }}" + echo '```' + } >> "$GITHUB_STEP_SUMMARY"