Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
1 change: 0 additions & 1 deletion crates/nub-cli/src/config_fields.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,6 @@ mod tests {
.iter()
.flat_map(|field| {
aube_settings::all()
.iter()
.filter(move |engine| {
field.address == engine.name || engine.npmrc_keys.contains(&field.address)
})
Expand Down
81 changes: 81 additions & 0 deletions crates/nub-cli/src/pm_engine/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,26 @@ pub(crate) const NUB: aube_util::Embedder = aube_util::Embedder {
// token is constant-on and folds the scanner version, so a scanner-logic bump
// invalidates a warm tree and re-links; standalone aube's `None` skips the fold.
extra_settings_fingerprint: Some(crate::dynamic_phantom::settings_fingerprint),
// `aubeNoAutoInstall` skips the engine's own pre-run staleness check, which
// lives in `commands::auto_install::ensure_installed` — reached only from the
// engine's `run` / `exec` / `restart`. None of those is an `ENGINE_VERB`: nub
// runs scripts through its own frontend and gates freshness in
// `crate::verify_deps`, so the engine's gate never executes and the setting
// decides nothing here. Before this entry `nub config set aubeNoAutoInstall
// true` wrote that key into the user's `.npmrc` and `nub config list --all`
// advertised it — nub putting the ENGINE's brand in a user's config for a
// value nub never reads.
//
// Deliberately the ONLY entry. `verifyDepsBeforeRun` and
// `optimisticRepeatInstall` are read by that same dead gate, but their names
// are neutral and pnpm-shared, and nub honors `verify-deps-before-run` on its
// own path — hiding them would break the pnpm surface to fix nothing.
unsupported_settings: &[(
"aubeNoAutoInstall",
"nub does not auto-install before a run. Use `verifyDeps` in nub.jsonc, or \
`verify-deps-before-run` in .npmrc, to choose what happens when dependencies \
are stale.",
)],
};

/// Register [`NUB`] as the active embedder profile. Idempotent (the engine's
Expand Down Expand Up @@ -293,4 +313,65 @@ const _: () = {
assert!(!NUB.warm_trust_revalidate);
assert!(matches!(NUB.trust_policy_ignore_after_default, Some(20160)));
assert!(NUB.extra_settings_fingerprint.is_some());
assert!(matches!(NUB.unsupported_settings, [(n, a)]
if matches!(n.as_bytes(), b"aubeNoAutoInstall") && !a.is_empty()));
};

#[cfg(test)]
mod tests {
use super::NUB;

/// A name in `unsupported_settings` that no longer spells a real setting is
/// a SILENT no-op — the filter simply never matches, the setting it was
/// meant to hide (if it was renamed) comes back, and nothing anywhere
/// fails. The engine cannot catch this: standalone aube's list is empty, so
/// its own tests exercise the empty case only. This is the one place the
/// pairing is checked, so it looks the names up in the UNFILTERED table —
/// the filtered `find` would report exactly the entries under test as
/// absent and pass vacuously.
#[test]
fn every_unsupported_setting_names_a_real_one() {
for (name, advice) in NUB.unsupported_settings {
assert!(
aube_settings::meta::find_unfiltered(name).is_some(),
"`{name}` is not in the settings table — the entry hides nothing"
);
assert!(
!advice.is_empty(),
"`{name}` has no replacement advice; `config set` would refuse it with no next step"
);
}
}

/// The filter has to actually reach the shared lookup, not just sit in the
/// profile. Guards against a future refactor that keeps the field but stops
/// consulting it — the failure mode would otherwise be invisible until a
/// user saw the engine's brand back in `config list --all`.
#[test]
fn the_profile_entry_removes_the_setting_from_the_table() {
// `set_embedder` is a silent set-once, so a sibling test registering a
// different profile first would make every assertion below read the
// WRONG tool and fail obscurely. Name that up front.
super::register();
assert_eq!(
aube_util::embedder().name,
NUB.name,
"another test registered a different embedder first"
);
assert!(
aube_settings::meta::find("aubeNoAutoInstall").is_none(),
"the embedder filter is not wired into `meta::find`"
);
assert!(
aube_settings::meta::all().all(|m| m.name != "aubeNoAutoInstall"),
"the embedder filter is not wired into `meta::all`"
);
assert!(
aube_settings::meta::unsupported_for_key("aube-no-auto-install").is_some(),
"an alias write must still be recognizable so `config set` can refuse it"
);
// The positive control: an ordinary setting is untouched, so the two
// assertions above are reading the filter rather than a broken lookup.
assert!(aube_settings::meta::find("autoInstallPeers").is_some());
}
}
3 changes: 1 addition & 2 deletions crates/nub-cli/src/pm_engine/install_report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,6 @@ impl SourceIndex {
};
let yaml_settings = |raw| {
aube_settings::all()
.iter()
.filter(|meta| !aube_settings::workspace_yaml_suppressed(meta))
.filter_map(|meta| workspace_yaml_scalar(meta, raw).map(|value| (meta.name, value)))
.collect::<Vec<_>>()
Expand All @@ -252,7 +251,7 @@ impl SourceIndex {
// the settings the YAML still supplies, this one asks whether the
// current pnpm posture rejected a layout key.
let pnpm_yaml_layout_dropped = [&raw, &global_raw].into_iter().any(|raw| {
aube_settings::all().iter().any(|meta| {
aube_settings::all().any(|meta| {
aube_settings::workspace_yaml_suppressed(meta)
&& meta.layout
&& !meta.npmrc_keys.is_empty()
Expand Down
34 changes: 33 additions & 1 deletion crates/nub-cli/src/pm_engine/store_config_family.rs
Original file line number Diff line number Diff line change
Expand Up @@ -605,6 +605,13 @@ fn dispatch_config(parsed: ConfigArgs) -> Result<i32> {
Some(ConfigCommand::Set(set)) => {
super::engine_brand_preflight();
if global {
// Global scope has no router, so it repeats the refusals it
// needs — the same shape as the map refusal below. A setting
// nub does not consume is refused in BOTH scopes; `--global`
// would otherwise be an open door straight to `~/.npmrc`.
if let Some(err) = npmrc_first::unsupported_setting_refusal(&set.key) {
return Err(err);
}
// Neutral global write. npm-shared/auth keys FIRST (a key like
// `registry` is auth, not the `registries` map — the shared
// check must win before the map refusal below).
Expand Down Expand Up @@ -800,6 +807,16 @@ mod npmrc_first {
/// non-pnpm / nub-identity surface. npm-shared keys (`.npmrc`) and map
/// refusals are independent of this signal.
pub(super) fn classify_set(key: &str, scalar_to_yaml: bool) -> SetRoute {
// A setting nub's embedder profile declares it does not consume. First,
// and its own arm rather than a case of the `setting_for_key` match
// below — that lookup is embedder-FILTERED, so an unsupported setting
// reads as unknown and falls to the free-form `ProjectNpmrc` route,
// writing the key verbatim into the user's `.npmrc`. That is how
// `aubeNoAutoInstall` used to land there: inert, unreadable by anything,
// and carrying the engine's brand into a file nub wrote.
if let Some(err) = unsupported_setting_refusal(key) {
return SetRoute::Refuse(err);
}
if is_npm_shared_key(key) {
return SetRoute::Engine;
}
Expand Down Expand Up @@ -941,7 +958,7 @@ mod npmrc_first {
/// then any alias surface (npmrc/yaml/env/cli spellings).
fn setting_for_key(key: &str) -> Option<&'static SettingMeta> {
meta::find(key).or_else(|| {
meta::all().iter().find(|meta| {
meta::all().find(|meta| {
meta.npmrc_keys.contains(&key)
|| meta.workspace_yaml_keys.contains(&key)
|| meta.env_vars.contains(&key)
Expand Down Expand Up @@ -1032,6 +1049,21 @@ mod npmrc_first {
cwd
}

/// The refusal for a key naming a setting nub's embedder profile declares
/// it does not consume, `None` for every other key. Both write scopes ask
/// this — the project route through [`classify_set`], the global one
/// directly, since it has no router.
///
/// `key` is echoed as the user spelled it; the advice is looked up by the
/// CANONICAL name, which is where the profile hangs it.
pub(super) fn unsupported_setting_refusal(key: &str) -> Option<anyhow::Error> {
let meta = meta::unsupported_for_key(key)?;
let advice = meta::unsupported_advice(meta.name).unwrap_or_default();
Some(anyhow!(
"nub config set {key}: `{key}` is not a nub setting\n\x20\x20{advice}"
))
}

fn map_setting_error(name: &str) -> anyhow::Error {
anyhow!(
"nub config set {name}: `{name}` is a workspace map setting and can't be set as a single value\n\
Expand Down
Loading
Loading