diff options
| author | Peter Geissler <peter@haxx.in> | 2026-08-26 23:37:41 +0200 |
|---|---|---|
| committer | Peter Geissler <peter@haxx.in> | 2026-08-26 23:37:41 +0200 |
| commit | 626af41b693aa98cd7d86cabd7c6f1a94d01ec41 (patch) | |
| tree | 272c7e602b59354512431a6c4d611e0c5332d3e7 | |
| parent | macos: sign and notarize on a mac that is not this one (diff) | |
| download | slosh-626af41b693aa98cd7d86cabd7c6f1a94d01ec41.tar.gz slosh-626af41b693aa98cd7d86cabd7c6f1a94d01ec41.tar.xz slosh-626af41b693aa98cd7d86cabd7c6f1a94d01ec41.zip | |
brew-release: cut a release from the Linux box it is cut from
Nothing about updating the formula needs a mac or needs brew -- it
resolves a ref, fetches a tarball, hashes it, rewrites three stanzas.
But it hashed with `shasum`, which is perl's and only usually present
on Linux, where `sha256sum` is coreutils and always is. Take whichever
exists, as macos-remote-build already did.
The version count no longer falls back to 0 when the commit is not in
this clone. `0.1.0-0-<short>` is older than every version published, so
a release cut from a clone that had not fetched yet would have looked
like a downgrade and never installed; say so and stop instead.
Also written down: why the formula builds from source rather than
shipping the notarized zip the mac builder makes -- brew's curl sets no
quarantine attribute, so notarization gains a formula nothing, and the
build is a minute with zig as its only dependency. If that ever changes
the answer is bottles, which are additive and cost a per-macOS-version
matrix, not a cask.
| -rwxr-xr-x | contrib/brew-release | 23 | ||||
| -rw-r--r-- | contrib/homebrew/README.md | 38 | ||||
| -rw-r--r-- | contrib/homebrew/slosh.rb | 6 |
3 files changed, 62 insertions, 5 deletions
diff --git a/contrib/brew-release b/contrib/brew-release index 603ed88..d3eadda 100755 --- a/contrib/brew-release +++ b/contrib/brew-release @@ -29,6 +29,18 @@ TAP="" fail() { printf 'brew-release: %s\n' "$*" >&2; exit 1; } say() { printf ' %s\n' "$*"; } +# Neither brew nor a mac is needed to run this -- it resolves a ref, fetches a +# tarball and hashes it -- so it has to work on the Linux box a release is cut +# from. `shasum` is perl's and merely usually present there; `sha256sum` is +# coreutils and always is. Take whichever exists. +sha256_of() { + if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | cut -d' ' -f1 + elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | cut -d' ' -f1 + elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha256 "$1" | sed 's/.*= *//' + else fail "no sha256sum, shasum or openssl here -- cannot hash the snapshot" + fi +} + while [ $# -gt 0 ]; do case "$1" in --tap) [ $# -ge 2 ] || fail "--tap needs a directory"; TAP="$2"; shift 2 ;; @@ -75,7 +87,14 @@ esac mkver="$(sed -n 's/^VERSION *:*= *//p' "$root/Makefile" | head -1)" [ -n "$mkver" ] || fail "no VERSION in $root/Makefile" short="$(printf '%s' "$full" | cut -c1-7)" -count="$(git -C "$root" rev-list --count "$full" 2>/dev/null || echo 0)" +# The count must be a real count. Falling back to 0 when the commit is not in +# this clone would mint "0.1.0-0-<short>", which brew reads as older than every +# version already published -- so a release cut from a clone that had not +# fetched yet would look like a downgrade and never install. +count="$(git -C "$root" rev-list --count "$full" 2>/dev/null || true)" +[ -n "$count" ] || fail "commit $full is not in this clone, so its position in +history is unknown and the version would be a lie. Fetch it first: + git -C $root fetch origin" case "$label" in v[0-9]*.[0-9]*) version="${label#v}" ;; [0-9]*.[0-9]*) version="$label" ;; @@ -93,7 +112,7 @@ trap 'rm -rf "$tmp"' EXIT INT TERM code="$(curl -sL -o "$tmp/snap.tar.gz" -w '%{http_code}' "$url")" [ "$code" = 200 ] || fail "snapshot fetch returned HTTP $code -- is the ref pushed?" tar tzf "$tmp/snap.tar.gz" >/dev/null 2>&1 || fail "snapshot is not a valid tarball" -sha="$(shasum -a 256 "$tmp/snap.tar.gz" | cut -d' ' -f1)" +sha="$(sha256_of "$tmp/snap.tar.gz")" say "sha256 $sha" # Rewrite exactly the three stanzas, leaving every comment in place. diff --git a/contrib/homebrew/README.md b/contrib/homebrew/README.md index 4f62299..56501f0 100644 --- a/contrib/homebrew/README.md +++ b/contrib/homebrew/README.md @@ -31,13 +31,51 @@ master instead of the pinned release. higher bar: a stable tagged release, a LICENSE, notable usage, and no network access during the build. See "known rough edges" below.) +## Why source and not the signed binary + +The obvious thought, once there is a mac builder producing a signed, notarized +zip, is to point brew at that zip instead. Three reasons not to: + +- **Notarization buys nothing here.** Gatekeeper only checks binaries carrying + the `com.apple.quarantine` attribute, and the curl that fetches a formula's + tarball does not set it. (Casks are different: they manage quarantine + deliberately.) Every bottle in homebrew-core is unsigned-but-for-ad-hoc and + runs fine. The notarized zip matters for the *web* download, where the + browser does set the attribute — not for `brew install`. +- **The build is a minute and needs one thing.** zig. Formulae that take ten + minutes and drag in twenty dependencies are the ones that need binaries. +- **Source is the only path that covers Linux**, which brew also runs on. + +If the build time ever does become the complaint, the answer is **bottles**, +not a cask and not a hand-rolled binary url. A bottle is Homebrew's own binary +format: a tarball of the installed keg, listed in a `bottle do` block with a +`root_url` pointing at wherever we host them. It is *additive* — the formula +keeps its source path, and anyone whose macOS version we have not bottled +falls back to building. The cost is a matrix: bottles are tagged per macOS +version *and* arch (`arm64_sequoia`, `arm64_sonoma`, ...), each one built on +that version. With one builder mac that means one tag covered and everyone +else compiling — which is most of the work for a fraction of the benefit, +until there are more builders. + ## Releasing `slosh.rb` here is the source of truth; the tap repo gets a copy. +**This needs no mac and no brew.** `brew-release` resolves a ref, downloads the +snapshot and hashes it, so cutting a release works from the Linux box — which +is the point, because that is where releases are cut. Signed macOS binaries are +a separate pipeline (`make macos-dist`) that brew is not involved in. + ```bash +git fetch origin && git push origin v0.2.0 # brew-release pins what + # the server can serve contrib/brew-release --tap ../homebrew-slosh # pin origin/master contrib/brew-release --tap ../homebrew-slosh v0.2.0 # pin a pushed tag +``` + +Then, on a machine that has brew (either OS), prove it before pushing the tap: + +```bash brew install --build-from-source contrib/homebrew/slosh.rb brew test slosh ``` diff --git a/contrib/homebrew/slosh.rb b/contrib/homebrew/slosh.rb index c71fa9a..ae0ed1c 100644 --- a/contrib/homebrew/slosh.rb +++ b/contrib/homebrew/slosh.rb @@ -17,9 +17,9 @@ class Slosh < Formula # cgit serves a deterministic snapshot per ref: same ref, same bytes, so the # sha256 below stays honest. Pinned to a full commit id rather than a branch, # because a branch tarball changes under you and brew would keep the stale one. - url "https://git.sl0p.foo/slosh.git/snapshot/slosh-b744a90859bf248bfbea97f990668301b5fa9537.tar.gz" - version "0.1.0-185-b744a90" - sha256 "facafd86fc656a1c4fd925faa90b56b6932f7fb816bcdf3a1c55f2f43cb91c10" + url "https://git.sl0p.foo/slosh.git/snapshot/slosh-4bd545a46891b6ac5bb7599911d370978b84c234.tar.gz" + version "0.1.0-194-4bd545a" + sha256 "c720a4e1606a6ed01fb5c3cda106f37c1d36d06d0bec1e9f84a6cb74e3f83c86" head "https://git.sl0p.foo/slosh.git", branch: "master" # zig is the whole toolchain: it compiles the C, builds the vendored |
