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
10 changes: 10 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ cap-std-ext = { workspace = true, features = ["fs_utf8"] }
cfg-if = { workspace = true }
chrono = { workspace = true, features = ["serde"] }
clap = { workspace = true, features = ["derive","cargo"] }
clap_complete = "4"
clap_mangen = { workspace = true, optional = true }
composefs = { workspace = true }
composefs-boot = { workspace = true }
Expand Down
44 changes: 44 additions & 0 deletions crates/lib/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use anyhow::{anyhow, ensure, Context, Result};
use camino::{Utf8Path, Utf8PathBuf};
use cap_std_ext::cap_std;
use cap_std_ext::cap_std::fs::Dir;
use clap::CommandFactory;
use clap::Parser;
use clap::ValueEnum;
use composefs::dumpfile;
Expand Down Expand Up @@ -733,6 +734,15 @@ pub(crate) enum Opt {
/// Diff current /etc configuration versus default
#[clap(hide = true)]
ConfigDiff,
/// Generate shell completion script for supported shells.
///
/// Example: `bootc completion bash` prints a bash completion script to stdout.
#[clap(hide = true)]
Completion {
/// Shell type to generate (bash, zsh, fish)
#[clap(value_enum)]
shell: clap_complete::aot::Shell,
},
#[clap(hide = true)]
DeleteDeployment {
depl_id: String,
Expand Down Expand Up @@ -1573,6 +1583,15 @@ async fn run_from_opt(opt: Opt) -> Result<()> {
Ok(())
}
},
Opt::Completion { shell } => {
use clap_complete::aot::generate;

let mut cmd = Opt::command();
let mut stdout = std::io::stdout();
let bin_name = "bootc";
generate(shell, &mut cmd, bin_name, &mut stdout);
Ok(())
}
Opt::Image(opts) => match opts {
ImageOpts::List {
list_type,
Expand Down Expand Up @@ -1978,4 +1997,29 @@ mod tests {
]));
assert_eq!(args.as_slice(), ["container", "image", "pull"]);
}

#[test]
fn test_generate_completion_scripts_contain_commands() {
use clap_complete::aot::{generate, Shell};

// For each supported shell, generate the completion script and
// ensure obvious subcommands appear in the output. This mirrors
// the style of completion checks used in other projects (e.g.
// podman) where the generated script is examined for expected
// tokens.

// `completion` is intentionally hidden from --help / suggestions;
// ensure other visible subcommands are present instead.
let want = ["install", "upgrade"];

for shell in [Shell::Bash, Shell::Zsh, Shell::Fish] {
let mut cmd = Opt::command();
let mut buf = Vec::new();
generate(shell, &mut cmd, "bootc", &mut buf);
let s = String::from_utf8(buf).expect("completion should be utf8");
for w in &want {
assert!(s.contains(w), "{shell:?} completion missing {w}");
}
}
}
}