feat(release): release binaries and install script #26

Merged
deadstyle merged 7 commits from feature/11-release-install into develop 2026-10-02 12:26:50 -05:00
Owner

Summary

Release binaries and the one-line installer (#11). Pushing a v* tag that is on main builds static binaries for Linux and macOS (amd64 and arm64) and publishes a Forgejo release with a checksums.txt. curl -fsSL https://git.bobparsons.dev/deadstyle/lazyforge/raw/branch/main/install.sh | bash installs or upgrades lazyforge in ~/.local/bin.

Changes

  • make release: plain cross-compiled go build (CGO off, version stamped in), with tar.gz archives and SHA-256 checksums.txt in dist/.
  • .forgejo/workflows/release.yml: on a v* tag it refuses tags that aren't on main, runs make check, then make release, then publishes.
  • scripts/publish-release.sh: creates the release as a draft, uploads every asset, then publishes it. The installer therefore never sees a half-uploaded release. The token is never printed.
  • install.sh:
    • detects the OS and architecture, and resolves the latest release, or LAZYFORGE_VERSION
    • refuses to install on a checksum mismatch or a missing checksum line
    • replaces the binary atomically, so it's safe to run twice
    • prints a PATH hint, and uses sudo only when the target directory needs it
  • CI installs shellcheck and runs make install-test, which is part of make check. Linux only, because no macOS runner is available.
  • ADR 0008 records the decision: no GoReleaser, because four targets and one API call don't justify another tool.

Test plan

  • scripts/test-install.sh runs the real make release and serves it from a local stand-in server. It covers 9 cases:
    • release artifacts
    • fresh install
    • reinstall
    • corrupt archive keeps the existing binary
    • corrupt archive with no prior install leaves nothing
    • missing checksum line
    • pinned version skips the latest lookup
    • PATH hint when off PATH, and no hint when on it
  • Mutation check: 15 one-at-a-time breakages. Two weak assertions were found and strengthened, and every case was then killed.
  • make check exit 0: format, lint, go test -race, govulncheck, build, shellcheck, and 9/9 install cases.

Not verifiable before the first tag

The workflow's automatic token (secrets.GITHUB_TOKEN) and its write access to releases can only be exercised by a real tag push. The asset field name attachment was confirmed against the instance's API description.

Rulings

  • Scope and tooling were decided by the engineering lead, without a planner.
  • The reviewer's Info finding "release visible before assets upload" was promoted to a fix, so the release is now published as a draft first.

Closes #11

🤖 Generated with Claude Code

## Summary Release binaries and the one-line installer (#11). Pushing a `v*` tag that is on `main` builds static binaries for Linux and macOS (amd64 and arm64) and publishes a Forgejo release with a `checksums.txt`. `curl -fsSL https://git.bobparsons.dev/deadstyle/lazyforge/raw/branch/main/install.sh | bash` installs or upgrades lazyforge in `~/.local/bin`. ## Changes - `make release`: plain cross-compiled `go build` (CGO off, version stamped in), with tar.gz archives and SHA-256 `checksums.txt` in `dist/`. - `.forgejo/workflows/release.yml`: on a `v*` tag it refuses tags that aren't on `main`, runs `make check`, then `make release`, then publishes. - `scripts/publish-release.sh`: creates the release as a **draft**, uploads every asset, then publishes it. The installer therefore never sees a half-uploaded release. The token is never printed. - `install.sh`: - detects the OS and architecture, and resolves the latest release, or `LAZYFORGE_VERSION` - **refuses to install on a checksum mismatch or a missing checksum line** - replaces the binary atomically, so it's safe to run twice - prints a PATH hint, and uses sudo only when the target directory needs it - CI installs shellcheck and runs `make install-test`, which is part of `make check`. Linux only, because no macOS runner is available. - ADR 0008 records the decision: no GoReleaser, because four targets and one API call don't justify another tool. ## Test plan - `scripts/test-install.sh` runs the real `make release` and serves it from a local stand-in server. It covers 9 cases: - release artifacts - fresh install - reinstall - corrupt archive keeps the existing binary - corrupt archive with no prior install leaves nothing - missing checksum line - pinned version skips the latest lookup - PATH hint when off PATH, and no hint when on it - Mutation check: 15 one-at-a-time breakages. Two weak assertions were found and strengthened, and every case was then killed. - `make check` exit 0: format, lint, `go test -race`, govulncheck, build, shellcheck, and 9/9 install cases. ## Not verifiable before the first tag The workflow's automatic token (`secrets.GITHUB_TOKEN`) and its write access to releases can only be exercised by a real tag push. The asset field name `attachment` was confirmed against the instance's API description. ## Rulings - Scope and tooling were decided by the engineering lead, without a planner. - The reviewer's Info finding "release visible before assets upload" was promoted to a fix, so the release is now published as a draft first. Closes #11 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Plain go build cross-compile via make release, curl-based publish script,
tag-triggered release workflow, install.sh with checksum verification,
ADR 0008, and install-test wired into make check and CI.

#11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh removes its staged temp file when the copy/chmod/mv fails.
publish-release.sh tolerates whitespace in the release JSON.
release.yml installs shellcheck with the same sudo handling as ci.yml.
test-install.sh gains a missing-checksum-line case.

#11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An upload failure now exits non-zero and leaves the draft, so install.sh
never sees a release with missing assets. Refs #11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs #11.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test(install): catch stale dist files and incomplete checksums.txt
Some checks failed
ci / check (pull_request) Failing after 26s
23ffa66208
#11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(ci): pin shellcheck v0.11.0 in CI and release workflows
All checks were successful
ci / check (pull_request) Successful in 56s
cd9949b3ec
The runner's apt shellcheck is older than 0.11 and reports SC2317 on
functions in scripts/test-install.sh that are invoked indirectly.
Download the pinned release instead of installing from apt.

#11

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
deadstyle merged commit eb6bb1d70f into develop 2026-10-02 12:26:50 -05:00
deadstyle deleted branch feature/11-release-install 2026-10-02 12:26:50 -05:00
Sign in to join this conversation.
No description provided.