make build-bin: on Linux, build with bun's pinned release build (closes #240) #248

Merged
clawbot merged 2 commits from issue-240-system-loader into next 2026-11-12 00:28:48 +01:00
Collaborator

Closes #240

On Linux, make build-bin compiles bin/quak with bun's own release build instead of nix's bun. nix's bun loads through a glibc in the nix store, and bun build --compile copies that loader into the binary, where nix-collect-garbage can break it. The release build uses the system loader, such as /lib64/ld-linux-x86-64.so.2 on x86-64 or /lib/ld-musl-aarch64.so.1 on Alpine arm64.

The build moved from the Makefile into script/build-bin. On Linux it downloads bun 1.3.13's archive for the machine from GitHub:

  • bun-linux-x64-baseline or bun-linux-aarch64 on glibc;
  • bun-linux-x64-musl-baseline or bun-linux-aarch64-musl where ldd reports musl, as on Alpine.

It checks the archive against the SHA-256 in the script, then compiles with that bun. Nothing is cached.

Not visible in the diff:

  • Each Linux build downloads about 38 MB. On Linux, make install needs curl and unzip, and no longer nix.
  • On Alpine, the musl bun and the ~/bin/quak it builds need the libstdc++ and libgcc packages.
  • 1.3.13 is the bun nixpkgs ships now, so Linux and macOS build with the same version.
  • Linux on an architecture bun publishes no build for stops with an error; nixpkgs has no bun there either.
  • macOS still runs bun through nix-shell -p bun. nixpkgs' bun for macOS loads ICU from the nix store, so the macOS binary has the same problem, unchecked on a Mac: #251
  • test/packaging/build-bin.test.ts covers the archive choice, the hash check and the refusal; the download and compile are untested because they need the network.

Model: opus-5-5

Closes https://git.eeqj.de/sneak/quak/issues/240 On Linux, `make build-bin` compiles `bin/quak` with bun's own release build instead of nix's bun. nix's bun loads through a glibc in the nix store, and `bun build --compile` copies that loader into the binary, where `nix-collect-garbage` can break it. The release build uses the system loader, such as `/lib64/ld-linux-x86-64.so.2` on x86-64 or `/lib/ld-musl-aarch64.so.1` on Alpine arm64. The build moved from the `Makefile` into `script/build-bin`. On Linux it downloads bun 1.3.13's archive for the machine from GitHub: - `bun-linux-x64-baseline` or `bun-linux-aarch64` on glibc; - `bun-linux-x64-musl-baseline` or `bun-linux-aarch64-musl` where `ldd` reports musl, as on Alpine. It checks the archive against the SHA-256 in the script, then compiles with that bun. Nothing is cached. Not visible in the diff: - Each Linux build downloads about 38 MB. On Linux, `make install` needs `curl` and `unzip`, and no longer nix. - On Alpine, the musl bun and the `~/bin/quak` it builds need the `libstdc++` and `libgcc` packages. - 1.3.13 is the bun nixpkgs ships now, so Linux and macOS build with the same version. - Linux on an architecture bun publishes no build for stops with an error; nixpkgs has no bun there either. - macOS still runs bun through `nix-shell -p bun`. nixpkgs' bun for macOS loads ICU from the nix store, so the macOS binary has the same problem, unchecked on a Mac: https://git.eeqj.de/sneak/quak/issues/251 - `test/packaging/build-bin.test.ts` covers the archive choice, the hash check and the refusal; the download and compile are untested because they need the network. Model: opus-5-5
clawbot added the needs-review label 2026-11-11 21:33:37 +01:00
clawbot self-assigned this 2026-11-11 21:33:37 +01:00
Author
Collaborator

Verdict: FAIL
Head: 62f1f5900d

  1. Makefile lines 38-44 (build-bin), README.md line 1917: the bun runtime that becomes bin/quak is now downloaded from the npm registry with no hash check. Later builds reuse the cached copy under ~/.bun without checking it either. REPO_POLICIES.md requires everything fetched from a remote source to be pinned by cryptographic hash, with no exceptions. Adding the download to the README's "Not pinned" list does not meet that rule. Acceptable: the build fetches the release archive of a fixed bun version for the host's architecture from bun's GitHub releases, and checks it against a SHA-256 written in the repo before use. It then builds with that bun, so the bundler and the runtime are the same version. The README lists it under pinned dependencies.
  2. Makefile line 41 (Linux-*), README.md line 141 ("On Linux it works on x86-64 only."): make install now stops with an error on Linux arm64, which it built before. The issue asks for a binary that runs on glibc Linux without /nix. It does not ask for a platform to be dropped, and sneak has not ruled the removal. Acceptable: Linux arm64 builds the same way as x86-64, from bun's pinned bun-linux-aarch64 release build. The refusal and that README sentence are removed.

Model: opus-5-5

Verdict: FAIL Head: 62f1f5900df5f761c01141323c2c2115d68f9541 1. `Makefile` lines 38-44 (`build-bin`), `README.md` line 1917: the bun runtime that becomes `bin/quak` is now downloaded from the npm registry with no hash check. Later builds reuse the cached copy under `~/.bun` without checking it either. `REPO_POLICIES.md` requires everything fetched from a remote source to be pinned by cryptographic hash, with no exceptions. Adding the download to the README's "Not pinned" list does not meet that rule. Acceptable: the build fetches the release archive of a fixed bun version for the host's architecture from bun's GitHub releases, and checks it against a SHA-256 written in the repo before use. It then builds with that bun, so the bundler and the runtime are the same version. The README lists it under pinned dependencies. 2. `Makefile` line 41 (`Linux-*`), `README.md` line 141 ("On Linux it works on x86-64 only."): `make install` now stops with an error on Linux arm64, which it built before. The issue asks for a binary that runs on glibc Linux without `/nix`. It does not ask for a platform to be dropped, and sneak has not ruled the removal. Acceptable: Linux arm64 builds the same way as x86-64, from bun's pinned `bun-linux-aarch64` release build. The refusal and that README sentence are removed. Model: opus-5-5
clawbot force-pushed issue-240-system-loader from 62f1f5900d to 279d3f6974 2026-11-11 22:09:33 +01:00 Compare
clawbot changed title from make build-bin: binary uses the system loader on Linux (closes #240) to make build-bin: on Linux, build with bun's pinned release build (closes #240) 2026-11-11 22:09:39 +01:00
Author
Collaborator
  1. Fixed: on Linux, script/build-bin downloads bun 1.3.13's release archive from GitHub, checks its SHA-256 before running it, and compiles with that bun; nothing is cached. The README lists it as pinned.
  2. Fixed: Linux arm64 builds from the pinned bun-linux-aarch64. The refusal and the README sentence are gone.
  • Judgement call: the build moved from the Makefile into script/build-bin, since make targets call script/.
  • Partly verified: the arm64 build ran under qemu user-mode emulation, not on arm64 hardware.
  • Found, not fixed: nixpkgs' bun for macOS loads ICU from the nix store, so the earlier claim that macOS is unaffected was wrong. Filed as #251.

Model: opus-5-5

1. Fixed: on Linux, `script/build-bin` downloads bun 1.3.13's release archive from GitHub, checks its SHA-256 before running it, and compiles with that bun; nothing is cached. The README lists it as pinned. 2. Fixed: Linux arm64 builds from the pinned `bun-linux-aarch64`. The refusal and the README sentence are gone. - Judgement call: the build moved from the `Makefile` into `script/build-bin`, since `make` targets call `script/`. - Partly verified: the arm64 build ran under qemu user-mode emulation, not on arm64 hardware. - Found, not fixed: nixpkgs' bun for macOS loads ICU from the nix store, so the earlier claim that macOS is unaffected was wrong. Filed as https://git.eeqj.de/sneak/quak/issues/251. Model: opus-5-5
Author
Collaborator

Verdict: FAIL
Head: 279d3f6974

  1. script/build-bin lines 36-44 (the Linux-x86_64 and Linux-aarch64 cases): on musl Linux, such as Alpine, the script downloads bun's glibc build. That bun then fails to start with not found, and make install stops. Before this change, make install built on Alpine with nix. script/bootstrap supports Alpine's apk, and the README's Entrypoints section says the scripts run in containers such as Alpine. Acceptable: on musl Linux the script builds from bun 1.3.13's musl archives (bun-linux-x64-musl-baseline, bun-linux-aarch64-musl), each checked against a SHA-256 in the script like the glibc ones. The README lists them as pinned and says that on Alpine, bun's musl build needs libstdc++ and libgcc.

Model: opus-5-5

Verdict: FAIL Head: 279d3f6974063a48df09278f066b2ada1f47d0f2 1. `script/build-bin` lines 36-44 (the `Linux-x86_64` and `Linux-aarch64` cases): on musl Linux, such as Alpine, the script downloads bun's glibc build. That bun then fails to start with `not found`, and `make install` stops. Before this change, `make install` built on Alpine with nix. `script/bootstrap` supports Alpine's `apk`, and the README's Entrypoints section says the scripts run in containers such as Alpine. Acceptable: on musl Linux the script builds from bun 1.3.13's musl archives (`bun-linux-x64-musl-baseline`, `bun-linux-aarch64-musl`), each checked against a SHA-256 in the script like the glibc ones. The README lists them as pinned and says that on Alpine, bun's musl build needs `libstdc++` and `libgcc`. Model: opus-5-5
clawbot force-pushed issue-240-system-loader from 279d3f6974 to 0b97156c00 2026-11-11 22:51:54 +01:00 Compare
Author
Collaborator
  1. Fixed: on musl Linux, script/build-bin builds from the pinned bun-linux-x64-musl-baseline or bun-linux-aarch64-musl archive; the README lists both as pinned and names libstdc++ and libgcc for Alpine.
  • Judgement call: musl is detected by ldd --version output that begins with musl libc; a Linux without ldd gets the glibc build.
  • Partly verified: on arm64 the musl compile ran under qemu user-mode emulation against an Alpine root, not on arm64 hardware.

Model: opus-5-5

1. Fixed: on musl Linux, `script/build-bin` builds from the pinned `bun-linux-x64-musl-baseline` or `bun-linux-aarch64-musl` archive; the README lists both as pinned and names `libstdc++` and `libgcc` for Alpine. - Judgement call: musl is detected by `ldd --version` output that begins with `musl libc`; a Linux without `ldd` gets the glibc build. - Partly verified: on arm64 the musl compile ran under qemu user-mode emulation against an Alpine root, not on arm64 hardware. Model: opus-5-5
Author
Collaborator

Verdict: FAIL
Head: 0b97156c00

  1. No test covers script/build-bin. The README's "Development workflow" says all work on quak is test-driven, and the repo tests its other script/ entrypoints under test/packaging/. The PR body says a test would need the network, but the choice of archive and the hash check can both be tested without it. Acceptable: a new test file in test/packaging/ that works like test/packaging/version.test.ts. It copies script/build-bin into a temporary checkout with a stub script/build, and puts stub uname, ldd, curl and nix-shell first on PATH. The stub curl records the URL it was given and writes a file that is not the archive. The tests check that:
    • each of the four Linux combinations of machine and C library requests its own archive;
    • an archive whose SHA-256 does not match stops the script with a non-zero exit and leaves no bin/quak;
    • Linux on any other architecture exits non-zero;
    • a system other than Linux runs bun build through nix-shell -p bun.
  • Judgement call, nothing to change: on NixOS, whose default setup cannot start a downloaded bun, make install now stops with an error. The repo names no NixOS support. Not checked on NixOS.
  • Not run here: the two arm64 builds.

Model: opus-5-5

Verdict: FAIL Head: 0b97156c0032fdc962ae28b52555ecd66841200d 1. No test covers `script/build-bin`. The README's "Development workflow" says all work on quak is test-driven, and the repo tests its other `script/` entrypoints under `test/packaging/`. The PR body says a test would need the network, but the choice of archive and the hash check can both be tested without it. Acceptable: a new test file in `test/packaging/` that works like `test/packaging/version.test.ts`. It copies `script/build-bin` into a temporary checkout with a stub `script/build`, and puts stub `uname`, `ldd`, `curl` and `nix-shell` first on `PATH`. The stub `curl` records the URL it was given and writes a file that is not the archive. The tests check that: - each of the four Linux combinations of machine and C library requests its own archive; - an archive whose SHA-256 does not match stops the script with a non-zero exit and leaves no `bin/quak`; - Linux on any other architecture exits non-zero; - a system other than Linux runs `bun build` through `nix-shell -p bun`. - Judgement call, nothing to change: on NixOS, whose default setup cannot start a downloaded bun, `make install` now stops with an error. The repo names no NixOS support. Not checked on NixOS. - Not run here: the two arm64 builds. Model: opus-5-5
Author
Collaborator

For the reworker:

The script/build-bin, Makefile and README.md changes are settled. The only open finding is the third review's: no test covers script/build-bin. The rework adds that test and edits the PR body, nothing else.

Change

  1. Rebase onto current next.
  2. Add test/packaging/build-bin.test.ts, built like test/packaging/version.test.ts:
    • A header comment says what script/build-bin promises and why each case below matters.
    • Each test makes a checkout with mkdtempSync. It holds a copy of script/build-bin and a stub script/build that exits 0.
    • A directory of stubs goes first on PATH:
      • uname prints the test's system for -s and its machine for -m;
      • ldd prints musl libc (x86_64) in a musl case and ldd (GNU libc) 2.36 otherwise;
      • curl logs the URL it was given and writes a file that is not the archive to its -o path;
      • unzip logs that it ran and unpacks into its -d directory a stub bun that writes bin/quak;
      • nix-shell logs its arguments.
    • The script runs with only PATH and the stubs' settings in its environment, as runVersion does.
    • The tests:
      • For each of the four Linux cases (x86_64 or aarch64, glibc or musl), as one it.each: curl was asked for https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/ plus bun-linux-x64-baseline.zip, bun-linux-x64-musl-baseline.zip, bun-linux-aarch64.zip or bun-linux-aarch64-musl.zip. The script exits non-zero, unzip never ran, and there is no bin/quak. The stub unzip is what makes this a test of the hash check: a script without the check would leave bin/quak behind.
      • Linux on riscv64: exits non-zero; neither curl nor nix-shell ran.
      • Darwin on arm64: exits 0; curl never ran; nix-shell got -p bun --run "bun build dist/bin/quak.js --compile --outfile bin/quak".
    • No network, no real bun, and no host curl, unzip or nix-shell. These stubs work in the test phase, on Alpine as the node user.
  3. Commit the test on its own, with a Model: line.
  4. In the PR body, replace the bullet "No automated test: …" with one line: test/packaging/build-bin.test.ts covers the archive choice, the hash check and the refusal; the download and compile are untested because they need the network. Keep the body at or under 250 words; it is 251 now.

Leave alone

  • script/build-bin, Makefile and README.md: no edits.
  • The NixOS behaviour, musl detection through ldd, and macOS building through nix-shell -p bun. The macOS loader problem is #251, not this PR.
  • No test that downloads bun or compiles a binary.
  • The existing commit: no rewording or splitting.

The next review checks only

  • The head rebases cleanly onto current next, and make check is green there.
  • The new test covers the cases above. It fails when script/build-bin loses its sha256sum -c line, requests the wrong archive in one case, or builds on another Linux machine instead of refusing.
  • Since 0b97156, the diff adds only the test file. The PR body has the new line, stays at or under 250 words and ends with a Model: line, and so does every commit.
  • Code unchanged since 0b97156 is settled for this PR. Anything new found in it goes on a new issue, not into a failed review.

Model: opus-5-5

For the reworker: The `script/build-bin`, `Makefile` and `README.md` changes are settled. The only open finding is the third review's: no test covers `script/build-bin`. The rework adds that test and edits the PR body, nothing else. **Change** 1. Rebase onto current `next`. 2. Add `test/packaging/build-bin.test.ts`, built like `test/packaging/version.test.ts`: - A header comment says what `script/build-bin` promises and why each case below matters. - Each test makes a checkout with `mkdtempSync`. It holds a copy of `script/build-bin` and a stub `script/build` that exits 0. - A directory of stubs goes first on `PATH`: - `uname` prints the test's system for `-s` and its machine for `-m`; - `ldd` prints `musl libc (x86_64)` in a musl case and `ldd (GNU libc) 2.36` otherwise; - `curl` logs the URL it was given and writes a file that is not the archive to its `-o` path; - `unzip` logs that it ran and unpacks into its `-d` directory a stub `bun` that writes `bin/quak`; - `nix-shell` logs its arguments. - The script runs with only `PATH` and the stubs' settings in its environment, as `runVersion` does. - The tests: - For each of the four Linux cases (`x86_64` or `aarch64`, glibc or musl), as one `it.each`: `curl` was asked for `https://github.com/oven-sh/bun/releases/download/bun-v1.3.13/` plus `bun-linux-x64-baseline.zip`, `bun-linux-x64-musl-baseline.zip`, `bun-linux-aarch64.zip` or `bun-linux-aarch64-musl.zip`. The script exits non-zero, `unzip` never ran, and there is no `bin/quak`. The stub `unzip` is what makes this a test of the hash check: a script without the check would leave `bin/quak` behind. - Linux on `riscv64`: exits non-zero; neither `curl` nor `nix-shell` ran. - `Darwin` on `arm64`: exits 0; `curl` never ran; `nix-shell` got `-p bun --run "bun build dist/bin/quak.js --compile --outfile bin/quak"`. - No network, no real bun, and no host `curl`, `unzip` or `nix-shell`. These stubs work in the `test` phase, on Alpine as the `node` user. 3. Commit the test on its own, with a `Model:` line. 4. In the PR body, replace the bullet "No automated test: …" with one line: `test/packaging/build-bin.test.ts` covers the archive choice, the hash check and the refusal; the download and compile are untested because they need the network. Keep the body at or under 250 words; it is 251 now. **Leave alone** - `script/build-bin`, `Makefile` and `README.md`: no edits. - The NixOS behaviour, musl detection through `ldd`, and macOS building through `nix-shell -p bun`. The macOS loader problem is https://git.eeqj.de/sneak/quak/issues/251, not this PR. - No test that downloads bun or compiles a binary. - The existing commit: no rewording or splitting. **The next review checks only** - The head rebases cleanly onto current `next`, and `make check` is green there. - The new test covers the cases above. It fails when `script/build-bin` loses its `sha256sum -c` line, requests the wrong archive in one case, or builds on another Linux machine instead of refusing. - Since `0b97156`, the diff adds only the test file. The PR body has the new line, stays at or under 250 words and ends with a `Model:` line, and so does every commit. - Code unchanged since `0b97156` is settled for this PR. Anything new found in it goes on a new issue, not into a failed review. Model: opus-5-5
clawbot added 2 commits 2026-11-11 23:53:02 +01:00
nix's bun loads through a glibc in the nix store, and bun build --compile
copied that loader into bin/quak, so nix-collect-garbage could break the
cron backup. The build moves into script/build-bin. On Linux it downloads
bun 1.3.13's release archive for the machine from GitHub: x86-64
(baseline) or arm64, glibc or musl. It checks the archive's SHA-256
before running it, and compiles with that bun, so the binary loads
through the system loader. macOS still runs bun through nix-shell.

Model: opus-5-5
The test runs the script with stub uname, ldd, curl, unzip and nix-shell
first on PATH, so it needs no network and no bun. The download and the
compile stay untested.

Model: opus-5-5
clawbot force-pushed issue-240-system-loader from 0b97156c00 to 685f1d584c 2026-11-11 23:53:02 +01:00 Compare
Author
Collaborator
  1. Rebased onto next.
  2. Added test/packaging/build-bin.test.ts.
  3. Committed on its own: 685f1d5.
  4. The body has the test line and is at 250 words; to fit, I cut "now", "which make build-bin calls" and "into a temporary directory".

Model: opus-5-5

1. Rebased onto `next`. 2. Added `test/packaging/build-bin.test.ts`. 3. Committed on its own: `685f1d5`. 4. The body has the test line and is at 250 words; to fit, I cut "now", "which `make build-bin` calls" and "into a temporary directory". Model: opus-5-5
Author
Collaborator

Verdict: PASS
Head: 685f1d584c

test/packaging/build-bin.test.ts covers the archive choice for each Linux machine and C library, the hash check and the refusal, as the referee's instructions asked, and nothing else changed since the last review.

Model: opus-5-5

Verdict: PASS Head: 685f1d584c8ecff0569b4ea50b8fee959cac2dee `test/packaging/build-bin.test.ts` covers the archive choice for each Linux machine and C library, the hash check and the refusal, as the referee's instructions asked, and nothing else changed since the last review. Model: opus-5-5
clawbot merged commit 50e2381cad into next 2026-11-12 00:28:48 +01:00
Sign in to join this conversation.