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
5 changes: 4 additions & 1 deletion aube.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,11 @@ cmd add help="Add a dependency" {
flag --allow-low-downloads help="Bypass the [`lowDownloadThreshold`] confirm prompt / refusal for this invocation" {
long_help "Bypass the [`lowDownloadThreshold`] confirm prompt / refusal for this invocation.\n\n`aube add` looks up each candidate's weekly download count and prompts (interactive) or fails (CI) when the count is below [`lowDownloadThreshold`]. The flag is intended for the cases where you've already verified the package out-of-band — adding a brand-new niche tool, a fresh fork, an internal scratch package — and don't want the prompt to interrupt scripted workflows. Does not affect the OSV malicious-package check, which remains a hard block."
}
flag --dangerously-allow-all-builds help="Allow every dependency's lifecycle scripts to run" {
long_help "Allow every dependency's lifecycle scripts to run.\n\nBypasses the `allowBuilds` allowlist for this invocation. Do not use in CI. Mirrors pnpm's `--dangerously-allow-all-builds`."
}
flag --deny-build help="Mark a dependency's lifecycle scripts as reviewed and denied" var=#true {
long_help "Mark a dependency's lifecycle scripts as reviewed and denied.\n\nWrites `allowBuilds: { <pkg>: false }` into the workspace yaml (or `package.json#aube.allowBuilds`) before the install runs, so the named package's lifecycle scripts stay skipped without tripping `strictDepBuilds=true`. Repeatable — pass the flag once per package.\n\nConflicts with `--no-save`, which only snapshots `package.json` and the lockfile and would leave an orphaned denial in the workspace yaml on restore. Also conflicts with `--allow-build` for the same package name."
long_help "Mark a dependency's lifecycle scripts as reviewed and denied.\n\nWrites `allowBuilds: { <pkg>: false }` into the workspace yaml (or `package.json#aube.allowBuilds`) before the install runs, so the named package's lifecycle scripts stay skipped without tripping `strictDepBuilds=true`. Repeatable — pass the flag once per package.\n\nConflicts with `--no-save`, which only snapshots `package.json` and the lockfile and would leave an orphaned denial in the workspace yaml on restore. Also conflicts with `--allow-build` for the same package name and with `--dangerously-allow-all-builds`."
arg <PKG>
}
flag --ignore-scripts help="Skip lifecycle scripts (no-op; aube already skips by default)" hide=#true
Expand Down
4 changes: 4 additions & 0 deletions crates/aube/src/commands/add/filtered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ pub(super) async fn run(
let mut install_opts = install::InstallOptions::with_mode(
crate::commands::chained_frozen_mode(install::FrozenMode::Fix),
);
super::apply_dangerously_allow_all_builds(
&mut install_opts,
args.dangerously_allow_all_builds,
);
install_opts.workspace_filter = filter.clone();
// See the sibling `aube add` codepath for why this flag is set:
// live OSV API on the resolved transitives.
Expand Down
27 changes: 17 additions & 10 deletions crates/aube/src/commands/add/global.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ use super::{AddArgs, run};
use miette::{Context, IntoDiagnostic};
use std::collections::BTreeMap;

pub(super) struct GlobalAddOptions {
pub(super) allow_build: Vec<String>,
pub(super) allow_low_downloads: bool,
pub(super) dangerously_allow_all_builds: bool,
pub(super) deny_build: Vec<String>,
}

/// `aube add -g <pkg>...` — install into an isolated global install dir
/// and symlink the resulting binaries into the global bin dir.
///
Expand All @@ -20,9 +27,7 @@ use std::collections::BTreeMap;
/// keeps.
pub(super) async fn run_global(
packages: &[String],
allow_build: Vec<String>,
deny_build: Vec<String>,
allow_low_downloads: bool,
options: GlobalAddOptions,
lockfile: crate::cli_args::LockfileArgs,
network: crate::cli_args::NetworkArgs,
virtual_store: crate::cli_args::VirtualStoreArgs,
Expand Down Expand Up @@ -73,9 +78,7 @@ pub(super) async fn run_global(
.collect();
let result = run_global_inner(
packages,
allow_build,
deny_build,
allow_low_downloads,
options,
&layout,
&install_dir,
lockfile,
Expand Down Expand Up @@ -111,19 +114,22 @@ pub(super) async fn run_global(
result
}

#[allow(clippy::too_many_arguments)]
async fn run_global_inner(
packages: &[String],
allow_build: Vec<String>,
deny_build: Vec<String>,
allow_low_downloads: bool,
options: GlobalAddOptions,
layout: &crate::commands::global::GlobalLayout,
install_dir: &std::path::Path,
lockfile: crate::cli_args::LockfileArgs,
network: crate::cli_args::NetworkArgs,
virtual_store: crate::cli_args::VirtualStoreArgs,
) -> miette::Result<()> {
use crate::commands::global;
let GlobalAddOptions {
allow_build,
allow_low_downloads,
dangerously_allow_all_builds,
deny_build,
} = options;

// Seed a minimal package.json so the resolver has a project to work
// against. We never persist metadata beyond this; the install dir is
Expand Down Expand Up @@ -197,6 +203,7 @@ async fn run_global_inner(
// install" even when the user explicitly approved the dep
// (see Discussion #617).
allow_build,
dangerously_allow_all_builds,
// Same contract as `allow_build`, but force-denies matching
// packages so global installs can satisfy `strictDepBuilds=true`
// while keeping selected lifecycle scripts skipped.
Expand Down
36 changes: 30 additions & 6 deletions crates/aube/src/commands/add/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ pub struct AddArgs {
/// which remains a hard block.
#[arg(long)]
pub allow_low_downloads: bool,
/// Allow every dependency's lifecycle scripts to run.
///
/// Bypasses the `allowBuilds` allowlist for this invocation. Do not
/// use in CI. Mirrors pnpm's `--dangerously-allow-all-builds`.
#[arg(long)]
pub dangerously_allow_all_builds: bool,
/// Mark a dependency's lifecycle scripts as reviewed and denied.
///
/// Writes `allowBuilds: { <pkg>: false }` into the workspace yaml
Expand All @@ -80,11 +86,11 @@ pub struct AddArgs {
/// Conflicts with `--no-save`, which only snapshots `package.json`
/// and the lockfile and would leave an orphaned denial in the
/// workspace yaml on restore. Also conflicts with `--allow-build` for
/// the same package name.
/// the same package name and with `--dangerously-allow-all-builds`.
#[arg(
long = "deny-build",
value_name = "PKG",
conflicts_with = "no_save",
conflicts_with_all = ["no_save", "dangerously_allow_all_builds"],
require_equals = true,
value_parser = parse_deny_build_value,
)]
Expand Down Expand Up @@ -214,8 +220,9 @@ pub async fn run(
save_catalog,
save_catalog_name,
allow_build,
deny_build,
allow_low_downloads,
dangerously_allow_all_builds,
deny_build,
lockfile,
network,
virtual_store,
Expand All @@ -236,9 +243,12 @@ pub async fn run(
if global {
return global::run_global(
packages,
allow_build,
deny_build,
allow_low_downloads,
global::GlobalAddOptions {
allow_build,
allow_low_downloads,
dangerously_allow_all_builds,
deny_build,
},
lockfile,
network,
virtual_store,
Expand Down Expand Up @@ -399,6 +409,7 @@ pub async fn run(
// `ERR_AUBE_MALICIOUS_PACKAGE` as the CLI-name gate above.
let mut install_opts =
install::InstallOptions::with_mode(super::chained_frozen_mode(install::FrozenMode::Fix));
apply_dangerously_allow_all_builds(&mut install_opts, dangerously_allow_all_builds);
install_opts.osv_transitive_check = true;
let pipeline_result: miette::Result<()> = install::run(install_opts).await;

Expand Down Expand Up @@ -438,3 +449,16 @@ pub async fn run(
}
Ok(())
}

pub(super) fn apply_dangerously_allow_all_builds(
install_opts: &mut install::InstallOptions,
enabled: bool,
) {
if enabled {
install_opts.dangerously_allow_all_builds = true;
install_opts.cli_flags.push((
"dangerously-allow-all-builds".to_string(),
"true".to_string(),
));
}
}
40 changes: 40 additions & 0 deletions crates/aube/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1356,6 +1356,46 @@ mod cli_spec_tests {
);
}

#[test]
fn add_accepts_dangerously_allow_all_builds_after_package() {
let cli = Cli::try_parse_from([
"aube",
"add",
"--global",
"opencode-ai@1.17.13",
"--dangerously-allow-all-builds",
])
.expect("add --dangerously-allow-all-builds should parse after a package");

let Some(Commands::Add(add_args)) = cli.command else {
panic!("expected add subcommand");
};
assert!(add_args.global);
assert!(add_args.dangerously_allow_all_builds);
assert_eq!(add_args.packages, ["opencode-ai@1.17.13"]);
}

#[test]
fn add_rejects_deny_build_with_dangerously_allow_all_builds() {
let err = match Cli::try_parse_from([
"aube",
"add",
"some-package",
"--deny-build=some-package",
"--dangerously-allow-all-builds",
]) {
Ok(_) => panic!("deny-build should conflict with dangerously-allow-all-builds"),
Err(err) => err,
};

assert!(
err.to_string().contains(
"'--deny-build=<PKG>' cannot be used with '--dangerously-allow-all-builds'"
),
"{err}"
);
}

#[test]
fn lifter_does_not_eat_lifted_flag_as_kept_flag_value() {
// Regression: `aube --dir /tmp --frozen-lockfile install` would
Expand Down
8 changes: 7 additions & 1 deletion docs/cli/add.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,13 +46,19 @@ Bypass the [`lowDownloadThreshold`] confirm prompt / refusal for this invocation

`aube add` looks up each candidate's weekly download count and prompts (interactive) or fails (CI) when the count is below [`lowDownloadThreshold`]. The flag is intended for the cases where you've already verified the package out-of-band — adding a brand-new niche tool, a fresh fork, an internal scratch package — and don't want the prompt to interrupt scripted workflows. Does not affect the OSV malicious-package check, which remains a hard block.

### `--dangerously-allow-all-builds`

Allow every dependency's lifecycle scripts to run.

Bypasses the `allowBuilds` allowlist for this invocation. Do not use in CI. Mirrors pnpm's `--dangerously-allow-all-builds`.

### `--deny-build… <PKG>`

Mark a dependency's lifecycle scripts as reviewed and denied.

Writes `allowBuilds: { <pkg>: false }` into the workspace yaml (or `package.json#aube.allowBuilds`) before the install runs, so the named package's lifecycle scripts stay skipped without tripping `strictDepBuilds=true`. Repeatable — pass the flag once per package.

Conflicts with `--no-save`, which only snapshots `package.json` and the lockfile and would leave an orphaned denial in the workspace yaml on restore. Also conflicts with `--allow-build` for the same package name.
Conflicts with `--no-save`, which only snapshots `package.json` and the lockfile and would leave an orphaned denial in the workspace yaml on restore. Also conflicts with `--allow-build` for the same package name and with `--dangerously-allow-all-builds`.

### `--no-save`

Expand Down
15 changes: 14 additions & 1 deletion docs/cli/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,24 @@
"hide": false,
"global": false
},
{
"name": "dangerously-allow-all-builds",
"usage": "--dangerously-allow-all-builds",
"help": "Allow every dependency's lifecycle scripts to run",
"help_long": "Allow every dependency's lifecycle scripts to run.\n\nBypasses the `allowBuilds` allowlist for this invocation. Do not use in CI. Mirrors pnpm's `--dangerously-allow-all-builds`.",
"help_first_line": "Allow every dependency's lifecycle scripts to run",
"short": [],
"long": [
"dangerously-allow-all-builds"
],
"hide": false,
"global": false
},
{
"name": "deny-build",
"usage": "--deny-build… <PKG>",
"help": "Mark a dependency's lifecycle scripts as reviewed and denied",
"help_long": "Mark a dependency's lifecycle scripts as reviewed and denied.\n\nWrites `allowBuilds: { <pkg>: false }` into the workspace yaml (or `package.json#aube.allowBuilds`) before the install runs, so the named package's lifecycle scripts stay skipped without tripping `strictDepBuilds=true`. Repeatable — pass the flag once per package.\n\nConflicts with `--no-save`, which only snapshots `package.json` and the lockfile and would leave an orphaned denial in the workspace yaml on restore. Also conflicts with `--allow-build` for the same package name.",
"help_long": "Mark a dependency's lifecycle scripts as reviewed and denied.\n\nWrites `allowBuilds: { <pkg>: false }` into the workspace yaml (or `package.json#aube.allowBuilds`) before the install runs, so the named package's lifecycle scripts stay skipped without tripping `strictDepBuilds=true`. Repeatable — pass the flag once per package.\n\nConflicts with `--no-save`, which only snapshots `package.json` and the lockfile and would leave an orphaned denial in the workspace yaml on restore. Also conflicts with `--allow-build` for the same package name and with `--dangerously-allow-all-builds`.",
"help_first_line": "Mark a dependency's lifecycle scripts as reviewed and denied",
"short": [],
"long": [
Expand Down
13 changes: 13 additions & 0 deletions test/global_install.bats
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,19 @@ teardown() {
assert_success
}

@test "aube add -g --dangerously-allow-all-builds runs unreviewed builds" {
AUBE_STRICT_DEP_BUILDS=true run aube add -g \
aube-test-builds-marker@1.0.0 \
--dangerously-allow-all-builds
assert_success
refute_output --partial "must be reviewed before install"
refute_output --partial "ignored build scripts"

pkg_dir="$AUBE_HOME/global-aube"
install_dir="$(find "$pkg_dir" -mindepth 1 -maxdepth 1 -type d -print -quit)"
assert_file_exists "$install_dir/aube-builds-marker.txt"
}

@test "aube add -g --deny-build=<pkg> marks a global dep's build scripts reviewed" {
AUBE_STRICT_DEP_BUILDS=true run aube add -g \
--deny-build=aube-test-builds-marker \
Expand Down
Loading