Skip to content

ci(release): semantic-release + GoReleaser pipeline#32

Merged
altaywtf merged 3 commits into
mainfrom
chore/release-pipeline
Apr 26, 2026
Merged

ci(release): semantic-release + GoReleaser pipeline#32
altaywtf merged 3 commits into
mainfrom
chore/release-pipeline

Conversation

@altaywtf
Copy link
Copy Markdown
Member

@altaywtf altaywtf commented Apr 26, 2026

Summary

  • Replace manual scripts/release.sh with a push-to-main, Conventional Commits driven pipeline
  • semantic-release decides version + tags + drafts the GitHub Release; GoReleaser ships darwin tarballs and bumps uinaf/homebrew-tap
  • Verify on PR + push, release only on push to main (skipped on [skip ci])
  • Tarball name + contents byte-for-byte match the current layout (healthd_v<ver>_darwin_<arch>.tar.gz, binary-only) so scripts/install.sh and the existing brew formula keep working

Changes

  • New .github/workflows/ci.yml — verify (fmt + lint + test + coverage gate via go run ./cmd/verify) → release (semantic-release → GoReleaser → build provenance attestation)
  • New .goreleaser.yaml — darwin arm64+amd64, ldflag-injected version, brew formula auto-bump in uinaf/homebrew-tap via TAP_GITHUB_TOKEN
  • New .releaserc.json — tag-only mode (no source bump for Go), conventionalcommits preset, chore/ci/docs excluded from version bumps
  • New .gitignore/dist/, /bin/
  • Delete scripts/release.sh, .github/workflows/release.yml, .github/workflows/ci-go-checks.yml
  • cmd/root.go — wire Version/Commit/BuildDate (ldflag-injected) + cobra --version flag, with a debug.ReadBuildInfo fallback for plain go build
  • go mod tidy (new GoReleaser before-hook surfaced this) reclassifies bubbletea + lipgloss as direct deps — they are imported directly
  • AGENTS.md / CLAUDE.md — drop the manual release line, add a Releases section

Validation

  • go run ./cmd/verify — total 82.7%, all packages ≥ 70%
  • actionlint .github/workflows/ci.yml — clean
  • goreleaser check — clean
  • goreleaser release --snapshot --clean --skip=publish,sign — produces healthd_v0.5.0_darwin_arm64.tar.gz containing only healthd, plus checksums.txt and a generated brew formula matching the existing tap shape
  • go build + ./healthd --versiondev (4d7c067, 2026-04-26T...) from VCS info
  • First real release (post-merge): write a feat: / fix: commit and watch verify→release→tap-update

Risks

  • Required secret: TAP_GITHUB_TOKEN — fine-grained PAT (or GitHub App token) with contents: write on uinaf/homebrew-tap only. Without it the release job will fail on the GoReleaser brew step. Add to repo secrets before the first feat/fix commit lands on main.
  • semantic-release is tag-only here (Go doesn't have a package.json to bump back). No [skip ci] loop risk, but the guard is in both jobs as defense-in-depth.
  • This commit itself is ci(...) — by .releaserc.json rules it does not trigger a release, so merging is safe.
  • GoReleaser warns that brews: is being phased out in favor of homebrew_casks. Migration is a separate concern (touches the tap repo too); keeping brews: for now since the existing tap uses Formula format.

Linked Issues

None.


Note

Medium Risk
Medium risk because it replaces the release/tagging flow with an automated GitHub Actions pipeline that depends on repo secrets (TAP_GITHUB_TOKEN) and new tooling; failures would affect publishing rather than runtime behavior.

Overview
Replaces the separate Go CI and tag-based release workflows (and the manual scripts/release.sh) with a single ci.yml that runs go run ./cmd/verify on PRs/pushes and, on pushes to main, automatically tags/releases via semantic-release and publishes binaries/Homebrew updates via GoReleaser (plus build provenance attestation).

Adds GoReleaser (.goreleaser.yaml) and semantic-release (.releaserc.json) configuration, updates the CLI to expose --version using ldflag-injected Version/Commit/BuildDate with a debug.ReadBuildInfo fallback, and tweaks docs and ignores (/dist, /bin).

Reviewed by Cursor Bugbot for commit 9545644. Bugbot is set up for automated code reviews on this repo. Configure here.

@altaywtf altaywtf force-pushed the chore/release-pipeline branch from 344c9ed to c583be5 Compare April 26, 2026 15:40
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d1a9353d2f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml Outdated
Comment on lines +9 to +11
concurrency:
group: verify-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove workflow-level auto-cancel for release runs

The workflow-level concurrency block cancels any in-progress run on main when a newer push arrives, which can interrupt the release job after semantic-release has already created the tag/GitHub release but before GoReleaser uploads tarballs/checksums. In that case, the next run may not republish assets for that already-tagged version, leaving a published release without binaries. This is introduced by placing cancel-in-progress: true at the workflow scope in ci.yml instead of scoping cancellation to verify only.

Useful? React with 👍 / 👎.

@altaywtf altaywtf force-pushed the chore/release-pipeline branch from c583be5 to be4fcdb Compare April 26, 2026 15:42
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit be4fcdb. Configure here.

Comment thread .github/workflows/ci.yml Outdated
Comment thread .github/workflows/ci.yml
Replace the manual `scripts/release.sh` flow with a push-to-main,
Conventional-Commits-driven pipeline:

- Verify job runs on PR + push (`go run ./cmd/verify`).
- Release job runs on push to main after verify, skipped on `[skip ci]`.
- semantic-release decides the next version from commits and creates
  the tag + GitHub Release (tag-only mode — no source bump for Go).
- GoReleaser builds darwin/arm64 + darwin/amd64 tarballs, uploads them
  to the release, and pushes the formula update to `uinaf/homebrew-tap`.
- `actions/attest-build-provenance` signs the artifacts.
- Bot identity matches the org pattern (`glitch418x`).

Tarball naming and contents match the current artifacts byte-for-byte
(`healthd_v<ver>_darwin_<arch>.tar.gz`, binary-only) so the existing
`scripts/install.sh` and `uinaf/homebrew-tap/Formula/healthd.rb`
keep working without changes.

Side changes:
- Add `--version` wiring in `cmd/root.go` so GoReleaser ldflags inject
  build metadata; falls back to `debug.ReadBuildInfo` for `go build`.
- `go mod tidy` (now run as a release before-hook) reclassifies
  bubbletea/lipgloss as direct deps — they are imported directly.
- Drop redundant `integration` + `e2e/cli` steps from CI; `cmd/verify`
  already covers them via `go list ./...`.
- Add `.gitignore` for `dist/` and `bin/`.

Requires `TAP_GITHUB_TOKEN` repo secret (fine-grained PAT with
`contents: write` on `uinaf/homebrew-tap`) before the first release.
@altaywtf altaywtf force-pushed the chore/release-pipeline branch from be4fcdb to af421b4 Compare April 26, 2026 15:51
@altaywtf
Copy link
Copy Markdown
Member Author

Both review findings addressed in af421b4:

  1. Workflow-level cancel-in-progress could abort an in-flight release (Codex P1, Cursor High) — moved the cancellable concurrency from workflow scope to the verify job only. The release job keeps its own non-cancellable release-${{ github.repository }}-main group, so a superseded PR push cancels its own verify run but a release run can never be aborted between semantic-release tagging and GoReleaser uploading.
  2. Release job missing the empty-check guard on .tool-versions parse (Cursor Medium) — copied the same [ -z "${version}" ] && exit 1 block the verify job uses.

actionlint clean.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 56d4f27b03

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml Outdated
run: git fetch --tags --force

- name: Run GoReleaser
if: steps.release.outputs.new_release_published == 'true'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Allow GoReleaser to run on release-job retries

Run GoReleaser is gated on steps.release.outputs.new_release_published == 'true', so if semantic-release succeeds (tag/release created) but a later step fails, rerunning the job on the same commit will skip artifact publishing because semantic-release now reports no new release. In that failure mode (for example transient GoReleaser/Homebrew token issues), the GitHub release stays published without binaries/checksums unless someone performs a manual recovery.

Useful? React with 👍 / 👎.

Codex P1: with the previous gate, a partial-failure rerun (e.g. brew
token blip after semantic-release tagged) would skip GoReleaser
because semantic-release reports no NEW release the second time.
Result was a published GitHub Release without binaries.

Now we always fetch tags after semantic-release, then detect via
'git describe --exact-match --tags HEAD'. Re-running the job
recovers the GoReleaser publish step against the existing tag.
@altaywtf
Copy link
Copy Markdown
Member Author

Updates on 9545644:

  • @chatgpt-codex-connector P1 — "Allow GoReleaser to run on release-job retries" — fixed. GoReleaser is now gated on git describe --exact-match --tags HEAD rather than steps.release.outputs.new_release_published. After a partial-failure rerun (semantic-release tagged but GoReleaser failed on, say, a transient TAP_GITHUB_TOKEN issue), the second run finds the tag still at HEAD, skips the no-op semantic-release step, and re-runs GoReleaser against the existing release (mode: append in .goreleaser.yaml makes the upload idempotent).

  • @cursor — "Release job missing Go version empty-check guard" — false positive on the re-review. The guard is at lines 84-87 of ci.yml:

    if [ -z "${version}" ]; then
      echo "Failed to determine Go version from .tool-versions" >&2
      exit 1
    fi
    

    Same BUGBOT_BUG_ID: 2fafa678-... as the prior comment — looks like the bot replayed without re-diffing.

@chatgpt-codex-connector
Copy link
Copy Markdown

To use Codex here, create an environment for this repo.

@altaywtf altaywtf merged commit cfac300 into main Apr 26, 2026
3 checks passed
@altaywtf altaywtf deleted the chore/release-pipeline branch April 26, 2026 18:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant