From 5eda784726ac2e335b6a90d04f5fee7f959c01c6 Mon Sep 17 00:00:00 2001 From: Corey Martin Date: Thu, 26 Feb 2026 11:06:52 -0800 Subject: [PATCH] Add release workflow and switch publishing to public npm - release.yaml: workflow_dispatch creates a version bump PR with changelog, limited to patch/minor (no major bumps) - publish.yml: replaced GitHub Packages publishing with public npm, triggers on release/ branch PR merge, creates git tag after publish Co-Authored-By: Claude Opus 4.6 --- .github/workflows/publish.yml | 49 +++++--- .github/workflows/release.yaml | 205 +++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 16 deletions(-) create mode 100644 .github/workflows/release.yaml diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index a497c4b..c5aee95 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,27 +1,44 @@ -name: Publish Package +name: "Publish to npm" on: - release: - types: [published] + pull_request: + types: [closed] + branches: [main] jobs: publish: + if: github.event.pull_request.merged == true && startsWith(github.head_ref, 'release/') runs-on: ubuntu-latest permissions: - contents: read - packages: write + contents: write + id-token: write steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 with: - node-version: 20 - cache: 'npm' - registry-url: https://npm.pkg.github.com - - run: npm ci --legacy-peer-deps + node-version: 24 + cache: "npm" + registry-url: https://registry.npmjs.org + + - name: Install dependencies + run: npm ci --legacy-peer-deps env: CENTRAL_LICENSE_KEY: ${{ secrets.CENTRAL_LICENSE_KEY }} - - run: npm run test:unit - - run: npm run lint:publish - - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Run tests + run: npm run test:unit + + - name: Run publish checks + run: npm run lint:publish + + - name: Publish to npm + run: npm publish --provenance --access public + + - name: Create git tag + run: | + VERSION=$(node -p "require('./package.json').version") + git tag "v${VERSION}" + git push origin "v${VERSION}" diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..4e7183b --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,205 @@ +name: "Create Release PR" + +on: + workflow_dispatch: + +jobs: + create-release-pr: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: "npm" + + - name: Configure git + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + + - name: Collect changes since last release + id: changes + run: | + OLD_VERSION=$(node -p "require('./package.json').version") + echo "old_version=$OLD_VERSION" >> "$GITHUB_OUTPUT" + + # Collect PR merge commit messages since last tag + MERGES=$(git log v${OLD_VERSION}..HEAD --merges --pretty=format:"%s" || true) + + # Fall back to non-merge commits if no merges + if [ -z "$MERGES" ]; then + MERGES=$(git log v${OLD_VERSION}..HEAD --pretty=format:"%s" --no-merges | head -30) + fi + + # Also collect the diff stat for context + DIFFSTAT=$(git diff v${OLD_VERSION}..HEAD --stat | tail -1) + + # Write to files for the next step + echo "$MERGES" > /tmp/commit_messages.txt + echo "$DIFFSTAT" > /tmp/diffstat.txt + + - name: Determine version bump and generate changelog + id: ai + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + run: | + OLD_VERSION="${{ steps.changes.outputs.old_version }}" + COMMITS=$(cat /tmp/commit_messages.txt) + DIFFSTAT=$(cat /tmp/diffstat.txt) + DATE=$(date +%Y-%m-%d) + + # Build the prompt + cat > /tmp/prompt.json << 'PROMPT_EOF' + { + "model": "claude-sonnet-4-5-20250514", + "max_tokens": 1024, + "messages": [ + { + "role": "user", + "content": "COMMITS_PLACEHOLDER" + } + ] + } + PROMPT_EOF + + # Inject the actual prompt content with proper escaping + PROMPT_TEXT=$(cat <> "$GITHUB_OUTPUT" + + # Write changelog to file (multi-line safe) + echo "$CHANGELOG" > /tmp/changelog_entry.md + + echo "--- AI determined: $VERSION_BUMP ---" + echo "$CHANGELOG" + + - name: Bump version + id: bump + run: | + npm version ${{ steps.ai.outputs.version_bump }} --no-git-tag-version + NEW_VERSION=$(node -p "require('./package.json').version") + echo "new_version=$NEW_VERSION" >> "$GITHUB_OUTPUT" + + - name: Update changelog + run: | + NEW_VERSION="${{ steps.bump.outputs.new_version }}" + + # Replace the AI's placeholder version with the actual bumped version + sed -i "s/→ [0-9]*\.[0-9]*\.[0-9]*/→ ${NEW_VERSION}/" /tmp/changelog_entry.md + + if [ -f CHANGELOG.md ]; then + # Insert after the title line + { + head -1 CHANGELOG.md + echo "" + cat /tmp/changelog_entry.md + echo "" + tail -n +2 CHANGELOG.md + } > /tmp/CHANGELOG.md + mv /tmp/CHANGELOG.md CHANGELOG.md + else + { + echo "# Changelog" + echo "" + cat /tmp/changelog_entry.md + } > CHANGELOG.md + fi + + - name: Create release branch and PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + OLD_VERSION="${{ steps.changes.outputs.old_version }}" + NEW_VERSION="${{ steps.bump.outputs.new_version }}" + VERSION_BUMP="${{ steps.ai.outputs.version_bump }}" + BRANCH="release/v${NEW_VERSION}" + + git checkout -b "$BRANCH" + git add package.json package-lock.json CHANGELOG.md + git commit -m "v${NEW_VERSION}" + git push -u origin "$BRANCH" + + CHANGELOG_ENTRY=$(cat /tmp/changelog_entry.md) + + gh pr create \ + --title "v${NEW_VERSION}" \ + --body "$(cat <