Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
This repository was archived by the owner on May 14, 2026. It is now read-only.

Install respects every snapshot — no os/cpu/libc filter for optional deps #266

@zkochan

Description

@zkochan

What's happening

On an install (frozen-lockfile or not) pacquet iterates every snapshot in the lockfile and fetches / extracts / links all of them:

// crates/package-manager/src/create_virtual_store.rs
snapshots
    .iter()
    .map(|(snapshot_key, snapshot)| async move {})
    .pipe(future::try_join_all)
    .await?;

No reference to the snapshot's `optional` flag, and nothing that reads the `os` / `cpu` / `libc` fields from the package manifest or lockfile. So an `npm/pnpm` lockfile that contains, say, `fsevents@1.2.13` (declared `"os": ["darwin"]`) ends up in `node_modules/.pnpm/node_modules/fsevents` on Linux too — downloaded, extracted to the CAS, linked — even though it's a native module that won't run and the spec says to skip it.

The information to decide is already parsed:

// crates/lockfile/src/package_metadata.rs:43
pub optional: bool,

— we just don't consume it yet, nor the per-package os/cpu/libc filters.

Why it matters

Correctness. The optional-dependency contract is "if it fails to install on this platform, the install still succeeds". pacquet's behaviour is close enough for packages whose "optional" is really "lazy", but a spec-compliant consumer shouldn't install a `darwin-only` native addon on a `linux` runner. Right now we do.

Wasted work. Every cross-platform optional dep in a lockfile is:

  • a network fetch from the registry,
  • a tarball extraction to CAS,
  • a hash+write for every entry,
  • a virtual-store symlink tree.

Small per-package; adds up on realistic lockfiles (any project that depends on `chokidar`, `@swc/core`, `esbuild`, `rollup`, `@napi-rs/*`, …). Disk and time both.

Benchmark fairness. Surfaced while cleaning up the integrated-benchmark fixture in #264: on Linux CI pnpm skipped `fsevents` (and every other darwin-only optional) via its normal platform filter while pacquet installed them, quietly tilting the comparison in pnpm's favour. #264 works around this on the pnpm side by setting `supportedArchitectures` to all of `darwin|linux|win32 × x64|arm64 × glibc|musl`, so pnpm now pulls the same cross-platform set as pacquet — but that's levelling the fixture to match the bug here, not fixing it.

Proposed behaviour

Match pnpm v11's `installPlatforms`. For every snapshot:

  1. If the snapshot's manifest declares `os` / `cpu` / `libc` and the current runtime's triple isn't in the allow-list, skip it iff the snapshot is marked `optional`.
  2. Otherwise install it (required dep that won't actually run is pnpm's `EBADPLATFORM`-equivalent error — worth surfacing separately from the optional case).
  3. Honour the `supportedArchitectures` setting from `pnpm-workspace.yaml`: for each listed os/cpu/libc combo, expand the allow-list. Enables the "install deploy-target binaries on my dev machine" use case and keeps bench(integrated-benchmark): level cold-install comparison on macOS and across platforms #264's fixture knob working once this lands.

Roughly:

// pseudo-code, create_virtual_store.rs
snapshots
    .iter()
    .filter(|(_, snapshot)| install_platforms.includes(snapshot))
    .map()

with `install_platforms` computed from the current process + `supportedArchitectures` at install start.

Non-obvious bits

  • `os` / `cpu` / `libc` need to be read from the snapshot's manifest (pnpm stores them in the lockfile's `packages:` stanza for each resolved version). Our `PackageMetadata` type doesn't carry them yet — lockfile parser needs the fields added.
  • pacquet should honour `libc` detection (glibc vs musl) to avoid installing the wrong prebuilt — the v11 lockfile records this per-package. Ditto for `cpu=arm64` vs `cpu=x64`.
  • The lockfile round-trip should stay byte-identical. We're only changing what to install, not what to record.
  • CI matrix should probably run this on every supported platform (Linux glibc/musl, macOS arm64/x64, Windows) so we catch regressions in the filter.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions