Retire staged Yarn dependencies during Nix installation (#473)

This commit is contained in:
Quixos Development Agent 01f26965-84fa-4a79-8069-725682c13065
2026-10-29 12:30:56 +00:00
parent 43fcf6b27a
commit a962586381
4 changed files with 70 additions and 1 deletions
+6
View File
@@ -69,6 +69,12 @@ describes its install-state file as an optimization cache in its
This helper output is a dependency directory for a build, not a standalone
Package or a runnable Yarn project. Its `devShell` supplies freestanding Yarn for
editable projects; do not run Yarn against the readonly dependency output.
Installation discards its disposable archive cache and streams dependencies into
the output while retiring staged files, including across separate build/store
mounts. It preserves package layout, native results, hidden data and executable
links without holding two complete dependency trees. The maintained
`camino-yarn-dependencies` check covers transfer and runtime layout; consuming
Studio and Package-builder checks retain real locked-installation coverage.
Use the shared `installDependencies` assembly for consumers: authored libraries
are copied into the consumer's `node_modules`, where Node can find their
+31
View File
@@ -0,0 +1,31 @@
# Tiny generated-project interface fixture. Real offline Yarn installation
# remains covered by the consuming Studio and Package-builder checks.
{ stdenvNoCC, nodejs }:
{ src, overrideAttrs ? null }:
let
project = stdenvNoCC.mkDerivation {
name = "yarn-dependencies-transfer-fixture";
inherit src;
configurePhase = ''
mkdir -p node_modules/fixture node_modules/.bin .yarn/cache
printf '%s\n' '{"main":"index.cjs"}' > node_modules/fixture/package.json
printf '%s\n' 'module.exports = {value: "retained dependency"};' > node_modules/fixture/index.cjs
ln node_modules/fixture/index.cjs node_modules/fixture/twin.cjs
ln -s fixture node_modules/alias
printf '%s\n' '#!${nodejs}/bin/node' 'console.log("linked executable");' > node_modules/fixture/cli.cjs
chmod +x node_modules/fixture/cli.cjs
ln -s ../fixture/cli.cjs node_modules/.bin/fixture
printf '%s\n' 'hidden dependency data' > node_modules/.fixture-data
printf '%s\n' 'disposable linker state' > node_modules/.yarn-state.yml
printf '%s\n' 'disposable archive' > .yarn/cache/fixture.zip
'';
postInstall = ''
test ! -e node_modules
test ! -e .yarn/cache
test ! -e "$out/.yarn-state.yml"
test ! -e "$out/node_modules"
test "$(stat -c %i "$out/fixture/index.cjs")" = "$(stat -c %i "$out/fixture/twin.cjs")"
'';
};
in
if overrideAttrs == null then project else project.overrideAttrs overrideAttrs
+9 -1
View File
@@ -9,10 +9,18 @@ let
overrideAttrs = _: {
buildPhase = "runHook preBuild; runHook postBuild";
installPhase = ''
set -o pipefail
runHook preInstall
test -d node_modules
test ! -L node_modules
mkdir -p "$out"
cp -a node_modules/. "$out/"
# Installation is finished: retire its disposable archive cache, then
# stream dependencies across the build/store mount boundary without
# retaining a second complete tree. This is the same installation
# policy used by Central's dependency derivations.
rm -rf .yarn/cache
${pkgs.gnutar}/bin/tar --create --file=- --remove-files node_modules \
| ${pkgs.gnutar}/bin/tar --extract --file=- --strip-components=1 --directory="$out"
# Node resolves installed packages directly. This generated index is for
# Yarn's linker, not a runtime input; do not export build-session state.
rm -f "$out/.yarn-state.yml"
+24
View File
@@ -0,0 +1,24 @@
{ pkgs }:
let
dependencies = import ./yarn-dependencies.nix {
inherit pkgs;
src = ./test/yarn-dependencies;
};
in
pkgs.runCommand "quixos-yarn-dependencies-check" { nativeBuildInputs = [ pkgs.nodejs_24 ]; } ''
node - ${dependencies} <<'JS'
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const {execFileSync} = require('node:child_process');
const root = process.argv[2];
assert.deepEqual(require(path.join(root, 'fixture')), {value: 'retained dependency'});
assert.deepEqual(require(path.join(root, 'alias')), {value: 'retained dependency'});
assert.equal(fs.readlinkSync(path.join(root, 'alias')), 'fixture');
assert.equal(fs.readlinkSync(path.join(root, '.bin/fixture')), '../fixture/cli.cjs');
assert.equal(execFileSync(path.join(root, '.bin/fixture'), {encoding: 'utf8'}), 'linked executable\n');
assert.equal(fs.readFileSync(path.join(root, '.fixture-data'), 'utf8'), 'hidden dependency data\n');
assert.equal(fs.existsSync(path.join(root, '.yarn-state.yml')), false);
JS
touch "$out"
''