fix(nix): pin pnpm bits to flake's own nixpkgs #99

Merged
padreug merged 1 commit from fix/flake-pnpm-from-flake-nixpkgs into dev 2026-06-10 14:20:13 +00:00
Owner

Fixes the deploy-time ERR_PNPM_NO_OFFLINE_TARBALL @vite-pwa/assets-generator-1.0.2.tgz regression introduced by #98.

Root cause

mkWebapp was sourcing pnpm, fetchPnpmDeps, pnpmConfigHook from the consumer's pkgs. fetchPnpmDeps produces a snapshot whose byte content depends on the exact pnpm version running it — even minor 10.33 → 10.34 differences produce mismatched snapshots. The pinned hash matches one snapshot exactly, so any consumer with a different nixpkgs (server-deploy, in this case) hits a tarball that isn't in the snapshot.

My #98 commit message claimed pinning pkgs.pnpm_10 "still allowing minor drift inside major-10" was fine. That was wrong — minor drift breaks snapshot reproducibility.

Fix

mkWebapp now derives a flakePkgs from this flake's own pinned nixpkgs (taking only the system attribute from the consumer's pkgs), and sources pnpm, pnpmConfigHook, fetchPnpmDeps, nodejs, autoPatchelfHook, stdenv, and stdc++ from it.

flakePkgsFor = pkgs: import nixpkgs {
  inherit (pkgs.stdenv.hostPlatform) system;
};

Consumer's pkgs is essentially unused except for its system. The build is now reproducible regardless of which nixpkgs is calling.

The pinned pnpmDeps hash (sha256-FUN2lMHsaBTkk1tljDysYZAoQD+5MIBIEvGnRUWiF4s=) is unchanged — it was always computed against the flake's own nixpkgs, so it stays valid.

Test plan

  • nix build .#main — works, produces dist/ with 6 icons
  • nix build --impure --expr '...lib.mkWebapp { pkgs = <system>; brandDir = /tmp/fixture; app = "events"; }' — now succeeds where it would previously hit ERR_PNPM_NO_OFFLINE_TARBALL; fixture's "Sortir" branding propagates end-to-end
  • aio-demo deploy via bumped flake.lock — the actual repro scenario

Follow-up

Worth documenting in branding/README.md that the consumer's pkgs argument is mostly cosmetic. The API stays the same so server-deploy's inputs.webapp.lib.mkWebapp { inherit pkgs; brandDir = ./../branding; } keeps working — pkgs just contributes the system selector now.

🤖 Generated with Claude Code

Fixes the deploy-time `ERR_PNPM_NO_OFFLINE_TARBALL @vite-pwa/assets-generator-1.0.2.tgz` regression introduced by #98. ## Root cause `mkWebapp` was sourcing `pnpm`, `fetchPnpmDeps`, `pnpmConfigHook` from the **consumer's** `pkgs`. `fetchPnpmDeps` produces a snapshot whose byte content depends on the exact pnpm version running it — even minor 10.33 → 10.34 differences produce mismatched snapshots. The pinned hash matches one snapshot exactly, so any consumer with a different nixpkgs (server-deploy, in this case) hits a tarball that isn't in the snapshot. My #98 commit message claimed pinning `pkgs.pnpm_10` "still allowing minor drift inside major-10" was fine. **That was wrong** — minor drift breaks snapshot reproducibility. ## Fix `mkWebapp` now derives a `flakePkgs` from this flake's own pinned nixpkgs (taking only the `system` attribute from the consumer's `pkgs`), and sources `pnpm`, `pnpmConfigHook`, `fetchPnpmDeps`, `nodejs`, `autoPatchelfHook`, `stdenv`, and `stdc++` from it. ```nix flakePkgsFor = pkgs: import nixpkgs { inherit (pkgs.stdenv.hostPlatform) system; }; ``` Consumer's `pkgs` is essentially unused except for its system. The build is now reproducible regardless of which nixpkgs is calling. The pinned `pnpmDeps` hash (`sha256-FUN2lMHsaBTkk1tljDysYZAoQD+5MIBIEvGnRUWiF4s=`) is unchanged — it was always computed against the flake's own nixpkgs, so it stays valid. ## Test plan - [x] `nix build .#main` — works, produces dist/ with 6 icons - [x] `nix build --impure --expr '...lib.mkWebapp { pkgs = <system>; brandDir = /tmp/fixture; app = "events"; }'` — now succeeds where it would previously hit `ERR_PNPM_NO_OFFLINE_TARBALL`; fixture's "Sortir" branding propagates end-to-end - [ ] aio-demo deploy via bumped flake.lock — the actual repro scenario ## Follow-up Worth documenting in `branding/README.md` that the consumer's `pkgs` argument is mostly cosmetic. The API stays the same so server-deploy's `inputs.webapp.lib.mkWebapp { inherit pkgs; brandDir = ./../branding; }` keeps working — `pkgs` just contributes the system selector now. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
mkWebapp was passing the consumer's `pkgs.pnpm_10` into fetchPnpmDeps,
which means the pnpmDeps snapshot is byte-for-byte different across
consumers using different nixpkgs minor versions (flake's
nixos-unstable has pnpm_10@10.34.0, server-deploy's nixpkgs may have
a different 10.x). The pinned hash matches one snapshot exactly, so
the wrong consumer gets:

  ERR_PNPM_NO_OFFLINE_TARBALL @vite-pwa/assets-generator-1.0.2.tgz

at deploy time.

Fix: derive a `flakePkgs` from THIS flake's pinned nixpkgs (via
`flakePkgsFor`) and source pnpm, pnpmConfigHook, fetchPnpmDeps,
nodejs, autoPatchelfHook, stdenv, and stdc++ from it. The consumer's
`pkgs` argument is now used only for its system attribute.

Net effect: the pnpmDeps snapshot is now reproducible regardless of
who's calling mkWebapp. The pinned hash
sha256-FUN2lMHsaBTkk1tljDysYZAoQD+5MIBIEvGnRUWiF4s= remains valid (it
was computed against the flake's own nixpkgs originally).

Verified:
- `nix build .#main` — produces same dist/ as before (uses flake pkgs
  internally either way)
- `nix build --impure --expr '...lib.mkWebapp { pkgs = <system>; ... }'`
  — now succeeds with the system's nixpkgs, where it would fail
  before with NO_OFFLINE_TARBALL on @vite-pwa/assets-generator

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
padreug deleted branch fix/flake-pnpm-from-flake-nixpkgs 2026-06-10 14:20:13 +00:00
Sign in to join this conversation.
No description provided.