From 907fff4b230b894bd66431f11e66c2025f0afe18 Mon Sep 17 00:00:00 2001 From: Gary Chisholm Date: Wed, 29 Apr 2026 12:29:26 +1000 Subject: [PATCH] fix(publish): release process --- .github/workflows/publish.yml | 47 +++++++++------------- package.json | 1 + scripts/release.sh | 76 +++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+), 28 deletions(-) create mode 100755 scripts/release.sh diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index e42c001..24abb94 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -14,8 +14,6 @@ jobs: steps: - uses: actions/checkout@v4 - with: - fetch-depth: 0 - uses: actions/setup-node@v4 with: @@ -37,37 +35,30 @@ jobs: - name: Build run: npm run build - - name: Resolve version bump - id: bump + - name: Check if version is already published + id: check run: | - LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") - if [ -z "$LAST_TAG" ]; then - RANGE="HEAD" + VERSION=$(node -p "require('./package.json').version") + if git tag -l "v${VERSION}" | grep -q .; then + echo "published=true" >> "$GITHUB_OUTPUT" else - RANGE="${LAST_TAG}..HEAD" + echo "published=false" >> "$GITHUB_OUTPUT" fi + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - BUMP="" - while IFS= read -r subject; do - [ -z "$subject" ] && continue - if echo "$subject" | grep -qE '^[a-z]+(\(.+\))?!:'; then - BUMP="major"; break - fi - if [ "$BUMP" != "minor" ] && echo "$subject" | grep -qE '^feat(\(.+\))?:'; then - BUMP="minor" - fi - if [ -z "$BUMP" ] && echo "$subject" | grep -qE '^fix(\(.+\))?:'; then - BUMP="patch" - fi - done < <(git log "$RANGE" --format="%s" --no-merges) - - echo "type=$BUMP" >> "$GITHUB_OUTPUT" - - - name: Publish - if: steps.bump.outputs.type + - name: Tag and publish + if: steps.check.outputs.published == 'false' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - npm version "${{ steps.bump.outputs.type }}" -m "chore(release): %s" - git push --follow-tags + git tag "v${{ steps.check.outputs.version }}" + git push origin "v${{ steps.check.outputs.version }}" npm publish --provenance --access public + + - name: Create GitHub release + if: steps.check.outputs.published == 'false' + env: + GH_TOKEN: ${{ github.token }} + run: gh release create "v${{ steps.check.outputs.version }}" --generate-notes diff --git a/package.json b/package.json index 086c28b..59f27c7 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "lint:fix": "biome check --fix .", "check-types": "tsc --noEmit", "bench": "vitest bench --run", + "release": "./scripts/release.sh", "prepare": "lefthook install" }, "engines": { diff --git a/scripts/release.sh b/scripts/release.sh new file mode 100755 index 0000000..84b5e2f --- /dev/null +++ b/scripts/release.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash +set -euo pipefail + +OVERRIDE="${1:-}" + +if [[ -n "$OVERRIDE" && ! "$OVERRIDE" =~ ^(patch|minor|major)$ ]]; then + echo "Usage: npm run release [-- patch|minor|major]" + echo " Omit argument to auto-detect from conventional commits." + exit 1 +fi + +if [[ -n "$(git status --porcelain)" ]]; then + echo "Working directory is not clean. Commit or stash changes first." + exit 1 +fi + +CURRENT_BRANCH=$(git branch --show-current) +if [[ "$CURRENT_BRANCH" != "master" ]]; then + echo "Switch to master before running release." + exit 1 +fi + +git pull --ff-only origin master + +if [[ -n "$OVERRIDE" ]]; then + BUMP="$OVERRIDE" +else + LAST_TAG=$(git describe --tags --abbrev=0 2>/dev/null || echo "") + if [[ -n "$LAST_TAG" ]]; then + RANGE="${LAST_TAG}..HEAD" + else + RANGE="HEAD" + fi + + BUMP="" + while IFS= read -r subject; do + [[ -z "$subject" ]] && continue + if [[ "$subject" =~ ^[a-z]+(\(.+\))?!: ]]; then + BUMP="major"; break + fi + if [[ "$BUMP" != "minor" && "$subject" =~ ^feat(\(.+\))?: ]]; then + BUMP="minor" + fi + if [[ -z "$BUMP" && "$subject" =~ ^fix(\(.+\))?: ]]; then + BUMP="patch" + fi + done < <(git log "$RANGE" --format="%s" --no-merges) + + if [[ -z "$BUMP" ]]; then + echo "No releasable commits found since ${LAST_TAG:-the beginning}." + echo "Use 'npm run release -- patch|minor|major' to override." + exit 0 + fi + + echo "Auto-detected bump: $BUMP" +fi + +npm version "$BUMP" --no-git-tag-version +VERSION=$(node -p "require('./package.json').version") +BRANCH="release/v${VERSION}" + +git checkout -b "$BRANCH" +git add package.json package-lock.json +git commit -m "chore(release): v${VERSION}" +git push -u origin "$BRANCH" + +gh pr create \ + --base master \ + --head "$BRANCH" \ + --title "chore(release): v${VERSION}" \ + --body "Bumps version to \`${VERSION}\` (${BUMP}). + +Merging will tag and publish to npm automatically." + +echo "" +echo "PR created. Merge it to publish v${VERSION} to npm."