Skip to content

Install what a release publishes, on a machine that has nothing on it - #11

Merged
tamnd merged 1 commit into
mainfrom
clean-install
Aug 19, 2026
Merged

tamnd merged 1 commit into
mainfrom
clean-install

Conversation

@tamnd

@tamnd tamnd commented Aug 19, 2026

Copy link
Copy Markdown
Owner

Every job in this repository runs on a hosted image, and a hosted image is the least representative computer in the world. It has a compiler, a Rust toolchain, a git, a Python, and a few hundred libraries an addon can quietly link against and get away with. So the failures that only a user's machine sees are exactly the ones nothing here looks for. A file left out of files and loaded out of the checkout instead, where every path exists. An addon linked against a symbol version the build image had and a slim image has not. A platform package whose os, cpu or libc says the wrong thing, so npm installs it where it cannot load and skips it where it would have worked.

This is the DX3 line that asks for a clean-machine install test per SDK, nightly, in a container with only the language runtime.

What it does

A nightly that builds the two Linux binaries a release would build, in the same images and with the same flags as release.yml, and then installs what a release would publish in a container that holds a runtime, a package manager and nothing else.

image platform package
node:24-slim zudb-linux-x64-gnu
node:26-slim zudb-linux-x64-gnu
node:24-alpine zudb-linux-x64-musl

The version this package requires, the current release, and musl, which is where a platform package with the wrong libc in it installs anyway and then fails at the require. npm is offline, so nothing can arrive from the registry to cover for a mistake.

docker run rather than a job container, because a job container has the runner's own Node mounted into it and half the reason to use a slim image is that nothing is mounted into it.

What runs inside

tools/install.mjs, which is the program the release workflow already runs and for the same reason. It packs the two tarballs npm would fetch, installs them into an empty project outside the checkout, and runs a statement through both module formats and through the stream, which is the one part of the surface written in JavaScript over the addon rather than by it. Nothing new had to be written for this. The difference is the machine rather than the program.

The image is checked rather than believed

Every row asserts that cc, gcc, clang, rustc, cargo, make, git, python3 and node-gyp are all absent. The day a base image starts shipping a compiler is the day this job silently stops being about anything, and node-gyp is named because an install that falls back to building from source is an install this package promises never happens.

The gate is validated

The binary taken back out of the platform package has to fail the install. That is what one which did not build, did not upload or did not install looks like from inside the container, and it is also the only way to know the job is running the program at all: a bind mount pointing at nothing and a container whose exit code went nowhere both look exactly like success.

Verified locally against colima that none of the three images carries any of the nine tools, that hostPlatform() reads musl correctly inside node:24-alpine, and that the negative case exits 1 with the message naming the package and the file it wanted.

Why nightly

What this catches is drift in things outside this repository. A base image whose glibc moved, a musl that went forward, an npm that changed how it reads libc. None of that arrives in a diff anybody here writes. It runs on the pull request that changes the workflow, the program, the manifest or the platform packages too, since a nightly that broke is a nightly nobody reads for a week.

Every job in this repository runs on a hosted image, and a hosted image
is the least representative computer in the world. It has a compiler, a
Rust toolchain, a git, a Python, and a few hundred libraries an addon
can quietly link against and get away with. So the failures that only a
user's machine sees are exactly the ones nothing here looks for. A file
left out of `files` and loaded out of the checkout instead, where every
path exists. An addon linked against a symbol version the build image
had and a slim image has not. A platform package whose `os`, `cpu` or
`libc` says the wrong thing, so npm installs it where it cannot load and
skips it where it would have worked.

So: a nightly that builds the two Linux binaries a release would build,
in the same images and with the same flags, and then installs what a
release would publish in a container that holds a runtime, a package
manager and nothing else. Three rows, which are the version this package
requires, the current release, and musl, where a platform package with
the wrong `libc` in it installs anyway and then fails at the require.
npm is offline, so nothing can arrive from the registry to cover for a
mistake.

What runs inside is tools/install.mjs, which is the program the release
workflow already runs and for the same reason. It packs the two tarballs
npm would fetch, installs them into an empty project outside the
checkout, and runs a statement through both module formats and through
the stream, which is the one part of the surface written in JavaScript
over the addon rather than by it. Nothing new had to be written for
this: the difference is the machine rather than the program.

The images are checked rather than believed. Every row asserts that cc,
gcc, clang, rustc, cargo, make, git, python3 and node-gyp are all
absent, because the day a base image starts shipping a compiler is the
day this job silently stops being about anything, and node-gyp in
particular is named because an install that falls back to building from
source is an install this package promises never happens.

The gate is validated the way the other gates here are: the failure it
exists to catch has to fail it. The binary taken back out of the
platform package, which is what one that did not build, did not upload
or did not install looks like from inside the container, and which is
also the only way to know the job is running the program at all. A bind
mount pointing at nothing and a container whose exit code went nowhere
both look exactly like success.

Nightly rather than on every push. What this catches is drift in things
outside this repository, a base image whose glibc moved, a musl that
went forward, an npm that changed how it reads `libc`, and none of that
arrives in a diff anybody here writes.
@tamnd
tamnd merged commit 55d06ab into main Aug 19, 2026
18 checks passed
@tamnd
tamnd deleted the clean-install branch August 19, 2026 01:58
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.

1 participant