Skip to content

feat(cli): add --pnpmfile and --global-pnpmfile flags#439

Merged
jdx merged 2 commits intomainfrom
claude/pnpmfile-flags
Apr 30, 2026
Merged

feat(cli): add --pnpmfile and --global-pnpmfile flags#439
jdx merged 2 commits intomainfrom
claude/pnpmfile-flags

Conversation

@jdx
Copy link
Copy Markdown
Contributor

@jdx jdx commented Apr 30, 2026

Summary

Mirrors pnpm's --pnpmfile <path> (override the local pnpmfile location) and --global-pnpmfile <path> (run a second pnpmfile before the local one). Composition order matches pnpm: global runs first, local runs second, so per-project hooks override org-wide rules.

Wired through both aube install and aube update (which forwards into the chained install with chained_frozen_mode(Prefer)). All three hook contracts honor the chain: readPackage (hot-loop NDJSON host), afterAllResolved (one-shot per pnpmfile), and preResolution (fire-and-forget per pnpmfile).

Unblocks 4 hooks.ts ports — bumps test/pnpm_install_hooks.bats from 8/22 to 12/22 (lines 85, 110, 135, 176) — see the PNPM_TEST_IMPORT.md entry for the running tally.

Implementation

New in crates/aube/src/pnpmfile.rs:

  • detect() gains a CLI-override arg. Precedence is cli > workspaceYaml > default. Typos at any layer are a hard miss with a warning, never a silent fallback.
  • detect_global() resolves --global-pnpmfile. No default location — pnpm requires the path to be passed explicitly.
  • ordered_paths() flattens (global, local) into pnpm's run order. Single source of truth so the chain ordering doesn't drift across call sites.
  • ReadPackageHostChain spawns one node child per pnpmfile that declares a readPackage hook and pipes each call through them in order. Skips spawning a host for any pnpmfile that doesn't declare the hook (saves the per-call JSON round-trip). When only one host has the hook, it's a thin wrapper over ReadPackageHost::call.
  • run_after_all_resolved_chain / run_pre_resolution_chain keep the same global-first-then-local contract for the other two hook surfaces.

Wiring through:

  • crates/aube/src/commands/install/mod.rs — both the --lockfile-only fast path (line ~1908) and the streaming-resolver path (line ~2353) replace single-pnpmfile call sites with the new chain helpers.
  • crates/aube/src/commands/update.rs — local resolve gets the chain; flags propagate into the chained install at the end of the function.
  • crates/aube/src/commands/mod.rsrun_pnpmfile_pre_resolution now takes a &[PathBuf] so the global-first ordering lives in ordered_paths rather than being re-derived at each call site.
  • InstallOptions gains pnpmfile and global_pnpmfile fields (Option<PathBuf>), defaulted to None in with_mode and the literal struct constructions in ci.rs / deploy.rs.

Settings

Layer --pnpmfile (pnpmfilePath) --global-pnpmfile (globalPnpmfile)
CLI --pnpmfile <PATH> --global-pnpmfile <PATH>
Env AUBE_PNPMFILE_PATH AUBE_GLOBAL_PNPMFILE
pnpm-workspace.yaml pnpmfilePath (n/a)

Both settings carry typedAccessorUnused = true because they're consumed via direct fields on InstallOptions rather than the codegen accessor — same pattern as pnpmfilePath already used pre-PR.

Test plan

  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test — 338 passing, including 13 new pnpmfile unit tests (CLI override precedence, absolute paths, missing-target hard-misses, chain ordering) and the strict cli_ordering_tests::test_cli_ordering
  • mise run test:bats test/pnpm_install_hooks.bats — 14/14 (12 pass + 2 documented divergences skipped)
  • mise run test:bats test/install.bats test/update.bats test/pnpmfile.bats — 80/80 regression sweep
  • aube install --help and aube update --help show both new flags
  • CLI docs regenerated via mise run render

bats note: package substitution

The pnpm tests use is-positive@1.0.0 / is-positive@3.0.0 to demonstrate composition (global pins one version, local downgrades). is-positive isn't mirrored in test/registry/storage/ yet, so the bats ports use is-number@3.0.0 / is-number@7.0.0 (already mirrored, has the two-distinct-versions shape the test needs). The substitution is called out in the test comment.

🤖 Generated with Claude Code


Note

Medium Risk
Changes the install/update dependency-resolution pipeline by introducing chained pnpmfile hooks (including a new global hook), which can alter resolved graphs and lockfile output; failures/mis-ordering could break installs. Scope is contained to pnpmfile hook handling and related CLI/settings/doc wiring.

Overview
Adds pnpm-compatible --pnpmfile <path> and --global-pnpmfile <path> flags to install and update, letting users override the local pnpmfile location and optionally layer a global pnpmfile that runs first.

Refactors pnpmfile execution to support a global+local hook chain across preResolution, readPackage, and afterAllResolved (including a new ReadPackageHostChain that pipes readPackage through multiple node hosts in order), and threads the new options through InstallOptions (including CI/deploy/chained installs).

Updates settings metadata/docs and generated CLI docs, and expands bats + unit tests to cover custom pnpmfile paths, global pnpmfile loading, and global/local composition ordering.

Reviewed by Cursor Bugbot for commit 5df2591. Bugbot is set up for automated code reviews on this repo. Configure here.

Mirror pnpm's `--pnpmfile <path>` (override the local pnpmfile location)
and `--global-pnpmfile <path>` (run a second pnpmfile before the local
one). Composition order matches pnpm: global runs first, local runs
second, so per-project hooks override org-wide rules.

Implementation lives in `pnpmfile::ReadPackageHostChain`, which spawns
one node child per pnpmfile that declares a readPackage hook and pipes
each call through them in order. Single-host runs are unchanged. The
`run_after_all_resolved_chain` and `run_pre_resolution_chain` helpers
keep the same global-then-local ordering for the other two hook
contracts.

Unblocks 4 hooks.ts ports — bumps `pnpm_install_hooks.bats` from 8/22
to 12/22 (lines 85, 110, 135, 176).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 30, 2026

Greptile Summary

Adds pnpm-compatible --pnpmfile and --global-pnpmfile flags to aube install and aube update, wiring them through the preResolution, readPackage, and afterAllResolved hook surfaces as an ordered chain (global-first, then local). The new ReadPackageHostChain correctly serialises calls, attributes errors to their source pnpmfile, and skips spawning node children for pnpmfiles that don't declare readPackage. All construction sites for InstallOptions are updated, flag propagation into the chained frozen-prefer install is correct, and the 4 new bats tests exercise both single-file and two-file composition in sync and async variants.

Confidence Score: 5/5

Safe to merge — no logic errors, security concerns, or correctness issues found.

All changed paths are symmetric, well-tested (13 new unit tests + 4 bats), and follow established patterns in the codebase. No P0 or P1 findings.

No files require special attention.

Important Files Changed

Filename Overview
crates/aube/src/pnpmfile.rs Core logic: adds detect_global, ordered_paths, run_after_all_resolved_chain, run_pre_resolution_chain, and ReadPackageHostChain; chain ordering, error attribution, and path handling all look correct
crates/aube/src/commands/install/mod.rs Both the lockfile-only fast path and the streaming-resolver path are updated symmetrically to use ordered_paths/ReadPackageHostChain; InstallOptions gains pnpmfile and global_pnpmfile fields defaulted to None in all construction sites
crates/aube/src/commands/update.rs Local resolve updated to chain; flags propagated correctly to the chained frozen-prefer install via clone; no logic gaps found
crates/aube/src/commands/mod.rs run_pnpmfile_pre_resolution signature updated to &[PathBuf]; early-return on empty slice is correct; delegates to run_pre_resolution_chain
crates/aube/src/commands/ci.rs Struct literal updated with pnpmfile: None and global_pnpmfile: None; no issues
crates/aube/src/commands/deploy.rs Same None-initialisation as ci.rs; correct
crates/aube-settings/settings.toml Adds globalPnpmfile setting and wires CLI/env/yaml sources; typedAccessorUnused = true matches pnpmfilePath pattern
test/pnpm_install_hooks.bats Four new bats tests covering --pnpmfile, --global-pnpmfile, sync and async global+local composition; substitution rationale documented; coverage looks solid
aube.usage.kdl Flags added to both install and update command blocks with correct conflicts_with semantics
docs/cli/install.md Documentation for both new flags added; prose is clear and accurate
docs/cli/update.md Documentation for both new flags added correctly
docs/cli/commands.json Generated JSON updated to include both flags for install and update; all fields present and consistent with usage.kdl
docs/settings/index.md Settings docs regenerated; globalPnpmfile section added with correct source table and examples
test/PNPM_TEST_IMPORT.md Tally updated from 8/22 to 12/22; newly-ported tests listed; remaining work accurately described

Reviews (2): Last reviewed commit: "fix(pnpmfile): tighten ReadPackageHost::..." | Re-trigger Greptile

Comment thread crates/aube/src/pnpmfile.rs Outdated
@jdx jdx force-pushed the claude/pnpmfile-flags branch from dc1e48b to 634694f Compare April 30, 2026 23:37
… errors

Two greptile review nits on PR #439:

- Drop `pub(crate)` on `ReadPackageHost::call`. The only caller is its
  sibling `ReadPackageHostChain` in the same module, so module-private
  is enough.

- Pair each chained host with its source pnpmfile path and tag
  readPackage rejections with `(<path>): <err>` on the way out. Matches
  the `wrap_err_with` shape `run_after_all_resolved_chain` and
  `run_pre_resolution_chain` already use, so a hook failure surfaces
  the offending file instead of leaving the user to guess between the
  global and local pnpmfile.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@jdx jdx enabled auto-merge (squash) April 30, 2026 23:45
@jdx jdx merged commit a077bcb into main Apr 30, 2026
18 checks passed
@jdx jdx deleted the claude/pnpmfile-flags branch April 30, 2026 23:49
@github-actions
Copy link
Copy Markdown

Benchmark changes

Versions:

  • aube: 1.5.1 -> 1.5.2
  • pnpm: 11.0.2 -> 11.0.3

Public ratios: warm installs vs Bun 4x -> 9x; warm installs vs pnpm 5x -> 13x.

Benchmark aube bun pnpm
Fresh install (warm cache) 1021ms -> 214ms (-79%) 4134ms -> 1839ms (-56%) 4717ms -> 2699ms (-43%)
CI install (warm cache, GVS disabled) 2920ms -> 1289ms (-56%) 3396ms -> 1894ms (-44%) 4864ms -> 2530ms (-48%)
CI install (cold cache, GVS disabled) 10801ms -> 3964ms (-63%) 10012ms -> 4388ms (-56%) 9722ms -> 5821ms (-40%)

5df2591 vs 400c56a | aube/bun/pnpm | 3 scenarios | 3 runs | 500mbit/50ms | generated by Codex.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant