From 5803a9481e7a1ad563492a695deb6ffbf862b55d Mon Sep 17 00:00:00 2001 From: Shahzaib Sheikh Date: Thu, 27 Aug 2026 18:01:59 +0800 Subject: [PATCH 1/2] ci: publish to npm via OIDC trusted publishing Drops the NPM_TOKEN dependency from the @bluefin-exchange/pro-sdk publish path in favour of npm trusted publishing (OIDC). Node moves 20 -> 22 (trusted publishing needs >= 22.14.0) and npm is upgraded in-job, since the OIDC flow needs npm >= 11.5.1. Adds a workflow_dispatch path to retry a failed publish without cutting another tag, guarded so it only publishes a commit reachable from origin/main. The ts-release- tag must match ts/sdk/package.json's version, and a version already on npm is refused up front, since there is no token to fall back on and npm versions are immutable. This repo is public, so npm attaches a provenance attestation automatically under trusted publishing; the package.json repository field already matches. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/npm_publish.yaml | 80 ++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 5 deletions(-) diff --git a/.github/workflows/npm_publish.yaml b/.github/workflows/npm_publish.yaml index e1107a18..9c13fe43 100644 --- a/.github/workflows/npm_publish.yaml +++ b/.github/workflows/npm_publish.yaml @@ -1,9 +1,30 @@ name: NPM Release +# @bluefin-exchange/pro-sdk's npm trusted publisher is registered against this exact +# filename — renaming this file breaks publishing until npm is updated to match. +# npm allows one publisher per package, so every publish path lives in this file. on: push: tags: - ts-release-* + workflow_dispatch: + inputs: + dist_tag: + description: "npm dist-tag to publish under" + required: true + default: pre-release + type: choice + options: + - pre-release + - latest + +permissions: + id-token: write + contents: read + +concurrency: + group: npm-publish + cancel-in-progress: false jobs: publish-ts: @@ -12,14 +33,63 @@ jobs: run: working-directory: ts/sdk steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: "24" + # Trusted publishing requires Node >= 22.14.0. + node-version: "22" registry-url: "https://registry.npmjs.org" + - name: Strip _authToken so OIDC kicks in + # setup-node's registry-url always writes + # `//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}` and exports a dummy + # NODE_AUTH_TOKEN. npm would treat auth as configured and never do the OIDC + # exchange, failing with ENEEDAUTH. + run: | + npmrc="${NPM_CONFIG_USERCONFIG:-$HOME/.npmrc}" + sed -i '/_authToken/d' "$npmrc" + + - name: Upgrade npm for OIDC support + # Trusted publishing needs npm >= 11.5.1. + run: npm install -g npm@latest + + - name: Verify dispatched commit is on main + if: github.event_name == 'workflow_dispatch' + run: | + COMMIT_SHA=$(git rev-parse HEAD) + if ! git branch -r --contains "$COMMIT_SHA" | grep -q "origin/main"; then + echo "::error::Dispatched commit $COMMIT_SHA is not reachable from origin/main. Publish only merged code." + exit 1 + fi + + - name: Resolve dist-tag and guard version + run: | + PKG_NAME="$(node -p "require('./package.json').name")" + PKG_VERSION="$(node -p "require('./package.json').version")" + + if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then + DIST_TAG="${{ inputs.dist_tag }}" + else + DIST_TAG=latest + TAG_VERSION="${GITHUB_REF_NAME#ts-release-}" + if [[ "$TAG_VERSION" != "$PKG_VERSION" ]]; then + echo "::error::Tag version '$TAG_VERSION' != ts/sdk/package.json version '$PKG_VERSION' — the tag must be ts-release-." + exit 1 + fi + fi + + if npm view "$PKG_NAME@$PKG_VERSION" version >/dev/null 2>&1; then + echo "::error::$PKG_NAME@$PKG_VERSION is already published — bump the version (npm versions are immutable)." + exit 1 + fi + + echo "DIST_TAG=$DIST_TAG" >> "$GITHUB_ENV" + echo "Publishing $PKG_NAME@$PKG_VERSION (dist-tag: $DIST_TAG)" + - name: Install dependencies run: yarn install --immutable @@ -27,6 +97,6 @@ jobs: run: yarn build - name: Publish to NPM - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + # Authenticated by OIDC trusted publishing — no NPM_TOKEN. This repo is public, + # so npm attaches a provenance attestation automatically. + run: npm publish --tag "$DIST_TAG" From 148a6dda194f2b01d687b720ebf5901206b2fd9e Mon Sep 17 00:00:00 2001 From: Shahzaib Sheikh Date: Thu, 27 Aug 2026 18:08:10 +0800 Subject: [PATCH 2/2] ci: fold the pre-release publish into the single trusted-publisher workflow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit npm_publish_prerelease.yaml published the same package from a second workflow file. npm allows one trusted publisher per package, bound to a single workflow filename, so only one of the two could ever have authorised — the other would have failed with ENEEDAUTH once the token was gone. Folds its `ts-pre-release-*` tag trigger into npm_publish.yaml and deletes it. The pre-release prefix is matched before the release prefix, since both begin with "ts-". Node is 24 rather than 22: the pre-release path already built this package on 24, so it is the better-evidenced choice for the release path too. Co-Authored-By: Claude Opus 5 (1M context) --- .github/workflows/npm_publish.yaml | 20 +++++++---- .github/workflows/npm_publish_prerelease.yaml | 34 ------------------- 2 files changed, 14 insertions(+), 40 deletions(-) delete mode 100644 .github/workflows/npm_publish_prerelease.yaml diff --git a/.github/workflows/npm_publish.yaml b/.github/workflows/npm_publish.yaml index 9c13fe43..8af3f145 100644 --- a/.github/workflows/npm_publish.yaml +++ b/.github/workflows/npm_publish.yaml @@ -7,6 +7,7 @@ on: push: tags: - ts-release-* + - ts-pre-release-* workflow_dispatch: inputs: dist_tag: @@ -33,7 +34,7 @@ jobs: run: working-directory: ts/sdk steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 with: fetch-depth: 0 @@ -41,7 +42,7 @@ jobs: uses: actions/setup-node@v4 with: # Trusted publishing requires Node >= 22.14.0. - node-version: "22" + node-version: "24" registry-url: "https://registry.npmjs.org" - name: Strip _authToken so OIDC kicks in @@ -54,7 +55,7 @@ jobs: sed -i '/_authToken/d' "$npmrc" - name: Upgrade npm for OIDC support - # Trusted publishing needs npm >= 11.5.1. + # Trusted publishing needs npm >= 11.5.1, newer than what Node 24 bundles. run: npm install -g npm@latest - name: Verify dispatched commit is on main @@ -74,10 +75,17 @@ jobs: if [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then DIST_TAG="${{ inputs.dist_tag }}" else - DIST_TAG=latest - TAG_VERSION="${GITHUB_REF_NAME#ts-release-}" + # Check the pre-release prefix first; it also starts with "ts-". + if [[ "$GITHUB_REF_NAME" == ts-pre-release-* ]]; then + DIST_TAG=pre-release + TAG_VERSION="${GITHUB_REF_NAME#ts-pre-release-}" + else + DIST_TAG=latest + TAG_VERSION="${GITHUB_REF_NAME#ts-release-}" + fi + if [[ "$TAG_VERSION" != "$PKG_VERSION" ]]; then - echo "::error::Tag version '$TAG_VERSION' != ts/sdk/package.json version '$PKG_VERSION' — the tag must be ts-release-." + echo "::error::Tag version '$TAG_VERSION' != ts/sdk/package.json version '$PKG_VERSION' — the tag must end in the package.json version." exit 1 fi fi diff --git a/.github/workflows/npm_publish_prerelease.yaml b/.github/workflows/npm_publish_prerelease.yaml deleted file mode 100644 index f66e386e..00000000 --- a/.github/workflows/npm_publish_prerelease.yaml +++ /dev/null @@ -1,34 +0,0 @@ -name: NPM Pre Release - -on: - push: - tags: - - ts-pre-release-* - -jobs: - publish-ts: - permissions: - contents: read - runs-on: ubuntu-latest - defaults: - run: - working-directory: ts/sdk - steps: - - uses: actions/checkout@v6 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: "24" - registry-url: "https://registry.npmjs.org" - - - name: Install dependencies - run: yarn install --immutable - - - name: Build - run: yarn build - - - name: Publish to NPM - run: npm publish --tag pre-release - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}