d62bdb97
build: make release, tarred, so the exec bit survives
a73x 2026-08-21 08:33
Commit message
Makefile
| Old | New | ||
|---|---|---|---|
| @@ -2,7 +2,7 @@ | |||
| 2 | # default zig is 0.17-dev. Override with ZIG=... if yours lives elsewhere. | 2 | # default zig is 0.17-dev. Override with ZIG=... if yours lives elsewhere. |
| 3 | ZIG ?= $(HOME)/Downloads/zig-x86_64-linux-0.15.2/zig | 3 | ZIG ?= $(HOME)/Downloads/zig-x86_64-linux-0.15.2/zig |
| 4 | 4 | ||
| 5 | .PHONY: build test e2e soak bench agent deps clean clean-deps xversion xversion-build install | 5 | .PHONY: build test e2e soak bench agent deps clean clean-deps xversion xversion-build install release |
| 6 | 6 | ||
| 7 | # The QUIC stack (deps/quic) is built on demand by build.zig, so no target | 7 | # The QUIC stack (deps/quic) is built on demand by build.zig, so no target |
| 8 | # here needs to depend on this one. It exists to make the one-time cost | 8 | # here needs to depend on this one. It exists to make the one-time cost |
| @@ -24,6 +24,46 @@ install: build | |||
| 24 | install -d $(BINDIR) | 24 | install -d $(BINDIR) |
| 25 | install -m755 zig-out/bin/muxd zig-out/bin/mux zig-out/bin/muxa zig-out/bin/muxweb $(BINDIR)/ | 25 | install -m755 zig-out/bin/muxd zig-out/bin/mux zig-out/bin/muxa zig-out/bin/muxweb $(BINDIR)/ |
| 26 | 26 | ||
| 27 | # The artifact a release publishes, built the way every release since | ||
| 28 | # v0.0.1-4 has been: static musl so one binary runs on any x86_64 Linux, | ||
| 29 | # ReleaseSafe because the safety checks are worth more than the last few | ||
| 30 | # percent and they survive stripping — only DWARF goes, 17MB to ~3MB each. | ||
| 31 | # | ||
| 32 | # TARRED, and that is the point of having a target at all. Publishing the | ||
| 33 | # four binaries bare loses the exec bit: they download 0644, and the first | ||
| 34 | # thing anyone does on a fresh box is a chmod nothing documents. A tarball | ||
| 35 | # carries the mode, so `tar xzf - -C ~/.local/bin` over ssh is the whole | ||
| 36 | # remote install. | ||
| 37 | # | ||
| 38 | # Flat rather than a versioned directory inside, for the same reason: the | ||
| 39 | # common move is putting muxd on a box, and one pipe should finish it. | ||
| 40 | VERSION := $(shell sed -n 's/^[[:space:]]*const version = "\(.*\)";/\1/p' build.zig | head -1) | ||
| 41 | RELDIR ?= dist | ||
| 42 | RELBIN = $(RELDIR)/v$(VERSION) | ||
| 43 | RELTAR = $(RELDIR)/mux-v$(VERSION)-x86_64-linux-musl.tar.gz | ||
| 44 | # The version guard below asks the STRIPPED artifacts what they are, so a | ||
| 45 | # stale stage directory cannot ship under a bumped number. muxa has no | ||
| 46 | # --version (it is verbs and JSON), so it is built and shipped but not asked. | ||
| 47 | release: | ||
| 48 | @test -n "$(VERSION)" || { echo "release: no version found in build.zig"; exit 1; } | ||
| 49 | $(ZIG) build -Dtarget=x86_64-linux-musl -Doptimize=ReleaseSafe -p $(RELDIR)/stage | ||
| 50 | rm -rf $(RELBIN) $(RELTAR) | ||
| 51 | install -d $(RELBIN) | ||
| 52 | install -m755 $(RELDIR)/stage/bin/muxd $(RELDIR)/stage/bin/mux \ | ||
| 53 | $(RELDIR)/stage/bin/muxa $(RELDIR)/stage/bin/muxweb $(RELBIN)/ | ||
| 54 | strip $(RELBIN)/muxd $(RELBIN)/mux $(RELBIN)/muxa $(RELBIN)/muxweb | ||
| 55 | @for b in muxd mux muxweb; do \ | ||
| 56 | got=$$($(RELBIN)/$$b --version | awk '{print $$2}'); \ | ||
| 57 | [ "$$got" = "$(VERSION)" ] || { \ | ||
| 58 | echo "release: $$b reports $$got, expected $(VERSION)"; exit 1; }; \ | ||
| 59 | done | ||
| 60 | tar czf $(RELTAR) -C $(RELBIN) muxd mux muxa muxweb | ||
| 61 | @echo | ||
| 62 | @sha256sum $(RELTAR) | ||
| 63 | @echo "install: tar xzf $(RELTAR) -C ~/.local/bin" | ||
| 64 | @echo "remote: ssh HOST 'mkdir -p ~/.local/bin && tar xzf - -C ~/.local/bin' < $(RELTAR)" | ||
| 65 | @echo "publish: git collab release publish v$(VERSION) $(RELTAR)" | ||
| 66 | |||
| 27 | test: | 67 | test: |
| 28 | $(ZIG) build test | 68 | $(ZIG) build test |
| 29 | 69 | ||
README.md
| Old | New | ||
|---|---|---|---|
| @@ -25,6 +25,16 @@ that runs on any x86_64 Linux: | |||
| 25 | ~/Downloads/zig-x86_64-linux-0.15.2/zig build -Dtarget=x86_64-linux-musl | 25 | ~/Downloads/zig-x86_64-linux-0.15.2/zig build -Dtarget=x86_64-linux-musl |
| 26 | ``` | 26 | ``` |
| 27 | 27 | ||
| 28 | `make release` does the whole release build instead — static musl, | ||
| 29 | `ReleaseSafe`, stripped, checked against `build.zig`'s version, and tarred | ||
| 30 | into `dist/`. The tarball is what to hand another machine, because it | ||
| 31 | carries the exec bit that a bare downloaded binary does not: | ||
| 32 | |||
| 33 | ```sh | ||
| 34 | make release | ||
| 35 | ssh HOST 'mkdir -p ~/.local/bin && tar xzf - -C ~/.local/bin' < dist/mux-vVERSION-x86_64-linux-musl.tar.gz | ||
| 36 | ``` | ||
| 37 | |||
| 28 | ## Quick start, local | 38 | ## Quick start, local |
| 29 | 39 | ||
| 30 | ```sh | 40 | ```sh |
docs/decisions.md
| Old | New | ||
|---|---|---|---|
| @@ -5132,3 +5132,20 @@ in a local session worked with no flag. The daemon's socket now overwrites | |||
| 5132 | it, and a local session needs `-A` where it previously needed nothing. The | 5132 | it, and a local session needs `-A` where it previously needed nothing. The |
| 5133 | overwrite is the point — otherwise every client shares one identity — but it | 5133 | overwrite is the point — otherwise every client shares one identity — but it |
| 5134 | is a regression for one workflow and is now documented as one. | 5134 | is a regression for one workflow and is now documented as one. |
| 5135 | |||
| 5136 | **Releases are tarred, and `make release` is why there is a rule.** From | ||
| 5137 | v0.0.1-4 through v0.0.1-10 a release was one `mux-vX-x86_64-linux-musl.tar.gz`; | ||
| 5138 | v0.0.1-11 published the four binaries bare, with no decision recorded anywhere | ||
| 5139 | and nothing in the repo consuming either shape. The bare form costs the exec | ||
| 5140 | bit — a downloaded binary arrives 0644, so `mux HOST` on a freshly installed | ||
| 5141 | box dies with `Permission denied` from the remote `muxd` and reports it as | ||
| 5142 | `no endpoint announce (UnterminatedLine)`, which names neither the file nor | ||
| 5143 | the mode. Observed on a LAN box the day v0.0.1-12-pre1 went out. | ||
| 5144 | |||
| 5145 | So: tar, flat, one `tar xzf - -C ~/.local/bin` over ssh to install, and the | ||
| 5146 | recipe lives in the Makefile rather than in a memory of how the last one was | ||
| 5147 | built. The target asks the STRIPPED artifacts their `--version` and refuses | ||
| 5148 | to ship if it disagrees with `build.zig` — a stale stage directory under a | ||
| 5149 | bumped number is the failure a hand-run recipe actually has. The binaries are | ||
| 5150 | reproducible (a rebuild matched the published sha256s byte for byte); the | ||
| 5151 | gzip wrapper is not, since it embeds an mtime. | ||