Building sadame¶
Sadame's Ruby code in lib/ is compiled together with
mruby into one executable. This page explains the
build targets, what each produces, how the release binaries are checked
before they are published, and how to fix common build problems.
Overview¶
| Task | Runs on | Produces | Used for |
|---|---|---|---|
rake build:native |
any Unix | build/host/bin/sadame and build/host/bin/mruby |
development; rake test:mruby |
rake build:linux-amd64 |
any machine with Docker | dist/sadame-linux-amd64 |
releases |
rake build:linux-arm64 |
any machine with Docker | dist/sadame-linux-arm64 |
releases |
rake build:macos |
macOS with Xcode command line tools | dist/sadame-darwin-arm64, dist/sadame-darwin-amd64 |
releases |
rake verify:linux-amd64, verify:linux-arm64 |
Docker | runs the binary in several distributions | releases |
rake verify:macos |
macOS | runs both macOS binaries | releases |
rake checksums |
any | dist/SHA256SUMS |
releases |
The first build clones mruby 3.4.0 into vendor/ and the third-party gems
into build/*/repos/. Both directories are ignored by git.
How the build is put together¶
build_config.rbis mruby's build configuration.sadame_gemslists every mruby gem in the binary; adding one needs a reason in the commit message. TheSADAME_TARGETenvironment variable selects one of four configurations:development(debug information, plus an mruby interpreter with mruby-mtest for the tests),linux-static,darwin-arm64anddarwin-amd64. The Rakefile sets it.build_config.rb.lockpins each third-party gem to a commit. mruby writes it on the first build and reads it afterwards, so every build uses the same code. Commit it when it changes.mrbgem.raketurns sadame into an mruby gem. It reads therequire_relativelines oflib/sadame.rband compiles those files in that order, so CRuby and mruby load the same code the same way.tools/sadame/sadame.cis themainfunction: it buildsARGV, callsSadame.mainand turns the returned Integer into the exit status.
Static Linux binaries¶
rake build:linux-amd64 and rake build:linux-arm64 build inside an
Alpine Linux container of the target architecture
(script/build/alpine.Dockerfile). Alpine uses musl libc, which links
statically without the problems glibc has with static binaries, so the
result runs on any Linux distribution of that architecture. The container
runs as your user, so the files in build/ and dist/ stay yours, and the
binary is stripped with the container's strip.
Building for the other architecture than your machine's needs an emulator registered with Docker. Docker Desktop and OrbStack include one; on a Linux machine, register QEMU once:
docker run --privileged --rm tonistiigi/binfmt --install arm64
Emulated builds are slower, especially under QEMU.
macOS binaries¶
rake build:macos compiles both macOS binaries on one Mac with clang's
-arch flag, targeting macOS 11 and newer. It uses mruby's cross-build
support, so the mrbc compiler used during the build runs natively.
Verification¶
Before a binary is published, it has to run unmodified in the systems it is
meant for. rake verify:linux-amd64 and rake verify:linux-arm64 start
each of these images with the binary mounted, and run
script/build/smoke-test.sh inside it:
| Image | amd64 | arm64 |
|---|---|---|
ubuntu:24.04 |
yes | yes |
debian:12 |
yes | yes |
rockylinux/rockylinux:9 |
yes | yes |
alpine:3.20 |
yes | yes |
archlinux:latest |
yes | no arm64 image exists |
menci/archlinuxarm:latest |
no | yes (community image of Arch Linux ARM) |
The smoke test runs --version, then plan, apply and drift on
examples/01-hello, edits the managed file, and checks that drift now
exits 1 and reports "drift": true in JSON. rake verify:macos runs the
same test with both macOS binaries; the Intel one runs through Rosetta when
it is installed.
Continuous integration and releases¶
.github/workflows/build.ymlbuildslinux-amd64on an x86_64 runner,linux-arm64on an ARM64 runner and both macOS binaries on a macOS runner. Each Linux binary is then verified on a runner of its own architecture:filemust report it statically linked, and it runs in every distribution image. A last job serves the binaries over HTTP and runsinstall.shagainst them, once with correct checksums and once with a wrong one, which must fail.ci.ymlruns that workflow on every push and pull request, after the test jobs.release.ymlruns on av*tag. After the build workflow passes, it checks that the tag matchesSadame::VERSION, writesSHA256SUMS, takes the release notes from the matching section ofCHANGELOG.md, and creates the GitHub release with all files attached. Started by hand from the Actions tab, it does everything except publishing and keeps the assembled files as a workflow artifact instead.
To publish a release:
- Set
VERSIONinlib/sadame/version.rband move theUnreleasedentries ofCHANGELOG.mdunder a heading such as## [0.1.0] - 2026-11-15. - Regenerate the docs' terminal sessions and the README recording, which
show the version:
rake docs:transcripts docs:recording. - Commit, then tag and push:
git tag v0.1.0 && git push origin v0.1.0. - When the release is published, write the packages with its checksums,
rake "packaging[0.1.0]", and commit them: the Homebrew formula inhomebrew/sadame.rband the AUR package (PKGBUILDand.SRCINFO) inpackaging/aur/. - Copy
homebrew/sadame.rbtoFormula/sadame.rbin the enderkus/homebrew-sadame tap and push it there;brew upgrade sadamethen finds the release.
Building a release locally¶
rake build:linux-amd64 build:linux-arm64 verify:linux-amd64 verify:linux-arm64
rake build:macos verify:macos # on a Mac
rake checksums
script/build/test-install.sh # install.sh against dist/
Common problems¶
exec format errorwhile building the other architecture. No emulator is registered; run thetonistiigi/binfmtcommand above.- A gem fails to clone. Builds need network access to GitHub on the
first run. After that the gems in
build/*/repos/are reused. - The lock file changed unexpectedly. A new build target or a new gem
adds entries to
build_config.rb.lock. Check the diff and commit it. - Stale objects after changing
build_config.rb. Delete the target's directory underbuild/and build again.
See also¶
- Architecture for how the code is organized and the rules for code that must run on mruby.
- Install for the user side of the release files.