Skip to content
This repository was archived by the owner on Feb 16, 2024. It is now read-only.

Commit eda3d22

Browse files
committed
Support generation of shell completions (#54)
## Description For #37 This was surprisingly easy. Following the same semantics as `kubectl`. Docs will be added via c97f6ac in #36. We can copy parts of `kubectl` docs You can try it out with `source <(stackablectl completion bash)` (for bash)
1 parent c65688f commit eda3d22

File tree

8 files changed

+96
-23
lines changed

8 files changed

+96
-23
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Added
6+
7+
- Support generation of shell completions ([#54](https://github.com/stackabletech/stackablectl/pull/54))
8+
59
### Changed
610

711
- Update Rust and Go libs ([#46](https://github.com/stackabletech/stackablectl/pull/46))

Cargo.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ repository = "https://github.com/stackabletech/stackablectl"
1010
[dependencies]
1111
cached = "0.37"
1212
clap = { version = "3.2", features = ["derive", "cargo"] }
13+
clap_complete = "3.2"
1314
env_logger = "0.9"
1415
indexmap = { version = "1.9", features = ["serde"] }
1516
lazy_static = "1.4"

src/arguments.rs

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,72 @@
11
use crate::{operator::CliCommandOperator, release::CliCommandRelease, stack::CliCommandStack};
2-
use clap::{ArgEnum, Parser};
2+
use clap::{ArgEnum, Command, Parser, ValueHint};
3+
use clap_complete::{generate, Generator, Shell};
34
use log::LevelFilter;
5+
use std::io;
46

57
#[derive(Parser)]
68
#[clap(author, version, about)]
79
pub struct CliArgs {
810
#[clap(subcommand)]
911
pub cmd: CliCommand,
1012

11-
/// Log level. One of Error, Warn, Info, Debug or Trace
12-
#[clap(short, long, default_value = "Info")]
13-
pub log_level: LevelFilter,
13+
/// Log level.
14+
#[clap(short, long, arg_enum, default_value = "info")]
15+
pub log_level: LogLevel,
1416

1517
/// Namespace where to deploy the products and operators
16-
#[clap(short, long, default_value = "default")]
18+
#[clap(short, long, default_value = "default", value_hint = ValueHint::Other)]
1719
pub namespace: String,
1820

21+
/// Overwrite the URL of the stable helm repo
22+
///
1923
/// If you don't have access to the Stackable Helm repos you can mirror the repo and provide the URL here
2024
/// (e.g. <https://my.repo/repository/stackable-stable/>).
2125
#[clap(
2226
long,
23-
default_value = "https://repo.stackable.tech/repository/helm-stable"
27+
default_value = "https://repo.stackable.tech/repository/helm-stable",
28+
value_hint = ValueHint::Url,
2429
)]
2530
pub helm_repo_stackable_stable: String,
2631

32+
/// Overwrite the URL of the test helm repo
33+
///
2734
/// If you don't have access to the Stackable Helm repos you can mirror the repo and provide the URL here
2835
/// (e.g. <https://my.repo/repository/stackable-test/>).
2936
#[clap(
3037
long,
31-
default_value = "https://repo.stackable.tech/repository/helm-test"
38+
default_value = "https://repo.stackable.tech/repository/helm-test",
39+
value_hint = ValueHint::Url,
3240
)]
3341
pub helm_repo_stackable_test: String,
3442

43+
/// Overwrite the URL of the dev helm repo
44+
///
3545
/// If you don't have access to the Stackable Helm repos you can mirror the repo and provide the URL here
3646
/// (e.g. <https://my.repo/repository/stackable-dev/>).
3747
#[clap(
3848
long,
39-
default_value = "https://repo.stackable.tech/repository/helm-dev"
49+
default_value = "https://repo.stackable.tech/repository/helm-dev",
50+
value_hint = ValueHint::Url,
4051
)]
4152
pub helm_repo_stackable_dev: String,
4253

54+
/// Adds a YAML file containing custom releases
55+
///
4356
/// If you don't have access to the Stackable GitHub repos or you want to maintain your own releases you can specify additional YAML files containing release information.
4457
/// Have a look [here](https://raw.githubusercontent.com/stackabletech/stackablectl/main/releases.yaml) for the structure.
4558
/// Can either be an URL or a path to a file e.g. `https://my.server/my-releases.yaml` or '/etc/my-releases.yaml' or `C:\Users\Sebastian\my-releases.yaml`.
4659
/// Can be specified multiple times.
47-
#[clap(long, multiple_occurrences(true))]
60+
#[clap(long, multiple_occurrences(true), value_hint = ValueHint::FilePath)]
4861
pub additional_release_files: Vec<String>,
4962

63+
/// Adds a YAML file containing custom stacks
64+
///
5065
/// If you don't have access to the Stackable GitHub repos or you want to maintain your own stacks you can specify additional YAML files containing stack information.
5166
/// Have a look [here](https://raw.githubusercontent.com/stackabletech/stackablectl/main/stacks.yaml) for the structure.
5267
/// Can either be an URL or a path to a file e.g. `https://my.server/my-stacks.yaml` or '/etc/my-stacks.yaml' or `C:\Users\Sebastian\my-stacks.yaml`.
5368
/// Can be specified multiple times.
54-
#[clap(long, multiple_occurrences(true))]
69+
#[clap(long, multiple_occurrences(true), value_hint = ValueHint::FilePath)]
5570
pub additional_stack_files: Vec<String>,
5671
}
5772

@@ -68,6 +83,9 @@ pub enum CliCommand {
6883
/// This subcommand interacts with stacks, which are ready-to-use combinations of products.
6984
#[clap(subcommand, alias("s"), alias("st"))]
7085
Stack(CliCommandStack),
86+
87+
/// Output shell completion code for the specified shell.
88+
Completion(CliCommandCompletion),
7189
}
7290

7391
#[derive(Clone, Parser, ArgEnum)]
@@ -76,3 +94,35 @@ pub enum OutputType {
7694
Json,
7795
Yaml,
7896
}
97+
98+
#[derive(Clone, Copy, Parser, Debug, ArgEnum)]
99+
pub enum LogLevel {
100+
Error,
101+
Warn,
102+
Info,
103+
Debug,
104+
Trace,
105+
}
106+
107+
impl From<LogLevel> for LevelFilter {
108+
fn from(val: LogLevel) -> Self {
109+
match val {
110+
LogLevel::Error => LevelFilter::Error,
111+
LogLevel::Warn => LevelFilter::Warn,
112+
LogLevel::Info => LevelFilter::Info,
113+
LogLevel::Debug => LevelFilter::Debug,
114+
LogLevel::Trace => LevelFilter::Trace,
115+
}
116+
}
117+
}
118+
119+
#[derive(Parser)]
120+
pub struct CliCommandCompletion {
121+
// Shell to generate the completions for
122+
#[clap(arg_enum, value_parser)]
123+
pub shell: Shell,
124+
}
125+
126+
pub fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
127+
generate(gen, cmd, cmd.get_name().to_string(), &mut io::stdout());
128+
}

src/helm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn handle_common_cli_args(args: &CliArgs) {
4747
args.helm_repo_stackable_dev.clone(),
4848
);
4949

50-
*(LOG_LEVEL.lock().unwrap()) = args.log_level;
50+
*(LOG_LEVEL.lock().unwrap()) = args.log_level.into();
5151

5252
let namespace = &args.namespace;
5353
if namespace != "default" {

src/main.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::arguments::CliCommand;
22
use arguments::CliArgs;
3-
use clap::Parser;
3+
use clap::{IntoApp, Parser};
44

55
mod arguments;
66
mod helm;
@@ -37,7 +37,7 @@ fn main() {
3737
env_logger::builder()
3838
.format_timestamp(None)
3939
.format_target(false)
40-
.filter_level(args.log_level)
40+
.filter_level(args.log_level.into())
4141
.init();
4242
helm::handle_common_cli_args(&args);
4343
release::handle_common_cli_args(&args);
@@ -47,5 +47,9 @@ fn main() {
4747
CliCommand::Operator(command) => command.handle(),
4848
CliCommand::Release(command) => command.handle(),
4949
CliCommand::Stack(command) => command.handle(),
50+
CliCommand::Completion(command) => {
51+
let mut cmd = CliArgs::command();
52+
arguments::print_completions(command.shell, &mut cmd);
53+
}
5054
}
5155
}

src/operator.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::{arguments::OutputType, helm, helm::HELM_REPOS, kind, AVAILABLE_OPERATORS};
2-
use clap::Parser;
2+
use clap::{Parser, ValueHint};
33
use indexmap::IndexMap;
44
use log::{info, warn};
55
use serde::Serialize;
@@ -16,6 +16,7 @@ pub enum CliCommandOperator {
1616
/// Show details of a specific operator
1717
#[clap(alias("desc"))]
1818
Describe {
19+
#[clap(value_hint = ValueHint::Other)]
1920
operator: String,
2021
#[clap(short, long, arg_enum, default_value = "text")]
2122
output: OutputType,
@@ -26,7 +27,7 @@ pub enum CliCommandOperator {
2627
/// Space separated list of operators to install.
2728
/// Must have the form `name[=version]` e.g. `superset`, `superset=0.3.0`, `superset=0.3.0-nightly` or `superset=0.3.0-pr123`.
2829
/// You can get the available versions with `stackablectl operator list` or `stackablectl operator describe superset`
29-
#[clap(multiple_occurrences(true), required = true)]
30+
#[clap(multiple_occurrences(true), required = true, value_hint = ValueHint::Other)]
3031
operators: Vec<Operator>,
3132

3233
/// If specified a local kubernetes cluster consisting of 4 nodes for testing purposes will be created.
@@ -39,15 +40,16 @@ pub enum CliCommandOperator {
3940
#[clap(
4041
long,
4142
default_value = "stackable-data-platform",
42-
requires = "kind-cluster"
43+
requires = "kind-cluster",
44+
value_hint = ValueHint::Other,
4345
)]
4446
kind_cluster_name: String,
4547
},
4648
/// Uninstall a operator
4749
#[clap(alias("un"))]
4850
Uninstall {
4951
/// Space separated list of operators to uninstall.
50-
#[clap(multiple_occurrences(true), required = true)]
52+
#[clap(multiple_occurrences(true), required = true, value_hint = ValueHint::Other)]
5153
operators: Vec<String>,
5254
},
5355
/// List installed operators

src/release.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::{arguments::OutputType, helpers, kind, operator, operator::Operator, CliArgs};
22
use cached::proc_macro::cached;
3-
use clap::{ArgGroup, Parser};
3+
use clap::{ArgGroup, Parser, ValueHint};
44
use indexmap::IndexMap;
55
use lazy_static::lazy_static;
66
use log::{error, info, warn};
@@ -24,6 +24,7 @@ pub enum CliCommandRelease {
2424
/// Show details of a specific release
2525
#[clap(alias("desc"))]
2626
Describe {
27+
#[clap(value_hint = ValueHint::Other)]
2728
release: String,
2829
#[clap(short, long, arg_enum, default_value = "text")]
2930
output: OutputType,
@@ -37,17 +38,17 @@ pub enum CliCommandRelease {
3738
))]
3839
Install {
3940
/// Name of the release to install
40-
#[clap(required = true)]
41+
#[clap(required = true, value_hint = ValueHint::Other)]
4142
release: String,
4243

4344
/// Whitelist of product operators to install.
4445
/// Mutually exclusive with `--exclude-products`
45-
#[clap(short, long)]
46+
#[clap(short, long, value_hint = ValueHint::Other)]
4647
include_products: Vec<String>,
4748

4849
/// Blacklist of product operators to install.
4950
/// Mutually exclusive with `--include-products`
50-
#[clap(short, long)]
51+
#[clap(short, long, value_hint = ValueHint::Other)]
5152
exclude_products: Vec<String>,
5253

5354
/// If specified a local kubernetes cluster consisting of 4 nodes for testing purposes will be created.
@@ -60,15 +61,16 @@ pub enum CliCommandRelease {
6061
#[clap(
6162
long,
6263
default_value = "stackable-data-platform",
63-
requires = "kind-cluster"
64+
requires = "kind-cluster",
65+
value_hint = ValueHint::Other,
6466
)]
6567
kind_cluster_name: String,
6668
},
6769
/// Uninstall a release
6870
#[clap(alias("un"))]
6971
Uninstall {
7072
/// Name of the release to uninstall
71-
#[clap(required = true)]
73+
#[clap(required = true, value_hint = ValueHint::Other)]
7274
release: String,
7375
},
7476
}

0 commit comments

Comments
 (0)