name: release # Every push to main that passes CI cuts a new release. The workflow: # 1. waits for the ci workflow on the same commit to succeed # 2. looks up the latest existing vX.Y.Z tag # 3. bumps the patch number (vX.Y.Z+1) — or starts at v0.0.1 if none # 4. creates and pushes that tag # 5. runs goreleaser, which builds binaries for linux/darwin/windows # (amd64 + arm64) and attaches them plus checksums.txt to a fresh # GitHub Release page # # Skip a release by putting [release=skip] anywhere in the commit # message — useful for docs-only or ci-only commits. We use a rare # equals-sign form so commits that merely *mention* the feature # ("make the release step optional") don't accidentally opt out. on: workflow_run: workflows: [ci] types: [completed] branches: [main] permissions: # Needed to create tags and to upload release archives. contents: write # Only one release job at a time. If two pushes land in quick # succession, queue them so they don't race on the tag. concurrency: group: release cancel-in-progress: false jobs: release: # Only run when ci succeeded and we're on main, and when the head # commit doesn't opt out via [skip-release]. if: ${{ github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.head_branch == 'main' && !contains(github.event.workflow_run.head_commit.message, '[release=skip]') }} runs-on: ubuntu-latest steps: - name: checkout uses: actions/checkout@v4 with: # Full history so we can read existing tags. fetch-depth: 0 # Check out the exact sha that ci verified, not whatever the # branch tip happens to be at this moment (which may have # advanced since ci finished). ref: ${{ github.event.workflow_run.head_sha }} - name: compute next version id: bump shell: bash run: | set -euo pipefail # Find the newest vX.Y.Z tag. If the repo has none yet, start # at v0.0.0 so the first release becomes v0.0.1. latest=$(git tag --list 'v*.*.*' --sort=-v:refname | head -n1) if [ -z "$latest" ]; then latest="v0.0.0" fi echo "latest tag: $latest" # Strip the v prefix, split on dots, bump the patch. # After .99 roll over to the next minor (e.g. 0.0.99 -> 0.1.0, # 0.1.99 -> 0.2.0). Keep major fixed at 0 for now. ver="${latest#v}" IFS=. read -r major minor patch <<<"$ver" if [ "$patch" -ge 99 ]; then minor=$((minor + 1)) patch=0 else patch=$((patch + 1)) fi next="v${major}.${minor}.${patch}" echo "next tag: $next" echo "next=$next" >> "$GITHUB_OUTPUT" echo "version=${next#v}" >> "$GITHUB_OUTPUT" - name: create and push tag env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" git tag -a "${{ steps.bump.outputs.next }}" \ -m "release ${{ steps.bump.outputs.next }}" git push origin "${{ steps.bump.outputs.next }}" - name: set up go uses: actions/setup-go@v5 with: go-version: "1.23" cache: true - name: run goreleaser uses: goreleaser/goreleaser-action@v6 with: distribution: goreleaser version: latest args: release --clean env: # goreleaser reads the tag we just pushed and builds binaries # for every (goos, goarch) combination in .goreleaser.yaml. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}