diff --git a/Cargo.lock b/Cargo.lock index aa54e4dc..3deb3509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4439,6 +4439,7 @@ dependencies = [ "anyhow", "clap", "clap-markdown", + "clap_complete", "serde", "serde_json", ] diff --git a/docs/tools/swarmctl-cli.md b/docs/tools/swarmctl-cli.md index 1462dab8..1b654fd0 100644 --- a/docs/tools/swarmctl-cli.md +++ b/docs/tools/swarmctl-cli.md @@ -8,6 +8,7 @@ This document contains the help content for the `swarmctl` command-line program. * [`swarmctl user`↴](#swarmctl-user) * [`swarmctl user add`↴](#swarmctl-user-add) * [`swarmctl user update`↴](#swarmctl-user-update) +* [`swarmctl completions`↴](#swarmctl-completions) ## `swarmctl` @@ -18,6 +19,7 @@ swarm-level operator CLI ###### **Subcommands:** * `user` — Manage subjects in the swarm's SSO provider +* `completions` — Generate a shell completion script for `swarmctl` and print it to stdout ###### **Options:** @@ -81,6 +83,25 @@ Every flag is optional and they compose, so one call can set several things at o +## `swarmctl completions` + +Generate a shell completion script for `swarmctl` and print it to stdout. + +Supports bash, zsh, fish, elvish and powershell. The nix package already installs bash/zsh/fish system-wide; this is for ad-hoc or other-shell use. + +Dispatched before `PathArgs::resolve()` for the same reason as `markdown-docs`: emitting a completion script needs none of the `SWARMCTL_AUTHELIA_*` deployment env vars, and requiring them would make the package's own build-time invocation fail. + +**Usage:** `swarmctl completions ` + +###### **Arguments:** + +* `` — Shell to emit completions for + + Possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh` + + + +
diff --git a/nix/packages/default.nix b/nix/packages/default.nix index f47724d0..b4cc3bca 100644 --- a/nix/packages/default.nix +++ b/nix/packages/default.nix @@ -155,7 +155,27 @@ in # the one host that runs the controller, and belongs in that hive's # closure only. Kept a separate derivation rather than a second binary # in the daemon's package so a hive can pin one without the other. - swarmctl = mkBinPackage "swarmctl" "hyperhive swarm-level operator CLI"; + # + # Not `mkBinPackage`, because of the completions: they come from the + # binary's own `completions ` verb, which walks the live clap + # tree, so they cannot drift from the actual verbs. Same shape as + # `hivectlPkg` above. + swarmctl = + pkgs.runCommand "swarmctl" + { + nativeBuildInputs = [ pkgs.installShellFiles ]; + meta = { + description = "hyperhive swarm-level operator CLI"; + mainProgram = "swarmctl"; + }; + } + '' + install -Dm755 ${workspaceBuild}/bin/swarmctl $out/bin/swarmctl + installShellCompletion --cmd swarmctl \ + --bash <("$out/bin/swarmctl" completions bash) \ + --zsh <("$out/bin/swarmctl" completions zsh) \ + --fish <("$out/bin/swarmctl" completions fish) + ''; # Static build of the swarm-level UI shell — see ./swarm-ui.nix. Same # "swarm-scoped, not core-bundle" reasoning as swarm-controller/swarmctl diff --git a/swarmctl/Cargo.toml b/swarmctl/Cargo.toml index ed572751..f9865229 100644 --- a/swarmctl/Cargo.toml +++ b/swarmctl/Cargo.toml @@ -12,6 +12,7 @@ path = "src/main.rs" anyhow.workspace = true clap.workspace = true clap-markdown = "0.1" +clap_complete.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/swarmctl/src/main.rs b/swarmctl/src/main.rs index 416bbee1..3eee3917 100644 --- a/swarmctl/src/main.rs +++ b/swarmctl/src/main.rs @@ -143,6 +143,21 @@ enum Verb { /// outside a real deployment — exactly where the docs build runs it. #[command(hide = true)] MarkdownDocs, + /// Generate a shell completion script for `swarmctl` and print it to + /// stdout. + /// + /// Supports bash, zsh, fish, elvish and powershell. The nix package + /// already installs bash/zsh/fish system-wide; this is for ad-hoc or + /// other-shell use. + /// + /// Dispatched before `PathArgs::resolve()` for the same reason as + /// `markdown-docs`: emitting a completion script needs none of the + /// `SWARMCTL_AUTHELIA_*` deployment env vars, and requiring them would + /// make the package's own build-time invocation fail. + Completions { + /// Shell to emit completions for. + shell: clap_complete::Shell, + }, } #[derive(Subcommand)] @@ -209,6 +224,19 @@ fn main() -> Result<()> { print!("{}", clap_markdown::help_markdown::()); Ok(()) } + Verb::Completions { shell } => { + // Generated from the live clap tree — the same single source + // of truth `markdown-docs` renders — so completions cannot + // drift from the actual verbs and flags. + use clap::CommandFactory as _; + clap_complete::generate( + shell, + &mut Cli::command(), + "swarmctl", + &mut std::io::stdout(), + ); + Ok(()) + } } }