Skip to content

Release

Release #5

Workflow file for this run

name: Release
# One click from the Actions tab. The tag is the source of truth for the version: this
# workflow tags the current main, creates the GitHub Release, and dispatches the publish
# workflow on that tag, which stamps package.json from the tag before `npm publish`.
# Nothing is pushed to main, so the branch ruleset needs no bypass and no token exists.
# (Releases created with GITHUB_TOKEN do not trigger other workflows; the dispatch is explicit.)
on:
workflow_dispatch:
inputs:
bump:
description: "Version bump relative to the latest v* tag"
type: choice
default: patch
options: [patch, minor, major]
version:
description: "Exact version instead of a bump (e.g. 0.2.0), optional"
type: string
default: ""
dry_run:
description: "Compute and show the version; do not tag, release or publish"
type: boolean
default: false
permissions:
contents: write # push the tag, create the release
actions: write # dispatch publish.yml
concurrency: release
jobs:
release:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- run: bun install --frozen-lockfile
- run: bun run typecheck
- run: bun test
- name: Decide the version
id: v
run: |
set -euo pipefail
latest="$(git tag --list 'v*' --sort=-v:refname | head -1)"
latest="${latest#v}"; latest="${latest:-0.0.0}"
explicit="${{ inputs.version }}"
if [ -n "$explicit" ]; then
next="${explicit#v}"
else
IFS=. read -r major minor patch <<< "$latest"
case "${{ inputs.bump }}" in
major) next="$((major+1)).0.0" ;;
minor) next="$major.$((minor+1)).0" ;;
*) next="$major.$minor.$((patch+1))" ;;
esac
fi
echo "$next" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+([-.][0-9A-Za-z.-]+)?$' || { echo "not a semver version: $next" >&2; exit 1; }
git rev-parse -q --verify "refs/tags/v$next" >/dev/null && { echo "tag v$next already exists" >&2; exit 1; }
if ! grep -q "^## ${next}" CHANGELOG.md; then
echo "::warning::CHANGELOG.md has no '## $next' section — add one on main before or after the release"
fi
echo "latest=$latest" >> "$GITHUB_OUTPUT"
echo "next=$next" >> "$GITHUB_OUTPUT"
echo "Latest tag v$latest → releasing v$next from $(git rev-parse --short HEAD)"
- name: Tag, release, publish
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
v="${{ steps.v.outputs.next }}"
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag -a "v$v" -m "v$v"
git push origin "v$v"
gh release create "v$v" --title "v$v" --generate-notes
gh workflow run publish.yml --ref "v$v"
echo "Released v$v; publish workflow dispatched on the tag."
- name: Dry run
if: ${{ inputs.dry_run }}
run: echo "Dry run — would tag v${{ steps.v.outputs.next }} (latest v${{ steps.v.outputs.latest }}), create the release, and dispatch publish.yml."