test(nix): drive the installed binary through the shared tier (task 0038) #47
Reference in New Issue
Block a user
Delete Branch "task-0038-nix-build-drives-installed-binary"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements
.claude/tasks/0038-nix-build-drives-installed-binary.md.What was built
The Nix build now drives the binary it has just installed through the shared installed-binary tier, so
nix buildandnpm run test:packguarantee the same things about an installed gitea-axi.The check lives in
installCheckPhase, which runs afterfixupPhase— so the binary under test is the wrapped one an operator actually gets, not the bare entrypoint. It setsGITEA_AXI_INSTALLED_BIN=$out/bin/gitea-axiand runsnpm run test:installed, a new script running the installed-binary facet alone. Naming the binary is all the phase does; the assertions stay in the tier.npmInstallHookprunes dev dependencies out of the build tree duringinstallPhase, so vitest is gone by the time the check runs.preInstallsnapshotsnode_moduleswithcp -al(hardlinks — free, and the prune's deletions don't follow them) and the check restores it by copying, so a replayed phase doesn't consume the only snapshot.vitest.packaging.config.tsjoined the source allowlist — exactly the Gotcha task 0037 recorded, hit on the first build.Both negative criteria were confirmed by deliberate sabotage probes: stripping the executable bit fails all three tests with
EACCES, and moving the installedskillsdirectory aside fails thesetuptest.nix build --rebuildnow passes too, so the output is bit-for-bit reproducible.Deviations
Criterion 2 holds, but by a different mechanism than the task assumed. The task motivates the executable-bit guard with "the compiler does not set the executable bit that npm would otherwise set from the manifest's
binentry". That's true of the npm path and not of the Nix path:nodejsInstallExecutablesinstalls eachbinentry as a generated wrapper invokingnode <path>, not as a symlink. Sochmod -x dist/main.jschanges nothing (verified — the build stayed green), whilechmod -x $out/bin/gitea-axifails. The criterion as written is satisfied and was demonstrated, but the bit it protects under Nix is onemakeWrapperalways sets. That's an argument for the shared tier rather than against it: neither path picks which guarantees it feels like offering.Two defects fixed in passing, both surfaced by review rather than planned:
test:installedpins a test file by path while the packaging runner setspassWithNoTests: true— moving that file would have takennix buildgreen having asserted nothing, the same silently-inert trapdoChecksprang in task 0037. Now passes--passWithNoTests=false.checkPhase's vitest left a timestamped run cache undernode_modules/.vite, whichnpmInstallHookcopied into$out— shipping a stray cache and making the derivation non-reproducible. Strictly a 0037 defect, but this task adds a second vitest run over the same surface and the fix is one line in a file already being edited.Scope note:
.gitignoregainsresult/result-*, which is 0037's flake output rather than this task's.Review
Risk
Overall: LOW
nix flake check) and negatively (two sabotage probes).nix buildloudly rather than shipping a defect.Unaddressed findings
Standards —
test:packandtest:installedshare thevitest run --config vitest.packaging.config.tsprefix (Duplicated Code, judgement call). Left alone: the reviewer itself put it below the extraction threshold, and npm scripts have no good extraction mechanism short of a wrapper script that would obscure both.Spec — criterion 2 is arguably vacuous under Nix and "should be re-scoped or marked as covered by
test:packrather than bynix build". Left as[x]rather than[-]: the criterion as literally written was demonstrated by probe. The nuance is recorded in the task's Implementation Notes, in apackage.nixcomment, and as a CLAUDE.md Gotcha, which seemed better than silently rewriting an acceptance criterion after the fact.Everything else the review raised is fixed in the diff.