Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,3 @@ jobs:

- name: Build and Test
run: npm run build

- name: Publish to NPM
if: startsWith(github.ref, 'refs/tags/v')
run: npm publish
126 changes: 29 additions & 97 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,124 +3,55 @@ name: Release
on:
push:
branches: [ main ]
pull_request:
types: [opened, synchronize, closed]

permissions:
contents: write
pull-requests: write
packages: write
id-token: write

jobs:
create-version-pr:
release:
runs-on: ubuntu-latest
# Skip if this is a version bump commit
if: |
github.event_name == 'push' &&
github.ref == 'refs/heads/main' &&
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, 'chore: bump version')
!contains(github.event.head_commit.message, 'chore: bump version') &&
!contains(github.event.head_commit.message, '[skip release]')
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- uses: actions/setup-node@v4
with:
node-version: '24'
cache: 'npm'
registry-url: 'https://registry.npmjs.org'

- name: Install dependencies
run: npm ci

- name: Bump version
- name: Get current version and bump
id: version
run: |
npm version patch --no-git-tag-version
VERSION=$(node -p "require('./package.json').version")
echo "version=$VERSION" >> $GITHUB_OUTPUT
CURRENT_VERSION=$(node -p "require('./package.json').version")
NEW_VERSION=$(node -e "
const v = '$CURRENT_VERSION'.split('.');
v[2] = parseInt(v[2]) + 1;
console.log(v.join('.'));
")
echo "version=$NEW_VERSION" >> $GITHUB_OUTPUT
echo "Current: $CURRENT_VERSION → New: $NEW_VERSION"

- name: Create version PR
uses: peter-evans/create-pull-request@v6
with:
token: ${{ secrets.GITHUB_TOKEN }}
branch: chore/version-bump-${{ steps.version.outputs.version }}
title: "chore: bump version to ${{ steps.version.outputs.version }}"
body: |
Automated version bump to **${{ steps.version.outputs.version }}**

This PR will be automatically merged to trigger the release process.
commit-message: "chore: bump version to ${{ steps.version.outputs.version }}"
labels: |
automated
version-bump

auto-merge-version-pr:
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request' &&
startsWith(github.event.pull_request.title, 'chore: bump version to') &&
github.event.pull_request.head.repo.full_name == github.repository &&
(github.event.action == 'opened' || github.event.action == 'synchronize')
steps:
- name: Wait for checks and merge
uses: actions/github-script@v7
with:
script: |
const maxWait = 300;
const checkInterval = 10;
let waited = 0;

while (waited < maxWait) {
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
});

if (pr.mergeable === true && pr.mergeable_state === 'clean') {
await github.rest.pulls.merge({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.payload.pull_request.number,
merge_method: 'squash'
});
return;
}

await new Promise(resolve => setTimeout(resolve, checkInterval * 1000));
waited += checkInterval;
}

throw new Error('Timeout waiting for PR to be mergeable');

create-tag-and-release:
runs-on: ubuntu-latest
if: |
github.event_name == 'pull_request' &&
startsWith(github.event.pull_request.title, 'chore: bump version to') &&
github.event.pull_request.merged == true
steps:
- name: Extract version from PR title
id: version
- name: Update package.json version (in memory only)
run: |
VERSION=$(echo "${{ github.event.pull_request.title }}" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
echo "version=$VERSION" >> $GITHUB_OUTPUT
npm version ${{ steps.version.outputs.version }} --no-git-tag-version --no-commit-hooks

- name: Create tag
uses: actions/github-script@v7
with:
script: |
const version = '${{ steps.version.outputs.version }}';
const tagName = `v${version}`;

// Create tag via API (doesn't require checkout)
await github.rest.git.createRef({
owner: context.repo.owner,
repo: context.repo.repo,
ref: `refs/tags/${tagName}`,
sha: context.payload.pull_request.merge_commit_sha
});
- name: Create tag from current commit
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "v${{ steps.version.outputs.version }}" -m "Release v${{ steps.version.outputs.version }}"
git push origin "v${{ steps.version.outputs.version }}"

- name: Generate release notes
id: release-notes
Expand All @@ -130,22 +61,22 @@ jobs:
const { data: commits } = await github.rest.repos.listCommits({
owner: context.repo.owner,
repo: context.repo.repo,
per_page: 20,
per_page: 15,
sha: 'main'
});

const notes = commits
.filter(c =>
!c.commit.message.includes('chore: bump version') &&
!c.commit.message.includes('[skip ci]')
!c.commit.message.includes('[skip release]')
)
.slice(0, 10)
.map(c => `- ${c.commit.message.split('\n')[0]}`)
.join('\n');

core.setOutput('body', notes || 'See commit history for details.');

- name: Create Release
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: v${{ steps.version.outputs.version }}
Expand All @@ -158,5 +89,6 @@ jobs:
**Full Changelog**: https://github.com/${{ github.repository }}/compare/v${{ steps.version.outputs.version }}^...v${{ steps.version.outputs.version }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish to npm
run: npm publish