fetchPnpmDeps: fix reproducibility of pnpm v11 store index (#522703)

This commit is contained in:
Sefa Eyeoglu 2026-05-24 09:38:04 +00:00 committed by GitHub
commit f9679155c9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 162 additions and 6 deletions

View file

@ -342,7 +342,7 @@ stdenv.mkDerivation (finalAttrs: {
pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) pname version src;
inherit pnpm;
fetcherVersion = 3;
fetcherVersion = 4;
hash = "...";
};
})
@ -384,7 +384,7 @@ It is highly recommended to use a pinned version of pnpm (i.e., `pnpm_9` or `pnp
pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) pname version src;
+ pnpm = pnpm_10;
fetcherVersion = 3;
fetcherVersion = 4;
hash = "...";
};
})
@ -498,7 +498,7 @@ This is the version of the output of `fetchPnpmDeps`. New packages should use `3
# ...
pnpmDeps = fetchPnpmDeps {
# ...
fetcherVersion = 3;
fetcherVersion = 4;
hash = "..."; # clear this hash and generate a new one
};
}
@ -516,6 +516,7 @@ Version 3 is the recommended value for new packages. Versions 1 and 2 are deprec
- 1: Initial version, nothing special.
- 2: [Ensure consistent permissions](https://github.com/NixOS/nixpkgs/pull/422975)
- 3: [Build a reproducible tarball](https://github.com/NixOS/nixpkgs/pull/469950)
- 4: [Dump SQLite database to an SQL file](https://github.com/NixOS/nixpkgs/pull/522703)
### Yarn {#javascript-yarn}

View file

@ -8,6 +8,7 @@
makeSetupHook,
pnpm,
pnpm-fixup-state-db,
sqlite,
writableTmpDirAsHomeHook,
yq,
zstd,
@ -19,6 +20,7 @@ let
1 # First version. Here to preserve backwards compatibility
2 # Ensure consistent permissions. See https://github.com/NixOS/nixpkgs/pull/422975
3 # Build a reproducible tarball. See https://github.com/NixOS/nixpkgs/pull/469950
4 # Dump SQLite database to an SQL file. See https://github.com/NixOS/nixpkgs/pull/522703
];
in
{
@ -87,6 +89,7 @@ in
moreutils
pnpm # from args
pnpm-fixup-state-db'
sqlite
writableTmpDirAsHomeHook
yq
zstd
@ -172,6 +175,13 @@ in
if [ -f "$storePath/v11/index.db" ]; then
pnpm-fixup-state-db "$storePath/v11";
# Dump the SQLite database to a SQL text file for reproducibility.
# SQLite's binary format is non-deterministic (version-valid-for number, etc),
# so we store the logical contents as SQL statements and reconstruct during build.
if [[ ${toString fetcherVersion} -ge 4 ]]; then
sqlite3 "$storePath/v11/index.db" .dump > "$storePath/v11/index.db.sql"
rm "$storePath/v11/index.db"
fi
fi
# This folder contains symlinks to /build/source which we don't need
@ -231,6 +241,7 @@ in
pnpmConfigHook = makeSetupHook {
name = "pnpm-config-hook";
propagatedBuildInputs = [
sqlite
writableTmpDirAsHomeHook
zstd
];

View file

@ -55,6 +55,14 @@ pnpmConfigHook() {
chmod -R +w "$STORE_PATH"
# Reconstruct the SQLite database from the SQL dump if needed.
# The fetch phase stores a text SQL dump instead of the binary db
# to ensure reproducibility across platforms.
if [ -f "$STORE_PATH/v11/index.db.sql" ]; then
sqlite3 "$STORE_PATH/v11/index.db" < "$STORE_PATH/v11/index.db.sql"
rm "$STORE_PATH/v11/index.db.sql"
fi
pnpm config set store-dir "$STORE_PATH"
# Prevent hard linking on file systems without clone support.

View file

@ -45,8 +45,8 @@ let
sourceRoot
;
pnpm = pnpm_11;
fetcherVersion = 3;
hash = "sha256-zyY54pqlQnYdOyrNZA/WiALzdf4ZGwMLnwm6Mpb0S5k=";
fetcherVersion = 4;
hash = "sha256-jkPm7SySkzriOTcLpibJazNAzUKE48s6vBEzY7+ypBU=";
};
postBuild = ''

View file

@ -2,5 +2,6 @@
{
pnpm-empty-lockfile = callPackage ./pnpm-empty-lockfile { };
pnpm-fixup-state-db = callPackage ./pnpm-fixup-state-db { };
pnpm_11 = callPackage ./pnpm_11 { };
pnpm_11_v3 = callPackage ./pnpm_11_v3 { };
pnpm_11_v4 = callPackage ./pnpm_11_v4 { };
}

View file

@ -0,0 +1,60 @@
{
fetchPnpmDeps,
lib,
makeShellWrapper,
nodejs,
pnpmConfigHook,
pnpm_11,
stdenv,
testers,
}:
let
pnpm = pnpm_11;
in
stdenv.mkDerivation (finalAttrs: {
pname = "pnpm-test";
inherit (pnpm) version;
src = ./src;
pnpmDeps = testers.invalidateFetcherByDrvHash fetchPnpmDeps {
inherit (finalAttrs) pname version src;
inherit pnpm;
fetcherVersion = 4;
hash = "sha256-f5EC0m+y9vvt+6alquFjdgGmH8ha1YprthBU3dnq9SE=";
};
nativeBuildInputs = [
makeShellWrapper
nodejs
pnpm
pnpmConfigHook
];
buildPhase = ''
runHook preBuild
pnpm build
runHook postBuild
'';
installPhase = ''
runHook preInstall
install -Dm644 -t $out/lib/pnpm-11-test dist/index.js
makeWrapper ${lib.getExe nodejs} $out/bin/pnpm-11-test \
--add-flags "$out/lib/pnpm-11-test"
runHook postInstall
'';
__structuredAttrs = true;
meta = {
license = lib.licenses.mit;
mainProgram = "pnpm-11-test";
inherit (pnpm.meta) maintainers;
};
})

View file

@ -0,0 +1,2 @@
dist/
node_modules/

View file

@ -0,0 +1 @@
console.log("Hello World");

View file

@ -0,0 +1,20 @@
{
"name": "pnpm11test",
"version": "1.0.0",
"description": "",
"main": "dist/index.js",
"scripts": {
"build": "tsc",
"start": "node dist/index.js"
},
"license": "MIT",
"packageManager": "pnpm",
"type": "module",
"files": [
"dist/"
],
"devDependencies": {
"@types/node": "^25.5.0",
"typescript": "^6.0.2"
}
}

View file

@ -0,0 +1,39 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@types/node':
specifier: ^25.5.0
version: 25.5.0
typescript:
specifier: ^6.0.2
version: 6.0.2
packages:
'@types/node@25.5.0':
resolution: {integrity: sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==}
typescript@6.0.2:
resolution: {integrity: sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
snapshots:
'@types/node@25.5.0':
dependencies:
undici-types: 7.18.2
typescript@6.0.2: {}
undici-types@7.18.2: {}

View file

@ -0,0 +1,13 @@
{
"compilerOptions": {
"target": "ESNext",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"outDir": "dist",
"rootDir": ".",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}