Repository navigation
Release #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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." |