From 73a77c82b3464633e8d4a234686466683ee7930d Mon Sep 17 00:00:00 2001 From: Padreug Date: Thu, 24 Sep 2026 15:26:58 +0200 Subject: [PATCH] perf(deploy): stop the app closure retaining its build toolchain node-gyp leaves its scaffolding beside the addons it compiles, and several of those files carry absolute store paths to the tools that did the compiling: build/node_gyp_bins/python3 is an ELF copy of python3 with an RPATH into it, build/config.gypi names python3, nodejs and npm, the .o.d files under build/Release/.deps name pcsclite's dev output, and pnpm rewrote a few CLI helpers' shebangs to the full nodejs. Nix scans $out for store hashes, so each of those became a runtime reference. Every ATM was carrying python311, nodejs, npm and pcsclite.dev -- 212MB of closure -- for files nothing reads after the build. Only build/Release/*.node is ever loaded, through bindings and node-gyp-build. Drop the scaffolding, and point the stray shebangs at PATH rather than deleting files a package might still require. All three addons survive with their RPATHs intact: better_sqlite3.node, pcsclite.node and the serialport prebuilds. The derivation's references are now down to bash, pcsclite.lib and the two gcc runtime libs. Co-Authored-By: Claude Fable 5.1 --- nix/mkAtmApp.nix | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/nix/mkAtmApp.nix b/nix/mkAtmApp.nix index 527d259..3e1102d 100644 --- a/nix/mkAtmApp.nix +++ b/nix/mkAtmApp.nix @@ -190,6 +190,33 @@ pkgs.stdenv.mkDerivation (finalAttrs: { copy_pnpm_pkg "@serialport/$parser" "$out/node_modules/@serialport/$parser" done + # ── Strip node-gyp build detritus ────────────────────────────────── + # node-gyp leaves its scaffolding beside the compiled addons, and several + # of those files embed absolute /nix/store paths to the BUILD toolchain: + # build/node_gyp_bins/python3 an ELF copy of python3 with an RPATH + # build/config.gypi python3 + nodejs + npm paths + # build/Release/.deps/**.o.d pcsclite.dev include paths + # Nix scans $out for store hashes, so each becomes a RUNTIME reference and + # drags python311 + nodejs + npm + pcsclite.dev (~212MB of closure) onto + # every ATM. Nothing reads them at runtime — only build/Release/*.node is + # loaded, via `bindings` / `node-gyp-build`. Keep the addons, drop the + # scaffolding. obj.target/*.node is node-gyp's pre-copy of the same addon; + # the loaded one at build/Release/*.node is untouched. + find $out/node_modules -type d \ + \( -name node_gyp_bins -o -name .deps -o -name obj.target -o -name obj \) \ + -prune -exec rm -rf {} + + find $out/node_modules -path '*/build/*' -type f \ + \( -name config.gypi -o -name '*.mk' -o -name Makefile \ + -o -name binding.Makefile -o -name '*.a' -o -name '*.o' \) -delete + + # pnpm/node-gyp rewrote these CLI helpers' shebangs to the build nodejs, + # which alone retains the full nodejs (not the slim one Electron needs). + # They are build-time utilities — the runtime entry of each package + # (index.js) carries no shebang — so point them at PATH instead of + # deleting files a package might still require. + find $out/node_modules -type f -name '*.js' \ + -exec sed -i '1s|^#!/nix/store/[^ ]*/bin/node$|#!/usr/bin/env node|' {} + + runHook postInstall '';