diff --git a/.github/scripts/dry-run-oidc-publish.sh b/.github/scripts/dry-run-oidc-publish.sh new file mode 100755 index 0000000..8066cf9 --- /dev/null +++ b/.github/scripts/dry-run-oidc-publish.sh @@ -0,0 +1,54 @@ +#!/usr/bin/env bash +# +# OIDC dry-run — exercise npm publish auth without uploading to the registry. +# For throwaway branch testing only. Do not merge to master. +# +# Uses a unique prerelease version so npm does not reject the dry-run when the +# current release version (e.g. 7.4.0) already exists on the registry. +# +# Expects actions/setup-node to have configured the npm registry (.npmrc). +# setup-node may set NODE_AUTH_TOKEN for OIDC — do not override it with NPM_TOKEN. + +set -euo pipefail + +BASE_VERSION=$(node -p "require('./lerna.json').version") +DRY_RUN_VERSION="${BASE_VERSION}-oidc-dry-run.${GITHUB_RUN_NUMBER:?GITHUB_RUN_NUMBER is required}" + +echo "OIDC dry-run version: ${DRY_RUN_VERSION} (not committed, not published)" + +for dir in packages/*; do + node -e " + const fs = require('fs'); + const path = './${dir#./}/package.json'; + const pkg = JSON.parse(fs.readFileSync(path, 'utf8')); + pkg.version = '${DRY_RUN_VERSION}'; + if (pkg.dependencies?.['@smartlyio/yavl']) { + pkg.dependencies['@smartlyio/yavl'] = '${DRY_RUN_VERSION}'; + } + fs.writeFileSync(path, JSON.stringify(pkg, null, 2) + '\n'); + " +done + +FAILED=0 +for dir in packages/*; do + NAME=$(node -p "require('./$dir/package.json').name") + VERSION=$(node -p "require('./$dir/package.json').version") + + echo "Dry-run publish ${NAME}@${VERSION} (tag: oidc-dry-run)..." + if npm publish "./$dir" --access public --tag oidc-dry-run --dry-run --userconfig "$GITHUB_WORKSPACE/.npmrc"; then + echo "Dry-run passed: ${NAME}@${VERSION}" + else + echo "::error::Dry-run failed for ${NAME}@${VERSION}" + FAILED=1 + fi +done + +if [ "$FAILED" -eq 1 ]; then + echo "::error::One or more dry-runs failed." + echo "If the error mentions trusted publishing, workflow filename, or 401/403/404 auth, check OIDC config on npmjs.com." + echo "If the error mentions an existing version, re-run the workflow (run number changes the prerelease suffix)." + exit 1 +fi + +echo "" +echo "OIDC dry-run passed for all packages. Nothing was published to npm." diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index fc1aa97..3d353ac 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -27,6 +27,12 @@ name: Publish on: push: branches: [master] + workflow_dispatch: + inputs: + oidc_dry_run: + description: OIDC dry-run only (no npm upload, throwaway branch testing) + type: boolean + default: true # Prevent concurrent runs. If multiple PRs are merged in quick succession, # each triggered run will queue and execute in order, ensuring version bumps @@ -35,6 +41,7 @@ concurrency: ${{ github.workflow }}-${{ github.ref }} jobs: publish: + if: github.event_name == 'push' runs-on: ubuntu-latest # contents:write is needed for the default GITHUB_TOKEN (used by gh CLI). @@ -141,3 +148,35 @@ jobs: env: GITHUB_WORKSPACE: ${{ github.workspace }} run: ./.github/scripts/publish-packages.sh + + # Throwaway branch only — do not merge to master. + # Reuses publish.yml so existing npm trusted publisher config applies. + oidc-dry-run: + if: github.event_name == 'workflow_dispatch' && inputs.oidc_dry_run + runs-on: ubuntu-latest + permissions: + contents: read + id-token: write + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: 24.x + registry-url: https://registry.npmjs.org + + - name: Install yarn + run: npm install -g yarn + + - name: Install dependencies + run: yarn install --frozen-lockfile + + - name: Build + run: yarn build + + - name: OIDC dry-run publish + env: + GITHUB_WORKSPACE: ${{ github.workspace }} + GITHUB_RUN_NUMBER: ${{ github.run_number }} + run: ./.github/scripts/dry-run-oidc-publish.sh