From 21f3231a3ad9c5127c6465496ab7b2ab4a959253 Mon Sep 17 00:00:00 2001 From: Elliot Taylor Date: Mon, 13 Jul 2026 18:23:09 +0200 Subject: [PATCH] Derive release version from the tag instead of pushing to main The one-click Release workflow pushed the npm-version bump commit straight to main, which branch protection rejects (PRs + signed commits + status checks required). Drop that approach. Instead, make Publish read the version from the release tag (v0.3.3 -> 0.3.3), write it into package.json in CI only, then build and publish. Nothing is committed to main, so it works with branch protection and the tag can never disagree with the published version. Removes the old tag/version match guard (now unnecessary) and the release.yml workflow. Co-Authored-By: Claude Opus 4.8 --- .github/workflows/publish.yml | 11 +++-- .github/workflows/release.yml | 90 ----------------------------------- RELEASING.md | 42 ++++++++-------- 3 files changed, 30 insertions(+), 113 deletions(-) delete mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 7766931..6fbd671 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -39,6 +39,13 @@ jobs: - name: Install dependencies run: npm ci + - name: Set version from release tag + if: github.event_name == 'release' + run: | + version="${GITHUB_REF_NAME#v}" + echo "Publishing version $version (from tag $GITHUB_REF_NAME)" + npm version "$version" --no-git-tag-version --allow-same-version + - name: Typecheck run: npm run typecheck @@ -51,10 +58,6 @@ jobs: - name: Verify package contents run: npm pack --dry-run - - name: Verify release tag matches package version - if: github.event_name == 'release' - run: node -e "const pkg = require('./package.json'); const expected = 'v' + pkg.version; if (process.env.GITHUB_REF_NAME !== expected) { console.error('Release tag ' + process.env.GITHUB_REF_NAME + ' does not match package version ' + expected); process.exit(1); }" - - name: Dry-run publish if: github.event_name == 'workflow_dispatch' run: npm publish --access public --provenance --dry-run diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml deleted file mode 100644 index e7f995a..0000000 --- a/.github/workflows/release.yml +++ /dev/null @@ -1,90 +0,0 @@ -name: Release - -# One-click release: bumps package.json, tags, publishes to npm, and cuts the -# GitHub release from a single source of truth. Because the tag is derived from -# `npm version`, the tag can never disagree with package.json. - -on: - workflow_dispatch: - inputs: - bump: - description: "Semver bump (ignored if 'version' is set)" - type: choice - options: - - patch - - minor - - major - default: patch - version: - description: "Explicit version, e.g. 0.3.3 (overrides bump). Leave blank to use bump." - type: string - required: false - -permissions: - contents: write - id-token: write - -concurrency: - group: release - cancel-in-progress: false - -jobs: - release: - name: Release ${{ inputs.version || inputs.bump }} - runs-on: ubuntu-latest - environment: npm - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: 24.x - registry-url: https://registry.npmjs.org - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Typecheck - run: npm run typecheck - - - name: Test - run: npm test - - - name: Build - run: npm run build - - - name: Verify package contents - run: npm pack --dry-run - - - name: Configure git - run: | - git config user.name "github-actions[bot]" - git config user.email "41898282+github-actions[bot]@users.noreply.github.com" - - - name: Bump version and tag - id: bump - run: | - if [ -n "${{ inputs.version }}" ]; then - NEW=$(npm version "${{ inputs.version }}" -m "Release v%s") - else - NEW=$(npm version "${{ inputs.bump }}" -m "Release v%s") - fi - echo "tag=$NEW" >> "$GITHUB_OUTPUT" - echo "Bumped to $NEW" - - - name: Push commit and tag - run: git push origin HEAD --follow-tags - - - name: Publish to npm - run: npm publish --access public --provenance - - - name: Create GitHub release - env: - GH_TOKEN: ${{ github.token }} - run: gh release create "${{ steps.bump.outputs.tag }}" --generate-notes --title "${{ steps.bump.outputs.tag }}" diff --git a/RELEASING.md b/RELEASING.md index 313c388..eca955e 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -1,28 +1,32 @@ # Releasing -The version in `package.json` is the single source of truth. The published npm -version, the git tag, and the GitHub release must all match it — the `Publish` -workflow enforces this and will fail the release if they drift. +The **git tag is the single source of truth** for a release. The `Publish` +workflow reads the version from the tag (`v0.3.3` → `0.3.3`), writes it into +`package.json` in CI, then builds and publishes. Nothing is committed back to +`main`, so this works with branch protection and the tag can never disagree +with the published version. -## Recommended: one-click release +`package.json`'s committed `version` is just a placeholder — it does not need to +be bumped before a release. -Run the **Release** workflow (Actions → Release → Run workflow) on the branch you -want to release from (normally `main`): +## How to release -- **bump**: `patch` / `minor` / `major`, or -- **version**: an explicit version like `0.3.3` (overrides `bump`). +Create a GitHub release with a new tag: -The workflow bumps `package.json`, commits, tags, pushes, publishes to npm, and -cuts the GitHub release — all from the same version, so the tag can never -disagree with `package.json`. +```bash +gh release create v0.3.3 --generate-notes --title "v0.3.3" +``` -## Manual release (fallback) +or use the GitHub UI (Releases → Draft a new release → new tag `v0.3.3`). -If you create a release/tag by hand in the GitHub UI, you **must** bump -`package.json` to the matching version first and merge that commit into the -branch you tag. Cutting `v0.3.3` while `package.json` still says `0.2.11` is -exactly what the guard rejects. +Publishing the release triggers the `Publish` workflow, which validates +(typecheck, test, build, `npm pack`) and publishes to npm with provenance using +the tag's version. -1. `npm version 0.3.3` on the release branch (bumps + commits + tags). -2. `git push origin HEAD --follow-tags`. -3. Create the GitHub release from tag `v0.3.3` → the `Publish` workflow runs. +## Notes + +- Tags must be `vX.Y.Z` (the leading `v` is stripped to get the npm version). +- Pick a version higher than the current `latest` on npm (`npm view + @patchstack/connect version`); npm rejects re-publishing an existing version. +- Run the `Publish` workflow via **workflow_dispatch** for a dry-run publish + without cutting a release.