Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions crates/aube/src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1244,6 +1244,16 @@ pub(crate) async fn ensure_installed(no_install: bool) -> miette::Result<()> {
if skip_auto_install_on_package_manager_mismatch() {
return Ok(());
}
// Skip verify-deps when invoked from inside a script. The parent
// install (or parent `aube run`) already validated freshness and
// either holds the project lock or hasn't written `.aube-state` yet
// — re-entering `ensure_installed` here would either deadlock on
// the lock (`verifyDepsBeforeRun=install`) or hard-fail on the
// missing state file (`verifyDepsBeforeRun=error`). Matches
// npm/pnpm's "no verify-deps inside lifecycle scripts" contract.
if std::env::var_os("npm_lifecycle_event").is_some() {
return Ok(());
}

let initial_cwd = crate::dirs::cwd()?;
// Prefer the workspace root as the freshness anchor. A monorepo
Expand Down
15 changes: 7 additions & 8 deletions test/PNPM_TEST_IMPORT.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ Triaged (decisions recorded, awaiting work):
- **won't-support** — misc.ts:479 — `updateConfig` pnpmfile hook. Decision: env vars + `.npmrc` + `AUBE_*` overrides cover the static config-mutation use cases (frozen-lockfile per branch, registry switching, nodeLinker per env). The only thing `updateConfig` adds beyond that is programmatic JS-driven config, which is out of scope for aube's pnpmfile surface. Behavioral parity for misc.ts:457/479's CI test case is already covered via the env-var port. Move to "Explicitly skipped (Tier 3)".
- **won't-support** — hooks.ts:551 + 661 — readPackage on root project's manifest / shared-workspace-lockfile importer rewrite. aube fires `readPackage` only on resolved registry packages ([resolve.rs:1408](../crates/aube-resolver/src/resolve.rs:1408)), not on importer manifests (root or workspace-project). The cross-importer dep-policy use case pnpm solves with `readPackage`-on-importers is covered in aube by `pnpm.overrides` / `overrides` and `packageExtensions`. Narrower hook scope is the design choice — keeps `readPackage`'s blast radius limited to third-party deps, which is the 95% case. Document the limitation in pnpmfile docs.
- ~~**support** — lifecycleScripts.ts:108 — rollback dep on build failure.~~ Ported in [test/lifecycle_scripts.bats](lifecycle_scripts.bats). Added the `@pnpm.e2e/aube-test-failing-install` fixture under [test/registry/storage/](registry/storage/@pnpm.e2e/aube-test-failing-install/) (1.0.0, `install: exit 1`). Test asserts `node_modules/.aube-state` is absent after failure, second install still fails, and removing the broken dep allows install to succeed.
- **support — real bug, not test-only** — lifecycleScripts.ts:179, 200 — `verify-deps-before-run` interaction with preinstall sub-aube calls. Triage was wrong: the only "parent-set" mechanism at [run.rs:536](../crates/aube/src/commands/run.rs:536) preserves `INIT_CWD`, not lifecycle-script context. Empirically, with `verifyDepsBeforeRun=error` and `preinstall: aube run sayHello`, the inner `aube run` fires the verify-deps check (state isn't written yet, root preinstall runs before linking) and exits with `dependencies need install before run: install state not found`, failing the parent install. With `verifyDepsBeforeRun=install`, the inner `aube run` triggers `ensure_installed` → `install::run`, which deadlocks on the project lock the outer install holds (`Waiting for another aube process to finish in this project...`, observed via 60s timeout). Aube fix: skip `ensure_installed` when `npm_lifecycle_event` is set in the env (matches npm/pnpm's "no verify-deps inside lifecycle scripts" contract). Then port the tests. Tracked as a separate aube fix; not portable today without source changes.
- **support — landed** — lifecycleScripts.ts:282 — selective `aube rebuild <pkg>` shipped. [rebuild.rs](../crates/aube/src/commands/rebuild.rs) now takes `RebuildArgs { packages: Vec<String> }`, filters `run_dep_lifecycle_scripts` by named packages, bypasses the build policy for those deps, and skips the root preinstall/install/postinstall/prepare hooks when packages are supplied. Bats coverage in [test/rebuild.bats](rebuild.bats). Pnpm test port pending.
- ~~**support — real bug, not test-only** — lifecycleScripts.ts:179, 200 — `verify-deps-before-run` interaction with preinstall sub-aube calls.~~ Aube fix shipped: [ensure_installed](../crates/aube/src/commands/mod.rs:1240) returns early when `npm_lifecycle_event` is set in the env, matching npm/pnpm's "no verify-deps inside lifecycle scripts" contract. Both tests ported in [test/lifecycle_scripts.bats](lifecycle_scripts.bats) (`--config.verify-deps-before-run=error` flag form for #8954 + `verifyDepsBeforeRun: install` workspace-yaml form for #10060, with a 60s timeout guard in case the deadlock returns).
- ~~**support — landed** — lifecycleScripts.ts:282 — selective `aube rebuild <pkg>` shipped.~~ Pnpm test ported in [test/rebuild.bats](rebuild.bats) — asserts the un-approved sibling's `unreviewed_builds` entry survives a selective rebuild via the warm-path repeat-install warning (aube's behavioral analogue of pnpm's `modules.yaml.ignoredBuilds` inspection).
- **support, high priority — landed** — update.ts:14, 143, 170, 197 — GitHub `user/repo` shorthand resolution. Parser branch + `update --latest` skip-non-registry guard landed in PR #472. CLI-side `aube add <bare-shorthand>` extended [parse_pkg_spec](../crates/aube/src/commands/add.rs:171) to detect git-spec forms and write them verbatim to `package.json` (skipping the packument fetch). All four `kevva/is-negative` assertions are restored end-to-end via the network-gated ports in [test/pnpm_update_slow.bats](pnpm_update_slow.bats).
- **support — landed** — `aube add file:./<dir>` / `aube add link:./<dir>` end-to-end. [parse_pkg_spec](../crates/aube/src/commands/add.rs:175) now routes `file:` / `link:` local-path specs (and the `my-alias@file:./pkg` / `my-alias@link:./pkg` alias forms) through a non-packument branch — the verbatim spec is written to `package.json` and the resolver's existing local branch dispatches the install. Regression-guarded by three offline ports in [test/add.bats](add.bats). Closes the local-path-spec followup surfaced during the git-spec PR review.
- **support — landed** — update.ts:51, 95 — update without `--latest` rewrites manifest specifier. New `updateRewritesSpecifier` setting (default `true`) drops the `--latest`-only gate in [update.rs:rewrite_specifier](../crates/aube/src/commands/update.rs:792) for caret/tilde specs (`^X.Y.Z` / `~X.Y.Z`). User-typed `>=`, `1.x`, `latest`, git specs are preserved. Users who prefer aube's prior frozen-manifest behavior can flip the setting off.
Expand Down Expand Up @@ -63,16 +63,15 @@ Goal: highest install-path parity coverage for lowest cost. Each row is a pnpm s
- readPackage returning undefined fails install (68) — empirically aube continues with the original manifest (the IPC shim falls back to `pkg` when the hook returns falsy). Existing `skip "aube divergence"` at `pnpm_install_hooks.bats:310` documents it. Move to Tier 3 pending a Discussion on whether to mirror pnpm's hard-fail.
- readPackage on root project's manifest applies (551), readPackage with shared workspace lockfile rewrites importer deps (661) — aube fires readPackage only on resolved registry packages. Cross-importer dep policy is covered by `overrides` / `packageExtensions`.
- The 314 install-side --ignore-pnpmfile case is already covered by [test/pnpmfile.bats](pnpmfile.bats:215).
- [ ] `pnpm/test/install/lifecycleScripts.ts` (21 tests, 356 LOC) → folded into [test/lifecycle_scripts.bats](lifecycle_scripts.bats) (17/21 ported, [#421](https://github.com/endevco/aube/pull/421))
- Done: preinstall/postinstall/prepare stdout reaches the user (43, 56, 95), `npm_config_user_agent` set on lifecycle scripts (29), root postinstall NOT triggered by `aube add` / root prepare NOT triggered by `aube add` (69, 82), root postinstall NOT triggered by `aube remove` / `aube update`, strictDepBuilds throws on unreviewed registry build scripts (226), allowBuilds review placeholder auto-populated (260) and merged with existing approvals (268), strictDepBuilds still fails for packages with cached side-effects (303 — covers [pnpm/pnpm#11035](https://github.com/pnpm/pnpm/issues/11035)), `--allow-build=<pkg>` selectively pre-approves a dep's build scripts (149), bare `--allow-build` errors out (164), `--allow-build=<pkg>` errors when conflicting with `allowBuilds: false` (347), repeat-install ignored-build-scripts warning preservation (245 — `.aube-state` now persists the unreviewed set so the warm-path short-circuit re-emits the warning), failed dep build retries on next install / rollback contract (108 — minimal `@pnpm.e2e/aube-test-failing-install` fixture asserting state-not-written-on-failure semantics, mirrors pnpm's `package-that-cannot-be-installed` shape).
- [ ] `pnpm/test/install/lifecycleScripts.ts` (21 tests, 356 LOC) → split between [test/lifecycle_scripts.bats](lifecycle_scripts.bats) and [test/rebuild.bats](rebuild.bats) (20/21 ported, [#421](https://github.com/endevco/aube/pull/421))
- Done: preinstall/postinstall/prepare stdout reaches the user (43, 56, 95), `npm_config_user_agent` set on lifecycle scripts (29), root postinstall NOT triggered by `aube add` / root prepare NOT triggered by `aube add` (69, 82), root postinstall NOT triggered by `aube remove` / `aube update`, strictDepBuilds throws on unreviewed registry build scripts (226), allowBuilds review placeholder auto-populated (260) and merged with existing approvals (268), strictDepBuilds still fails for packages with cached side-effects (303 — covers [pnpm/pnpm#11035](https://github.com/pnpm/pnpm/issues/11035)), `--allow-build=<pkg>` selectively pre-approves a dep's build scripts (149), bare `--allow-build` errors out (164), `--allow-build=<pkg>` errors when conflicting with `allowBuilds: false` (347), repeat-install ignored-build-scripts warning preservation (245 — `.aube-state` now persists the unreviewed set so the warm-path short-circuit re-emits the warning), failed dep build retries on next install / rollback contract (108 — minimal `@pnpm.e2e/aube-test-failing-install` fixture asserting state-not-written-on-failure semantics, mirrors pnpm's `package-that-cannot-be-installed` shape), preinstall script does NOT trigger verify-deps-before-run (179 — pnpm/pnpm#8954, asserted via `--config.verify-deps-before-run=error`), preinstall + postinstall scripts do NOT trigger verify-deps-before-run via workspace yaml (200 — pnpm/pnpm#10060, asserted via `verifyDepsBeforeRun: install` with a 60s timeout guard).
- Aube divergence worth noting in ports: aube's strict-dep-builds error reads "dependencies with build scripts must be reviewed before install" where pnpm's reads "Ignored build scripts:". Aube now writes the same `"set this to true or false"` placeholder string as pnpm, so review-placeholder ports can assert verbatim.
- Equivalent coverage already exists in aube: installation-fails-on-lifecycle-script-error (17 — aube's "fails fast if a root lifecycle script exits non-zero" at lifecycle_scripts.bats:187 covers the same contract).
- **Support** (aube fix needed before / alongside port):
- ~~node-gyp on PATH (128)~~ — ported in [test/node_gyp_bootstrap.bats](node_gyp_bootstrap.bats); uses the offline mirrored node-gyp fixture, so no slow/network gate is needed.
- git-dep prepare under dangerouslyAllowAllBuilds (336) — port with network + a stable upstream pin (prefer a repo under github.com/endevco), gate slow.
- selective `aube rebuild <pkg>` (282) — **landed** as the CLI-side feature: [rebuild.rs](../crates/aube/src/commands/rebuild.rs) now accepts positional package names, filters `run_dep_lifecycle_scripts` to those, bypasses the build policy for the named deps, and skips root preinstall/install/postinstall/prepare hooks. Bats coverage in [test/rebuild.bats](rebuild.bats). Pnpm test port pending.
- **Support — real bug, not test-only** (aube fix needed before port):
- `verify-deps-before-run` interaction with preinstall sub-aube calls (179, 200) — triage was wrong. Empirically (a) with `verifyDepsBeforeRun=error`, the inner `aube run` from a preinstall script fails with `dependencies need install before run: install state not found` (the parent install hasn't written state yet), and (b) with `verifyDepsBeforeRun=install`, the inner call deadlocks on the project lock (`Waiting for another aube process to finish in this project...`). The "parent-set" mechanism at [run.rs:536](../crates/aube/src/commands/run.rs:536) only preserves `INIT_CWD`, not lifecycle context. Aube fix: treat `npm_lifecycle_event` being set as a signal to skip `ensure_installed` in `aube run` / `aube exec`, matching npm/pnpm's "no verify-deps inside lifecycle scripts" contract. Then port the tests.
- selective `aube rebuild <pkg>` (282) — **landed and ported**. CLI-side feature in [rebuild.rs](../crates/aube/src/commands/rebuild.rs); pnpm-equivalent regression guard ("selective rebuild preserves ignoredBuilds for packages not being rebuilt") in [test/rebuild.bats](rebuild.bats), asserting the un-approved sibling's `unreviewed_builds` entry persists via the warm-path repeat-install warning.
- **Support — landed**: `verify-deps-before-run` interaction with preinstall sub-aube calls (179, 200) — [ensure_installed](../crates/aube/src/commands/mod.rs:1240) now returns early when `npm_lifecycle_event` is set in the env, so nested `aube run` / `aube exec` from inside a script never fires the verify-deps check. Closes both fault lines (`verifyDepsBeforeRun=error` no longer hard-fails on the inner call; `verifyDepsBeforeRun=install` no longer deadlocks on the parent install's project lock). Both pnpm tests ported in [test/lifecycle_scripts.bats](lifecycle_scripts.bats).
- [x] `pnpm/test/saveCatalog.ts` (8 tests, 224 LOC) → [test/pnpm_savecatalog.bats](pnpm_savecatalog.bats) (8/8 ported)
- Implements `aube add --save-catalog` and `--save-catalog-name=<name>`, `<pkg>@workspace:*` CLI parsing for `aube add`, and `sharedWorkspaceLockfile=false` per-project lockfile writes.

Expand All @@ -84,7 +83,7 @@ Goal: highest install-path parity coverage for lowest cost. Each row is a pnpm s
- Aube-side fixes that landed alongside the ports: (1) `update.rs` look-up by manifest key for aliased direct deps so `--latest` rewrites the `npm:` spec correctly, (2) `--save-exact` / `-E` flag on `update`, (3) recursive update silently skips projects that don't declare any of the named args (matching pnpm), (4) bulk `update --latest` (no positional args) preserves a manifest pin that's numerically newer than the registry's `latest` dist-tag — pre-fetches packuments via the resolver's cache and skips the rewrite for any direct dep where the parsed exact pin > parsed `dist-tags.latest`. Mirrors pnpm's #7436 regression guard, (5) `<pkg>@<spec>` arg syntax — `@latest` is the syntactic equivalent of `--latest <pkg>`, (6) `aube update <indirect-pkg>@latest` refreshes a transitive dep without touching package.json (rewrites the parent's locked dep edge to `latest` so the resolver re-resolves through the registry instead of reusing the locked version pointer in `aube_resolver::resolve.rs:1164`).
- Skipped: recursive update --latest no-shared (249) and recursive update --latest --prod no-shared (302) are subsumed by the shared-lockfile ports above (aube's `update -r` always writes per-project lockfiles regardless of `sharedWorkspaceLockfile`).
- **Support, high priority — landed**: parser branch in [parse_git_spec](../crates/aube-lockfile/src/lib.rs:412) recognizes bare `user/repo` (PR #472), [update.rs:rewrite_specifier](../crates/aube/src/commands/update.rs:792)'s caller skips git specs so a bare shorthand isn't silently rewritten to a registry semver pin (PR #472), and the CLI add path now routes git specs through a non-packument branch in [parse_pkg_spec](../crates/aube/src/commands/add.rs:171). Four network-gated regression-guard ports in [test/pnpm_update_slow.bats](pnpm_update_slow.bats) cover `aube add kevva/is-negative` plus install/update --latest end-to-end for update.ts:14/143/170/197.
- **Support** (aube fix needed before port): update without --latest rewrites manifest specifier (51, 95) — drop the `--latest`-only gate in [update.rs:rewrite_specifier](../crates/aube/src/commands/update.rs:792) for the cosmetic floor-bump case (`^X.Y.Z` / `~X.Y.Z` only); gate behind `updateRewritesSpecifier=true` (default true).
- **Support — landed**: update without --latest rewrites manifest specifier (51, 95) — `updateRewritesSpecifier` setting (default true) drops the `--latest`-only gate in [update.rs:rewrite_specifier](../crates/aube/src/commands/update.rs:897) for caret/tilde specs (`^X.Y.Z` / `~X.Y.Z`). User-typed `>=`, `1.x`, `latest`, git specs are preserved. Single-project port at [test/pnpm_update.bats:1138](pnpm_update.bats), recursive port at [test/pnpm_update.bats:1249](pnpm_update.bats).
- **Won't-support** (intentional aube divergence, see Tier 3 skip section):
- deep update (599) — `aube update --depth N` no-op. Workaround is `rm aube-lock.yaml && aube install`. Aube should make `--depth` parsed-but-warn so the no-op isn't silent.
- Documented divergences (aube-side, no fix planned): `aube update -r` always writes per-project lockfiles even when `sharedWorkspaceLockfile=true` — there's no shared-lockfile aggregation on the update path; use `aube install` from the workspace root for that.
Expand Down
55 changes: 55 additions & 0 deletions test/lifecycle_scripts.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1008,3 +1008,58 @@ YAML
run grep -F '"dependencies"' package.json
assert_failure
}

# Ported from pnpm/test/install/lifecycleScripts.ts:179
# (preinstall script does not trigger verify-deps-before-run, pnpm/pnpm#8954).
# Substitution: cowsay@1.5.0 → is-odd (in-tree fixture), and pnpm's
# `--config.verify-deps-before-run=error` flag is preserved verbatim via
# aube's pnpm-compat `--config.<key>=<value>` parser.
@test "preinstall script does not trigger verify-deps-before-run" {
cat >package.json <<'JSON'
{
"name": "preinstall-script-does-not-trigger-verify-deps-before-run",
"version": "1.0.0",
"private": true,
"scripts": {
"sayHello": "echo hello world",
"preinstall": "aube run sayHello"
},
"dependencies": {
"is-odd": "^3.0.1"
}
}
JSON
run aube --config.verify-deps-before-run=error install
assert_success
assert_output --partial "hello world"
}

# Ported from pnpm/test/install/lifecycleScripts.ts:200
# (preinstall and postinstall scripts do not trigger verify-deps-before-run
# when using settings from a config file, pnpm/pnpm#10060).
# Without the lifecycle-context guard, the inner `aube run` deadlocks on the
# project lock the outer install holds. The test fails fast (60s ceiling) if
# the deadlock returns.
@test "preinstall + postinstall scripts do not trigger verify-deps-before-run via workspace yaml" {
cat >package.json <<'JSON'
{
"name": "preinstall-script-does-not-trigger-verify-deps-before-run-config-file",
"version": "1.0.0",
"private": true,
"scripts": {
"sayHello": "echo hello world",
"preinstall": "aube run sayHello",
"postinstall": "aube run sayHello"
},
"dependencies": {
Comment thread
greptile-apps[bot] marked this conversation as resolved.
"is-odd": "^3.0.1"
}
}
JSON
cat >pnpm-workspace.yaml <<'YAML'
verifyDepsBeforeRun: install
YAML
run timeout 60 aube install
assert_success
assert_output --partial "hello world"
}
Loading
Loading