Skip to content

Publish pre-built binaries and packages with each release - #55

Merged
beetlebugorg merged 3 commits into
beetlebugorg:mainfrom
openwatersio:release/prebuilt-binaries
Aug 18, 2026
Merged

Publish pre-built binaries and packages with each release#55
beetlebugorg merged 3 commits into
beetlebugorg:mainfrom
openwatersio:release/prebuilt-binaries

Conversation

@bkeepers

@bkeepers bkeepers commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

This adds a release workflow so that pushing a vX.Y.Z tag produces downloadable binaries for macOS, Linux and Windows on both architectures, plus a Homebrew formula and a .deb.

Adding a tag via git tag v0.4.0 && git push origin v0.4.0 or the GitHub Releases UI runs .github/workflows/release.yml, which:

  1. Builds ReleaseFast for six targets and packages each one as tile57-<version>-<arch>-<os>.tar.gz, or .zip on Windows, carrying bin/tile57, lib/libtile57.a, include/tile57.h, and the licenses.
  2. Builds tile57_<version>_{amd64,arm64}.deb from the same payload.
  3. Creates the GitHub release with --generate-notes, all the archives, and a SHA256SUMS file.
  4. Regenerates Formula/tile57.rb in the Homebrew tap and pushes it.

A tag carrying a suffix, v0.4.0-rc1, publishes as a prerelease and skips the tap, so the whole matrix can be exercised without shipping anything.

Before any of that, each build leg checks the tag against the version in build.zig.zon, src/tile57.zig and tools/common.zig. A mismatch fails the build rather than shipping a binary whose tile57 version contradicts the file it arrived in.

Two target choices

macOS builds run natively per arch instead of cross-compiling from one runner. scripts/macho-align.sh calls ld -r and libtool without -arch, so it can only repack libtile57.a for the host. Threading the target arch through that script would allow a single-runner build, but native runners cost no code. That threading is the fix if the macos-15-intel runner is ever retired.

Linux uses the -gnu targets rather than -musl. build.zig already retargets the CLI to static musl on a glibc host, so the -gnu archives carry a portable tile57 and a libtile57.a that a glibc C host can still link. Building -musl would give up the second half of that.

Packages

Homebrew covers macOS and Linux from one formula, generated by scripts/brew-formula.sh out of the checksums of the archives that were just built. The formula installs the binary, the static library and the header, and its test do block runs tile57 version. Generating it beats hand-editing because all four checksums change every release. It does need a tap repo and a token, which is the checklist below.

The .deb is a release asset, not an apt repository. sudo apt install ./tile57_0.4.0_amd64.deb works, apt install tile57 does not. A real repository needs a signing key in secrets and somewhere to host the index, which seemed worth deferring until someone asks for it. Scoop, winget and AUR are out for the same reason.

Docs

Installation leads with binaries and keeps building from source below them. Its note about zig fetch being broken is gone: .paths in build.zig.zon covers vendor/, include/ and tools/, and I confirmed that a fetched package builds libtile57.a with no submodules initialised, so the lazy s101_portrayal dependency is doing its job. The same stale note in Zig API is replaced with the zig fetch --save line. Contributing gains the release steps.

Tested

The .deb was built and inspected with dpkg-deb, so root ownership, the control fields, and the layout under /usr are all confirmed. Tarball layout checked. Formula generation checked, including its non-zero exit when an archive is missing. actionlint and shellcheck are clean, and a zig fetched package built to libtile57.a without submodules.

The macOS legs are the untested part. CI has never built Darwin, so an -rc1 tag should come before the real one.

TODO

Before the first tag:

Without the secret the tap step logs a warning and exits, and the release still publishes.

First release:

  • Push v0.3.0-rc1 and confirm all six build legs go green, especially the two macOS ones.
  • Download one archive and one .deb and check they run on a clean machine.
  • Push the real tag.
  • Verify brew install beetlebugorg/tap/tile57 on macOS and on Linux.
  • Update the version numbers written out in the download commands in docs/docs/installation.md.

Not in this PR, worth considering later:

  • Notarize the macOS binaries so they don't need xattr -d com.apple.quarantine. Needs an Apple Developer account and certificates in secrets.
  • An apt repository, if apt install tile57 is wanted.
  • Add macos-latest to ci.yml so Darwin breakage surfaces on PRs instead of at tag time.

@beetlebugorg

Copy link
Copy Markdown
Owner

Is there a good way to test this without merging and tagging? I've found I always need to iterate a little on GHA and it creates a lot of clutter in the build pipeline/tags/commits.

Maybe cloning the repo, merging there and testing, then pull any changes back into this PR?

Pushing a vX.Y.Z tag builds macOS, Linux and Windows on x86-64 and arm64,
attaches the archives, .debs and SHA256SUMS to the release, and regenerates
the Homebrew tap formula.

macOS builds run natively per arch because macho-align.sh repacks
libtile57.a for the host only. Linux uses -gnu targets so libtile57.a stays
glibc-linkable while build.zig retargets the CLI to static musl.
A fork testing the workflow pushes its formula to its own tap and points
the downloads at its own release, instead of writing to the upstream org.
@bkeepers
bkeepers force-pushed the release/prebuilt-binaries branch from 0838b67 to bf0a770 Compare August 18, 2026 19:12
Copilot AI lite review requested due to automatic review settings August 18, 2026 19:12
@bkeepers

bkeepers commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

Ran this on my fork.

Action run: https://github.com/openwatersio/tile57/actions/runs/32174366290/job/95834675883
Release: https://github.com/openwatersio/tile57/releases/tag/v0.3.0

$ brew install openwatersio/tap/tile57
==> Fetching downloads for: tile57
✔︎ Formula tile57 (0.3.0)                                                                                                              Verified      6.6MB/  6.6MB
==> Installing tile57 from openwatersio/tap
==> Caveats
The following tile57 executables are shadowed by other commands earlier in your PATH:
  tile57 (shadowed by /Users/bkeepers/.local/bin/tile57)
Running these by name will not invoke the version provided by Homebrew.
Disable this behaviour by setting `HOMEBREW_NO_PATH_SHADOW_CHECK=1`.
Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).
==> Summary
🍺  /opt/homebrew/Cellar/tile57/0.3.0: 8 files, 18MB, built in 2 seconds
==> Running `brew cleanup tile57`...
Disable this behaviour by setting `HOMEBREW_NO_INSTALL_CLEANUP=1`.
Hide these hints with `HOMEBREW_NO_ENV_HINTS=1` (see `man brew`).

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Adds an automated release pipeline so pushing a vX.Y.Z tag produces versioned, downloadable build artifacts (archives for multiple OS/arch targets, .deb packages, checksums, and a Homebrew formula update), aligning distribution with tile57’s “bake once, render/compose many” packaging story.

Changes:

  • Introduces a GitHub Actions tag-triggered release workflow to build, package, checksum, and publish release assets (and update the Homebrew tap on non-prerelease tags).
  • Adds release packaging scripts for staging archives/.deb and generating a Homebrew formula from freshly-built checksums.
  • Updates docs/README to lead with binary installation options and documents release-cutting steps.

Reviewed changes

Copilot reviewed 7 out of 8 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
.github/workflows/release.yml Tag-triggered workflow that builds a target matrix, publishes release assets + SHA256SUMS, and updates a Homebrew tap for non-prereleases.
scripts/package-release.sh Stages zig-out outputs into per-target archives and optionally builds .deb packages.
scripts/brew-formula.sh Generates a Homebrew formula by computing sha256 for the just-built archives.
README.md Updates “Start here” to prefer Homebrew / Releases and summarizes new distribution options.
docs/docs/installation.md Reworks installation docs to include Releases, .deb, Homebrew, and updated Zig fetch guidance.
docs/docs/zig-api.md Updates Zig package consumption guidance to use zig fetch --save for tagged tarballs.
docs/docs/contributing.md Documents release procedure and version/tag consistency requirements.
.gitignore Ignores /dist/ created by the new release packaging scripts.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread scripts/package-release.sh Outdated
Comment thread docs/docs/installation.md Outdated
Comment thread docs/docs/installation.md Outdated
Debian reads 0.4.0-rc1 as revision rc1 of 0.4.0, which sorts after the
final release and blocks the upgrade as a downgrade. Use 0.4.0~rc1.

Also names the archive placeholder as the Zig target triple it is, and
points the quarantine command at the extracted directory.
@beetlebugorg

Copy link
Copy Markdown
Owner

LGTM, if you have no further changes I'll merge it. I've already setup the homebrew-tap repository.

Thanks again for this PR!

@bkeepers

Copy link
Copy Markdown
Contributor Author

Should be good to go!

@beetlebugorg
beetlebugorg merged commit 0bf4530 into beetlebugorg:main Aug 18, 2026
5 checks passed
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.

3 participants