From c6318f41268679cba5e17b7206c5f93ef3948aab Mon Sep 17 00:00:00 2001 From: "blu@blu.cx" <38222767+thebrandonlucas@users.noreply.github.com> Date: Mon, 26 Oct 2026 13:04:21 -0500 Subject: [PATCH] fix(cli): accept --all on environments, organizations, and profiles list These local lists return every item at once but rejected --all, unlike the API lists fixed in #94. They now accept it and ignore it, through the same argument. A test walks the command tree so a new list cannot miss --all. Closes #119. --- CHANGELOG.md | 1 + docs/guides/requests.md | 2 ++ src/cli.rs | 80 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 76 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39995ce..b21f9b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ User-visible changes to `voltage`, newest first. Each pull request that changes ## Unreleased +- `organizations list`, `environments list`, and `profiles list` accept `--all` and ignore it, like the other lists that return every item at once. Before, they rejected it. - Filter `payments list` with `--check-only true|false`, and `wallets ledger list` with `--event-type adjustment`, from the refreshed API contract. Omit `--event-type` for payment ledger entries. Filters carry across pagination. - `webhooks test WEBHOOK_ID` without `--data` sends a test event, with a new `delivery_id` and a payload of type `test` and event `created`. Before, it failed with "This operation requires --data". `--data` still sends your own payload. - A list that returns every item in one response, such as `wallets list`, accepts `--all` and ignores it, because there are no more pages. Before, it rejected `--all`. `--all` still pages through lists that take `--limit`. diff --git a/docs/guides/requests.md b/docs/guides/requests.md index 29364c6..fafe639 100644 --- a/docs/guides/requests.md +++ b/docs/guides/requests.md @@ -36,4 +36,6 @@ voltage payments list --profile prod --statuses completed --statuses failed \ `--all` follows pagination by cursor, which the API recommends and the CLI requests by default; endpoints that only page by offset are followed by offset and count. The API has deprecated explicit offset paging (`--offset`, `--pagination offset`) where cursors exist, and the CLI warns when you use it. `--timeout SECONDS` (default 60) bounds the whole request, including paging and waits. +Every `list` command accepts `--all`. A list without `--limit`, such as `wallets list`, `organizations list`, `environments list`, or `profiles list`, returns every item at once, so it ignores `--all`. + Related: [safety model](../concepts/safety-model.md), [output reference](../reference/output.md). diff --git a/src/cli.rs b/src/cli.rs index d3899d4..c825101 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -292,12 +292,14 @@ pub struct ImportKeyFlags { #[derive(Debug, Subcommand)] pub enum ListCommand { + #[command(arg = all_at_once_arg())] List, } #[derive(Debug, Subcommand, Eq, PartialEq)] pub enum ProfileCommand { /// Show every saved profile + #[command(arg = all_at_once_arg())] List, /// Show one profile Get { name: String }, @@ -1027,18 +1029,21 @@ fn list_args(cmd: ClapCommand, operation: &Operation) -> ClapCommand { if operation.has_parameter("limit") { PaginationFlags::augment_args(cmd) } else if operation.lists_everything_at_once() { - cmd.arg( - Arg::new("all_at_once") - .long("all") - .action(ArgAction::SetTrue) - .help("No effect: this list returns every item in one response") - .help_heading("Query"), - ) + cmd.arg(all_at_once_arg()) } else { cmd } } +/// `--all` on a list without paging, such as `environments list`: accepted and ignored. +fn all_at_once_arg() -> Arg { + Arg::new("all_at_once") + .long("all") + .action(ArgAction::SetTrue) + .help("No effect: this list shows every item at once") + .help_heading("Query") +} + /// The resource ID argument and `--id`, one of which is required. A wallet command can /// name its wallet with `--wallet` instead, so `target_id` checks that one was given. fn target_args(cmd: ClapCommand, target: ResourceTarget, wallet: bool) -> ClapCommand { @@ -1556,6 +1561,67 @@ mod tests { ); } + #[test] + fn all_is_accepted_by_every_list_command() { + fn lists(cmd: &ClapCommand, path: &mut Vec, found: &mut Vec<(String, bool)>) { + path.push(cmd.get_name().into()); + if cmd.get_name() == "list" { + let all = cmd.get_arguments().any(|arg| arg.get_long() == Some("all")); + found.push((path.join(" "), all)); + } + for sub in cmd.get_subcommands() { + lists(sub, path, found); + } + path.pop(); + } + let mut found = Vec::new(); + lists(&command(), &mut Vec::new(), &mut found); + assert!( + found + .iter() + .any(|(path, _)| path == "voltage environments list"), + "{found:?}" + ); + let missing: Vec<_> = found.iter().filter(|(_, all)| !all).collect(); + assert!(missing.is_empty(), "lists without --all: {missing:?}"); + } + + #[test] + fn all_is_ignored_by_local_lists_and_rejected_by_other_local_commands() { + let parse = |args: &[&str]| { + Invocation::try_parse_from(["voltage"].iter().chain(args)).map(|i| i.command) + }; + assert!(matches!( + parse(&["environments", "list", "--all"]), + Ok(Command::Local(LocalCommand::Environments { + command: ListCommand::List + })) + )); + assert!(matches!( + parse(&["organizations", "list", "--all"]), + Ok(Command::Local(LocalCommand::Organizations { + command: ListCommand::List + })) + )); + assert!(matches!( + parse(&["profiles", "list", "--all"]), + Ok(Command::Local(LocalCommand::Profiles { + command: ProfileCommand::List + })) + )); + for args in [ + &["profiles", "get", "stage", "--all"][..], + &["context", "--all"], + ] { + let error = parse(args).unwrap_err(); + assert!( + error.message.contains("unexpected argument '--all'"), + "{args:?}: {}", + error.message + ); + } + } + #[test] fn wallet_flag_names_the_wallet_a_wallet_command_acts_on() { const OTHER: &str = "44444444-4444-4444-8444-444444444444";