From cebe945ca024260375a4379692462c532d99ea60 Mon Sep 17 00:00:00 2001 From: stromek Date: Fri, 14 Aug 2026 22:38:15 +0200 Subject: [PATCH] Publish from CI on a version tag Releases depended on whoever had a working npm login locally, which is also how 0.3.0 ended up tagged but never published. Pushing a version tag now runs lint, format, tests and build, checks the tag against package.json, and publishes with provenance. publishConfig.access=public is set so a plain 'npm publish' cannot fall back to a restricted publish -- scoped packages default to restricted. --- .github/workflows/publish.yml | 56 +++++++++++++++++++++++++++++++++++ package.json | 3 ++ 2 files changed, 59 insertions(+) create mode 100644 .github/workflows/publish.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..0ba170a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,56 @@ +name: Publish + +# Publishing runs here rather than from a laptop, so a release never depends on who +# happens to hold a working `npm login`. Tags in this repo are bare versions (0.3.1), +# not v-prefixed. +on: + push: + tags: ['[0-9]*.[0-9]*.[0-9]*'] + workflow_dispatch: + +permissions: + contents: read + # Lets npm attach a provenance attestation, so consumers can verify the tarball was + # built from this commit by this workflow. + id-token: write + +jobs: + publish: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - uses: actions/checkout@v7 + + - uses: actions/setup-node@v6 + with: + node-version: 22 + registry-url: https://registry.npmjs.org + cache: npm + + - run: npm ci + + # The tag decides what gets published, so a tag that disagrees with package.json + # would ship a version nobody asked for. This is exactly how 0.3.0 went wrong: + # the tag existed while package.json still said 0.2.0. + - name: Verify tag matches package.json + if: github.ref_type == 'tag' + run: | + tag="${GITHUB_REF_NAME}" + packageVersion="$(node -p "require('./package.json').version")" + if [ "$tag" != "$packageVersion" ]; then + echo "::error::Tag ${tag} does not match package.json version ${packageVersion}" + exit 1 + fi + + - run: npm run lint + - run: npm run format:check + - run: npm test + - run: npm run build + + # --access public is required for a scoped package; without it npm defaults to + # restricted and rejects the publish. + - name: Publish to npm + run: npm publish --access public --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/package.json b/package.json index 705f525..57e5649 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "LICENSE" ], "sideEffects": false, + "publishConfig": { + "access": "public" + }, "scripts": { "build": "tsup", "dev": "tsup --watch",