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.