From 47231a473cb001663ae5b6fc903f09bff43325b8 Mon Sep 17 00:00:00 2001 From: Mark Murray Date: Thu, 13 Aug 2026 16:58:17 +0100 Subject: [PATCH 1/2] Consolidate Web package releases --- .github/scripts/validate-release-version | 13 +- .github/workflows/ci.yml | 2 + .github/workflows/release.yml | 102 ++++++++++++++- .github/workflows/web-publish.yml | 150 ----------------------- platforms/web/RELEASING.md | 85 +++++-------- 5 files changed, 138 insertions(+), 214 deletions(-) delete mode 100644 .github/workflows/web-publish.yml diff --git a/.github/scripts/validate-release-version b/.github/scripts/validate-release-version index caff3bcd4..9ce6b4129 100755 --- a/.github/scripts/validate-release-version +++ b/.github/scripts/validate-release-version @@ -130,8 +130,19 @@ case "$PLATFORM_INPUT" in VERSION=$(json_version "$RN_PACKAGE_FILE") ;; + Web|web) + PLATFORM="web" + DISPLAY_PLATFORM="Web" + RELEASE_TITLE_PREFIX="Web" + TAG_PREFIX="web/" + PUBLISH_WORKFLOW="release.yml" + + WEB_PACKAGE_FILE="platforms/web/package.json" + VERSION=$(json_version "$WEB_PACKAGE_FILE") + ;; + *) - echo "::error::Unsupported platform '$PLATFORM_INPUT'. Expected one of: iOS, Android, Embedded Checkout Protocol, React Native." >&2 + echo "::error::Unsupported platform '$PLATFORM_INPUT'. Expected one of: iOS, Android, Embedded Checkout Protocol, React Native, Web." >&2 exit 1 ;; esac diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2dce65f9e..2adede5f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -98,6 +98,8 @@ jobs: - '.github/workflows/rn-build-android.yml' web: - '.github/workflows/web.yml' + - '.github/workflows/release.yml' + - '.github/scripts/validate-release-version' - '.github/actions/setup/**' - '.ci/changed-file-filters.yml' - '.github/workflows/ci.yml' diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1d4cecc0c..45b5c6813 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,6 +1,8 @@ name: Release package on: + release: + types: [published] workflow_dispatch: inputs: platform: @@ -12,6 +14,7 @@ on: - Android - Embedded Checkout Protocol - React Native + - Web version: description: Expected package version. Must match the checked-in version for the selected platform. required: true @@ -26,10 +29,6 @@ on: - Draft release - Production release -permissions: - contents: write - actions: write - concurrency: group: release-${{ inputs.platform }} cancel-in-progress: false @@ -37,8 +36,14 @@ concurrency: jobs: release: name: Release ${{ inputs.platform }} ${{ inputs.version }} + if: github.event_name == 'workflow_dispatch' + permissions: + contents: write + actions: write runs-on: ubuntu-latest timeout-minutes: 10 + outputs: + tag: ${{ steps.release.outputs.tag }} steps: - name: Checkout @@ -111,7 +116,11 @@ jobs: echo " Publish dispatch: skipped until the draft release is manually published" else echo " Release creation: published GitHub Release" - echo " Publish dispatch: ${PUBLISH_WORKFLOW} will be dispatched after release creation" + if [ "$DISPLAY_PLATFORM" = "Web" ]; then + echo " Publish: the Web publish job will run after release creation" + else + echo " Publish dispatch: ${PUBLISH_WORKFLOW} will be dispatched after release creation" + fi fi - name: Create GitHub Release @@ -139,7 +148,7 @@ jobs: gh release create "${args[@]}" - name: Dispatch publish workflow - if: ${{ inputs.mode == 'Production release' }} + if: ${{ inputs.mode == 'Production release' && inputs.platform != 'Web' }} env: TAG: ${{ steps.release.outputs.tag }} PUBLISH_WORKFLOW: ${{ steps.release.outputs.publish_workflow }} @@ -205,3 +214,84 @@ jobs: SUMMARY echo "::notice::GitHub CLI draft release command written to the job summary." fi + + web-publish: + name: Publish @shopify/checkout-kit to npm + needs: release + if: | + always() && ( + (github.event_name == 'workflow_dispatch' + && inputs.platform == 'Web' + && inputs.mode == 'Production release' + && needs.release.result == 'success') + || (github.event_name == 'release' + && startsWith(github.event.release.tag_name, 'web/')) + ) + permissions: + contents: read + id-token: write + environment: + name: npm-web + url: https://www.npmjs.com/package/@shopify/checkout-kit + runs-on: ubuntu-latest + timeout-minutes: 10 + defaults: + run: + working-directory: platforms/web + + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Setup Node.js, pnpm, and install dependencies + uses: ./.github/actions/setup + with: + node-version-file: platforms/web/package.json + cache-dependency-path: platforms/web/pnpm-lock.yaml + package-json-file: platforms/web/package.json + working-directory: platforms/web + ignore-scripts: "true" + + - name: Validate release version + id: release + working-directory: . + env: + EXPECTED_TAG: ${{ github.event_name == 'release' && github.event.release.tag_name || needs.release.outputs.tag }} + run: .github/scripts/validate-release-version Web "" "$EXPECTED_TAG" >> "$GITHUB_OUTPUT" + + - name: Verify version is not already published + run: | + set -euo pipefail + NAME=$(node -p "require('./package.json').name") + VERSION=$(node -p "require('./package.json').version") + URL="https://registry.npmjs.org/${NAME}/${VERSION}" + if curl -fs "$URL" > /dev/null; then + echo "::error::${NAME}@${VERSION} is already published on npm. Bump platforms/web/package.json before re-running." + exit 1 + fi + echo "::notice::${NAME}@${VERSION} is not yet on npm — safe to proceed." + + - name: Lint (typecheck + oxlint + format) + run: pnpm lint + + - name: Test + run: pnpm test + + - name: Build + run: pnpm build + + - name: Verify package (publint) + run: pnpm verify + + - name: Pack and inspect contents + run: | + pnpm pack --pack-destination /tmp/web-publish + echo "Tarball contents:" + tar -tzf /tmp/web-publish/*.tgz | sort + + - name: Publish to npm + run: pnpm dlx npm@11.5.1 publish --no-git-checks --tag "$DIST_TAG" --access public --provenance + env: + DIST_TAG: ${{ steps.release.outputs.npm_tag }} + NPM_CONFIG_PROVENANCE: "true" + NPM_TOKEN: "" + NODE_AUTH_TOKEN: "" diff --git a/.github/workflows/web-publish.yml b/.github/workflows/web-publish.yml deleted file mode 100644 index 5de03ea44..000000000 --- a/.github/workflows/web-publish.yml +++ /dev/null @@ -1,150 +0,0 @@ -name: Web — Publish to npm - -on: - release: - types: [published] - workflow_dispatch: - inputs: - tag: - description: "Override dist-tag (latest, next, beta, etc.). Leave blank to auto-infer ('next' for prereleases, 'latest' for stable versions)." - required: false - type: string - dry-run: - description: "Run the full pipeline + pack but skip the actual publish. Defaults to true for manual safety; uncheck to actually publish." - required: false - type: boolean - default: true - -permissions: - contents: read - id-token: write - -concurrency: - group: web-publish - cancel-in-progress: false - -jobs: - publish: - name: Publish @shopify/checkout-kit to npm - # Only run when either: - # - A GitHub Release tagged `web/X.Y.Z` is published (auto trigger), OR - # - The workflow is manually dispatched from the `main` branch. The - # branch lock prevents fat-fingering a publish from a feature branch - # that hasn't been reviewed. - # Web tags are `web/X.Y.Z` to disambiguate from Swift's bare semver and - # Android's `android/X.Y.Z`. - if: | - (github.event_name == 'release' && startsWith(github.event.release.tag_name, 'web/')) - || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/main') - environment: - name: npm-web - url: https://www.npmjs.com/package/@shopify/checkout-kit - runs-on: ubuntu-latest - timeout-minutes: 10 - defaults: - run: - working-directory: platforms/web - - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - # `ignore-scripts` prevents dep postinstall scripts from running in this - # privileged job — primary mitigation against credential exfiltration - # from a compromised transitive dependency. - - name: Setup Node.js, pnpm, and install dependencies - uses: ./.github/actions/setup - with: - node-version-file: platforms/web/package.json - cache-dependency-path: platforms/web/pnpm-lock.yaml - package-json-file: platforms/web/package.json - working-directory: platforms/web - ignore-scripts: "true" - - - name: Validate tag matches package.json version - if: github.event_name == 'release' - env: - TAG_NAME: ${{ github.event.release.tag_name }} - run: | - set -euo pipefail - VERSION_FROM_TAG="${TAG_NAME#web/}" - VERSION_FROM_PKG=$(node -p "require('./package.json').version") - if [ "$VERSION_FROM_TAG" != "$VERSION_FROM_PKG" ]; then - echo "::error::Tag '$TAG_NAME' implies version '$VERSION_FROM_TAG', but package.json has '$VERSION_FROM_PKG'." - echo "::error::Bump the version in a PR before tagging the release." - exit 1 - fi - echo "✓ Tag '$TAG_NAME' matches package.json version '$VERSION_FROM_PKG'." - - - name: Verify version is not already published - run: | - set -euo pipefail - NAME=$(node -p "require('./package.json').name") - VERSION=$(node -p "require('./package.json').version") - URL="https://registry.npmjs.org/${NAME}/${VERSION}" - if curl -fs "$URL" > /dev/null; then - echo "::error::${NAME}@${VERSION} is already published on npm. Bump platforms/web/package.json before re-running." - exit 1 - fi - echo "::notice::${NAME}@${VERSION} is not yet on npm — safe to proceed." - - - name: Lint (typecheck + oxlint + format) - run: pnpm lint - - - name: Test - run: pnpm test - - - name: Build - run: pnpm build - - - name: Verify package (publint) - run: pnpm verify - - - name: Pack and inspect contents - run: | - pnpm pack --pack-destination /tmp/web-publish - echo "Tarball contents:" - tar -tzf /tmp/web-publish/*.tgz | sort - - - name: Compute dist-tag - id: tag - env: - OVERRIDE: ${{ inputs.tag }} - PRERELEASE: ${{ github.event.release.prerelease }} - run: | - set -euo pipefail - VERSION=$(node -p "require('./package.json').version") - if [ -n "${OVERRIDE:-}" ]; then - TAG="$OVERRIDE" - echo "Using workflow_dispatch override dist-tag: $TAG" - elif [ "${PRERELEASE:-}" = "true" ]; then - TAG="next" - echo "GitHub Release marked as pre-release — publishing version '$VERSION' under 'next'." - elif node -e "process.exit(require('./package.json').version.includes('-') ? 0 : 1)"; then - TAG="next" - echo "Version '$VERSION' is a semver prerelease (contains '-') — publishing under 'next'." - echo " (Use the 'tag' workflow_dispatch input to override if you really want this on 'latest'.)" - else - TAG="latest" - echo "Stable version '$VERSION' — publishing under 'latest'." - fi - echo "tag=$TAG" >> "$GITHUB_OUTPUT" - - # `DIST_TAG` is passed via `env:` (not direct ${{ }} interpolation) so - # that a workflow_dispatch input like `latest$(whoami)` is treated as a - # literal string by bash rather than command substitution. - - name: Publish to npm - if: ${{ !inputs.dry-run }} - run: pnpm dlx npm@11.5.1 publish --no-git-checks --tag "$DIST_TAG" --access public --provenance - env: - DIST_TAG: ${{ steps.tag.outputs.tag }} - NPM_CONFIG_PROVENANCE: "true" - NPM_TOKEN: "" - NODE_AUTH_TOKEN: "" - - - name: Dry-run summary - if: ${{ inputs.dry-run }} - env: - DIST_TAG: ${{ steps.tag.outputs.tag }} - run: | - echo "::notice::Dry-run requested — skipped npm publish." - echo "Would have published with: --tag $DIST_TAG --access public --provenance" diff --git a/platforms/web/RELEASING.md b/platforms/web/RELEASING.md index 5aab450cf..b8089cca2 100644 --- a/platforms/web/RELEASING.md +++ b/platforms/web/RELEASING.md @@ -2,10 +2,8 @@ This guide covers how to publish a new version of the web package to npm. -The release flow mirrors the existing Android (`android-publish.yml`) and -Swift (`swift-publish.yml`) workflows: a maintainer drafts a GitHub Release -with a platform-prefixed tag, a workflow runs in a protected environment, and -the package is published to npm with SLSA provenance. +The `Release package` workflow creates a platform-prefixed GitHub Release, then +publishes the package in a protected environment with SLSA provenance. ## Day-to-day: publishing a release @@ -23,32 +21,32 @@ Use a [semver](https://semver.org/) string. Examples: - `4.0.0-rc.1` — release candidate - `4.0.0` — stable -`--no-git-tag-version` is intentional — the tag is created by the GitHub -Release UI in step 2, not by `pnpm version`. +`--no-git-tag-version` is intentional — the tag is created by the release +workflow in step 2, not by `pnpm version`. Open a PR titled like `chore(web): bump to 4.0.0-alpha.3`. Get it reviewed and merged into `main`. Wait for CI to be green on `main`. ### 2. Draft a GitHub Release -Go to : +Run the [Release package workflow](../../actions/workflows/release.yml): -- **Tag**: `web/` — e.g. `web/4.0.0-alpha.3`. Create the tag from `main`. -- **Title**: `Web ` — e.g. `Web 4.0.0-alpha.3`. -- **Notes**: click _Generate release notes_ and edit as needed. Highlight - any breaking changes at the top. -- **Set as a pre-release**: ✅ check this box for any version containing - `-alpha`, `-beta`, or `-rc`. Leave unchecked for stable releases. -- **Set as the latest release**: ✅ check for stable releases only. Don't - check for prereleases. +1. Select `Web` as the platform. +2. Enter the expected version. The workflow reads the version from + `platforms/web/package.json` and fails if the typed version does not match. +3. Select `Dry run` first to review the release plan. +4. From the dry-run summary, run the generated GitHub CLI command to create a + draft release without retyping the validated version. +5. Review the generated notes, then publish the draft release. -Click _Publish release_. +The workflow creates a `web/` tag and marks versions containing a +prerelease identifier such as `-alpha`, `-beta`, or `-rc` as prereleases. ### 3. Approve the publish -The release event triggers `.github/workflows/web-publish.yml`. The job uses -the `npm-web` environment, which requires a maintainer to approve before the -publish actually runs. +Publishing the draft triggers the Web publish job in +`.github/workflows/release.yml`. The job uses the `npm-web` environment, which +requires a maintainer to approve before the publish actually runs. You'll see a banner on the workflow run page: _Review pending deployments_. Click through and approve. @@ -81,28 +79,10 @@ run that built it. | --- | --- | --- | --- | | Stable | `web/4.0.0` | `latest` | `npm i @shopify/checkout-kit` | | Alpha / beta / rc | `web/4.0.0-alpha.3` | `next` | `npm i @shopify/checkout-kit@next` | -| Manual override | any | whatever you pass to `workflow_dispatch` | depends on tag | -The dist-tag is computed from three layered signals, in priority order: - -1. **Explicit override** — if you set the `tag` input on `workflow_dispatch`, - that value wins (`latest`, `next`, `beta`, etc.). -2. **GitHub Release's pre-release flag** — if you checked "Set as a - pre-release" in the Releases UI, the workflow uses `next`. -3. **Defensive fallback from `package.json` version** — if the version - contains a `-` (a semver prerelease identifier like `4.0.0-alpha.1`), - the workflow uses `next` regardless of the release flag. This catches: - - `workflow_dispatch` runs (where there's no release event so the - prerelease flag is empty) - - Release events where the maintainer forgot to check the - "pre-release" box for an obviously-prerelease version. - -If none of the above applies (stable version, no override, not flagged as -pre-release), the workflow publishes under `latest`. - -If you ever genuinely want to publish a `-alpha.X` version under `latest` -(rare — usually a mistake), use the `tag` `workflow_dispatch` input to -explicitly override. +The dist-tag is derived from the checked-in package version. Versions with a +prerelease identifier publish under `next`; stable versions publish under +`latest`. The `web/` tag prefix is required so the publish workflow knows the release is for the web platform. Other platforms have their own prefixes: @@ -110,23 +90,13 @@ is for the web platform. Other platforms have their own prefixes: - Web: `web/X.Y.Z` - Android: `android/X.Y.Z` - Swift: bare `X.Y.Z` -- React Native: TBD +- React Native: `react-native/X.Y.Z` ## Manual / emergency publish -You can trigger the workflow directly without creating a GitHub Release: - -1. Go to _Actions → Web — Publish to npm → Run workflow_ -2. Choose the branch (usually `main`) -3. Optionally: - - **Override dist-tag** — e.g. `latest`, `next`, `beta`, `experimental` - - **Dry run** — runs everything except the final `npm publish`. Use this - to sanity-check the pipeline before a real publish, or to verify a - misconfigured release. - -The tag-vs-package.json validation is skipped on `workflow_dispatch` runs -(since there's no tag to validate against). Make sure `package.json`'s -version is correct before running. +Use `Production release` mode in the `Release package` workflow to create the +GitHub Release and run the protected Web publish job in the same workflow. Use +`Dry run` first to validate the package version and inspect the release plan. ## One-time setup (already done — for reference) @@ -143,7 +113,7 @@ On : - **Provider**: GitHub Actions - **Owner**: `Shopify` - **Repository**: `checkout-kit` - - **Workflow filename**: `web-publish.yml` + - **Workflow filename**: `release.yml` - **Environment name**: `npm-web` This tells npm to accept publishes that present an OIDC token from this exact @@ -197,7 +167,7 @@ Check that: - The job is running on a public GitHub-hosted runner (not self-hosted without OIDC support) - The Trusted Publisher on npm is for the **same workflow file path** — - npm matches `web-publish.yml` exactly + npm matches `release.yml` exactly ## What gets published @@ -223,7 +193,8 @@ pnpm pack --dry-run ## Related -- Workflow: [`.github/workflows/web-publish.yml`](../../.github/workflows/web-publish.yml) +- Workflow: [`.github/workflows/release.yml`](../../.github/workflows/release.yml) +- Release validator: [`.github/scripts/validate-release-version`](../../.github/scripts/validate-release-version) - Pattern reference: [`.github/workflows/android-publish.yml`](../../.github/workflows/android-publish.yml), [`.github/workflows/swift-publish.yml`](../../.github/workflows/swift-publish.yml) - npm Trusted Publishers docs: From 317c39d5bf353a92c5edab98ae5c958d21f23e79 Mon Sep 17 00:00:00 2001 From: Mark Murray Date: Thu, 13 Aug 2026 17:08:24 +0100 Subject: [PATCH 2/2] Tighten Web release workflow --- .github/workflows/release.yml | 10 +- .github/workflows/web-publish-bootstrap.yml | 108 -------------------- platforms/web/RELEASING.md | 17 +-- 3 files changed, 20 insertions(+), 115 deletions(-) delete mode 100644 .github/workflows/web-publish-bootstrap.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 45b5c6813..d99c043da 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -30,7 +30,7 @@ on: - Production release concurrency: - group: release-${{ inputs.platform }} + group: ${{ (inputs.platform == 'Web' || startsWith(github.event.release.tag_name, 'web/')) && 'web-publish' || format('release-{0}', inputs.platform) }} cancel-in-progress: false jobs: @@ -66,6 +66,14 @@ jobs: echo "::error::Stable releases must be created from the main branch. Current ref: $REF" exit 1 + - name: Require main branch for Web releases + if: inputs.platform == 'Web' && inputs.mode != 'Dry run' && github.ref != 'refs/heads/main' + env: + REF: ${{ github.ref }} + run: | + echo "::error::Web releases must be created from the main branch. Current ref: $REF" + exit 1 + - name: Check tag and release do not already exist env: TAG: ${{ steps.release.outputs.tag }} diff --git a/.github/workflows/web-publish-bootstrap.yml b/.github/workflows/web-publish-bootstrap.yml deleted file mode 100644 index 7bef21156..000000000 --- a/.github/workflows/web-publish-bootstrap.yml +++ /dev/null @@ -1,108 +0,0 @@ -name: Web — Bootstrap Publish - -on: - workflow_dispatch: - inputs: - dry-run: - description: "Run the full pipeline + pack but skip the actual publish. Defaults to true; flip to false to actually publish." - required: true - type: boolean - default: true - tag: - description: "Dist-tag for this publish. 'next' for prereleases (e.g. 4.0.0-alpha.1), 'latest' only for stable versions." - required: true - type: choice - default: next - options: - - next - - latest - -permissions: - contents: read - id-token: write - -concurrency: - group: web-publish - cancel-in-progress: false - -jobs: - publish: - name: Bootstrap publish @shopify/checkout-kit to npm - # Refuse to run from anywhere but `main` - if: github.ref == 'refs/heads/main' - environment: - name: npm-web - url: https://www.npmjs.com/package/@shopify/checkout-kit - runs-on: ubuntu-latest - timeout-minutes: 10 - defaults: - run: - working-directory: platforms/web - - steps: - - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - - - name: Setup Node.js, pnpm, and install dependencies - uses: ./.github/actions/setup - with: - node-version-file: platforms/web/package.json - cache-dependency-path: platforms/web/pnpm-lock.yaml - package-json-file: platforms/web/package.json - working-directory: platforms/web - ignore-scripts: "true" - - - name: Lint (typecheck + oxlint + format) - run: pnpm lint - - - name: Test - run: pnpm test - - - name: Build - run: pnpm build - - - name: Verify package (publint) - run: pnpm verify - - - name: Pack and inspect contents - run: | - pnpm pack --pack-destination /tmp/web-bootstrap - echo "Tarball contents:" - tar -tzf /tmp/web-bootstrap/*.tgz | sort - - - name: Print publish plan - env: - DIST_TAG: ${{ inputs.tag }} - DRY_RUN: ${{ inputs.dry-run }} - run: | - VERSION=$(node -p "require('./package.json').version") - echo "::notice::Plan: publish @shopify/checkout-kit@${VERSION} with --tag ${DIST_TAG} (dry-run=${DRY_RUN})" - - # Fail fast with a clear error if the NPM_TOKEN secret is missing or - # empty. Nothing about the token value is logged — only its presence - # as a boolean. - - name: Verify NPM_TOKEN is present - if: ${{ !inputs.dry-run }} - env: - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - run: | - if [ -z "${NPM_TOKEN}" ]; then - echo "::error::NPM_TOKEN secret is missing or empty. Add it in repo Settings → Secrets and variables → Actions before re-running." - exit 1 - fi - echo "::notice::NPM_TOKEN is present." - - - name: Publish to npm - if: ${{ !inputs.dry-run }} - run: pnpm publish --no-git-checks --ignore-scripts --tag "$DIST_TAG" --access public - env: - DIST_TAG: ${{ inputs.tag }} - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} - - - name: Dry-run summary - if: ${{ inputs.dry-run }} - env: - DIST_TAG: ${{ inputs.tag }} - run: | - echo "::notice::Dry-run requested — skipped npm publish." - echo "Would have published with: --tag $DIST_TAG --access public" diff --git a/platforms/web/RELEASING.md b/platforms/web/RELEASING.md index b8089cca2..e9e8643f5 100644 --- a/platforms/web/RELEASING.md +++ b/platforms/web/RELEASING.md @@ -98,11 +98,15 @@ Use `Production release` mode in the `Release package` workflow to create the GitHub Release and run the protected Web publish job in the same workflow. Use `Dry run` first to validate the package version and inspect the release plan. -## One-time setup (already done — for reference) +## Required npm configuration -These are the one-time admin tasks required to enable Trusted Publishing. -Documented here so this guide remains complete if the configuration ever -needs to be re-created. +> [!IMPORTANT] +> Before the first Web release using `.github/workflows/release.yml`, update +> the npm Trusted Publisher to use `release.yml`. npm binds trusted publishing +> to the exact workflow filename; leaving it configured for the removed +> `web-publish.yml` workflow will cause publishing to fail. + +These are the admin settings required to enable Trusted Publishing. ### npm Trusted Publisher @@ -117,7 +121,8 @@ On : - **Environment name**: `npm-web` This tells npm to accept publishes that present an OIDC token from this exact -workflow file in this environment. No long-lived `NPM_TOKEN` is needed. +workflow file in this environment. No long-lived `NPM_TOKEN` is needed. Verify +this setting after merging any workflow rename and before publishing. ### GitHub environment @@ -125,7 +130,7 @@ In the repo's _Settings → Environments → New environment_: - **Name**: `npm-web` - **Required reviewers**: 1+ maintainers from the package owners list -- **Deployment branches**: restrict to `main` +- **Deployment branches and tags**: allow `main` and tags matching `web/*` The required-reviewer rule means every publish requires explicit human approval, even if the workflow somehow ran without authorization.