From 298edcf5fd8e8532dee24c1d32030173ee9f5db9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lomig=20Me=CC=81gard?= Date: Sat, 25 Apr 2026 22:07:25 +0200 Subject: [PATCH] docs(cli): Add CLI conventions --- CONTRIBUTING.md | 2 ++ crates/rite-cli/src/common.rs | 20 +++++++++++++++++--- crates/rite-cli/src/main.rs | 8 ++++++++ crates/rite-cli/src/run.rs | 4 ++-- crates/rite-cli/src/verify.rs | 2 ++ docs/cli-conventions.md | 24 ++++++++++++++++++++++++ 6 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 docs/cli-conventions.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0f3f90c..b1b1b65 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -3,6 +3,8 @@ Rite is early-stage and the design is still evolving. Pull requests are welcome, but opening an issue to discuss the change first is strongly preferred, it avoids wasted effort on both sides. +CLI behavior conventions are documented in `docs/cli-conventions.md`. + ## Local checks Run these before pushing. diff --git a/crates/rite-cli/src/common.rs b/crates/rite-cli/src/common.rs index 7b40bc8..c605cae 100644 --- a/crates/rite-cli/src/common.rs +++ b/crates/rite-cli/src/common.rs @@ -77,15 +77,15 @@ pub fn build_inputs( for s in params { let (key, value) = parse_key_value(s)?; - input_params.insert(key, serde_json::Value::String(value)); + input_params.insert(key.to_lowercase(), serde_json::Value::String(value)); } for s in roles { let (key, value) = parse_key_value(s)?; - input_roles.insert(key, value); + input_roles.insert(key.to_lowercase(), value); } for s in materials { let (key, value) = parse_key_value(s)?; - input_materials.insert(key, parse_material_value(&value)); + input_materials.insert(key.to_lowercase(), parse_material_value(&value)); } Ok(CeremonyInputs { @@ -213,4 +213,18 @@ mod tests { matches!(source, MaterialSource::Identifier { identifier } if identifier == "my-identifier") ); } + + #[test] + fn build_inputs_normalizes_cli_keys_to_lowercase() { + let inputs = build_inputs( + &["Param_Name=value".to_string()], + &["Role_ID=Alice".to_string()], + &["Material_ID=@file.pem".to_string()], + ) + .expect("inputs build"); + + assert!(inputs.parameters.contains_key("param_name")); + assert!(inputs.roles.contains_key("role_id")); + assert!(inputs.materials.contains_key("material_id")); + } } diff --git a/crates/rite-cli/src/main.rs b/crates/rite-cli/src/main.rs index 58b2439..eb0d5c2 100644 --- a/crates/rite-cli/src/main.rs +++ b/crates/rite-cli/src/main.rs @@ -10,10 +10,18 @@ mod verify; use clap::{CommandFactory, Parser, Subcommand}; use clap_complete::{Shell, generate}; +const TOP_LEVEL_AFTER_HELP: &str = "\ +Lifecycle: + rite check ceremony.rite.yaml + rite run ceremony.rite.yaml + rite verify +"; + #[derive(Parser)] #[command(name = "rite")] #[command(version)] #[command(about = "A CLI for cryptographic key ceremonies", long_about = None)] +#[command(after_help = TOP_LEVEL_AFTER_HELP)] struct Cli { #[command(subcommand)] command: Commands, diff --git a/crates/rite-cli/src/run.rs b/crates/rite-cli/src/run.rs index b4ca253..a0a52de 100644 --- a/crates/rite-cli/src/run.rs +++ b/crates/rite-cli/src/run.rs @@ -96,11 +96,11 @@ pub fn run(args: Args) { match executor.execute(&resolved, backend_registry) { Ok(_) => { - eprintln!("\nOutput directory: {}", output_dir.display()); + println!("Output directory: {}", output_dir.display()); std::process::exit(0); } Err(e) => { - eprintln!("\nCeremony failed: {e}"); + eprintln!("Ceremony failed: {e}"); std::process::exit(1); } } diff --git a/crates/rite-cli/src/verify.rs b/crates/rite-cli/src/verify.rs index 2459f76..b8df38e 100644 --- a/crates/rite-cli/src/verify.rs +++ b/crates/rite-cli/src/verify.rs @@ -63,6 +63,8 @@ pub fn run(args: Args) { status, events_count, } => { + // TODO: Revisit exit-code policy for Incomplete; currently uses 2 as a + // special-case status distinct from generic verification failure. eprintln!("Incomplete transcript (no final fingerprint)."); eprintln!(" Status: {status:?}"); eprintln!(" Events recorded: {events_count}"); diff --git a/docs/cli-conventions.md b/docs/cli-conventions.md new file mode 100644 index 0000000..5056adb --- /dev/null +++ b/docs/cli-conventions.md @@ -0,0 +1,24 @@ +# CLI Conventions + +Scope: `crates/rite-cli`. + +## Exit codes + +- `0`: success +- `1`: command/domain/runtime failure +- `2`: CLI usage and argument parsing errors + +## Output channels + +- `stdout`: successful user-consumable command output +- `stderr`: diagnostics, warnings, errors, and progress/status messages + +## Input key normalization + +- User-provided input keys should be normalized consistently across all input sources. +- This includes CLI flags (`--param`, `--role`, `--material`) and environment variables (`RITE_PARAM_*`, `RITE_ROLE_*`, `RITE_MATERIAL_*`). + +## Interactive prompting + +- Interactive prompts are enabled by default for interactive commands. +- Commands may provide explicit flags to disable prompting (for example `--no-prompt`).