diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a9a74d0..3bc36a4f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -190,9 +190,6 @@ jobs: run: cargo build --release --locked --target ${{ matrix.target }} ${{ matrix.build_flags }} working-directory: ./rage - - name: Generate completions - run: cargo run --example generate-completions - - name: Generate manpages run: cargo run --example generate-docs diff --git a/rage/Cargo.toml b/rage/Cargo.toml index 76389d5e..d79854f1 100644 --- a/rage/Cargo.toml +++ b/rage/Cargo.toml @@ -24,15 +24,15 @@ assets = [ ["target/release/rage", "usr/bin/", "755"], ["target/release/rage-keygen", "usr/bin/", "755"], ["target/release/rage-mount", "usr/bin/", "755"], - ["../target/completions/rage.bash", "usr/share/bash-completion/completions/rage", "644"], - ["../target/completions/rage-keygen.bash", "usr/share/bash-completion/completions/rage-keygen", "644"], - ["../target/completions/rage-mount.bash", "usr/share/bash-completion/completions/rage-mount", "644"], - ["../target/completions/rage.fish", "usr/share/fish/completions/", "644"], - ["../target/completions/rage-keygen.fish", "usr/share/fish/completions/", "644"], - ["../target/completions/rage-mount.fish", "usr/share/fish/completions/", "644"], - ["../target/completions/rage.zsh", "usr/share/zsh/functions/Completion/Debian/", "644"], - ["../target/completions/rage-keygen.zsh", "usr/share/zsh/functions/Completion/Debian/", "644"], - ["../target/completions/rage-mount.zsh", "usr/share/zsh/functions/Completion/Debian/", "644"], + ["target/release/completions/rage.bash", "usr/share/bash-completion/completions/rage", "644"], + ["target/release/completions/rage-keygen.bash", "usr/share/bash-completion/completions/rage-keygen", "644"], + ["target/release/completions/rage-mount.bash", "usr/share/bash-completion/completions/rage-mount", "644"], + ["target/release/completions/rage.fish", "usr/share/fish/completions/", "644"], + ["target/release/completions/rage-keygen.fish", "usr/share/fish/completions/", "644"], + ["target/release/completions/rage-mount.fish", "usr/share/fish/completions/", "644"], + ["target/release/completions/_rage", "usr/share/zsh/functions/Completion/Debian/", "644"], + ["target/release/completions/_rage-keygen", "usr/share/zsh/functions/Completion/Debian/", "644"], + ["target/release/completions/_rage-mount", "usr/share/zsh/functions/Completion/Debian/", "644"], ["../target/manpages/rage.1.gz", "usr/share/man/man1/", "644"], ["../target/manpages/rage-keygen.1.gz", "usr/share/man/man1/", "644"], ["../target/manpages/rage-mount.1.gz", "usr/share/man/man1/", "644"], @@ -75,8 +75,15 @@ tar = { version = "0.4", optional = true } time = { version = ">=0.3.7, <0.3.24", optional = true } # time 0.3.24 has MSRV 1.67 zip = { version = "0.6.2", optional = true } -[dev-dependencies] +[build-dependencies] +clap = { workspace = true, features = ["string", "unstable-styles"] } clap_complete = "4" +i18n-embed = { workspace = true, features = ["desktop-requester"] } +i18n-embed-fl.workspace = true +lazy_static.workspace = true +rust-embed.workspace = true + +[dev-dependencies] flate2 = "1" man = "0.3" trycmd = "0.14" diff --git a/rage/build.rs b/rage/build.rs new file mode 100644 index 00000000..dfabe856 --- /dev/null +++ b/rage/build.rs @@ -0,0 +1,82 @@ +use std::env; +use std::fs; +use std::io; +use std::path::Path; +use std::path::PathBuf; + +use clap::{Command, CommandFactory, ValueEnum}; +use clap_complete::{generate_to, Shell}; + +mod i18n { + include!("src/bin/rage/i18n.rs"); +} +mod rage { + include!("src/bin/rage/cli.rs"); +} +mod rage_keygen { + include!("src/bin/rage-keygen/cli.rs"); +} +mod rage_mount { + include!("src/bin/rage-mount/cli.rs"); +} + +#[macro_export] +macro_rules! fl { + ($message_id:literal) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id) + }}; + + ($message_id:literal, $($args:expr),* $(,)?) => {{ + i18n_embed_fl::fl!($crate::i18n::LANGUAGE_LOADER, $message_id, $($args), *) + }}; +} + +#[derive(Clone)] +struct Cli { + rage: Command, + rage_keygen: Command, + rage_mount: Command, +} + +impl Cli { + fn build() -> Self { + Self { + rage: rage::AgeOptions::command(), + rage_keygen: rage_keygen::AgeOptions::command(), + rage_mount: rage_mount::AgeMountOptions::command(), + } + } + + fn generate_completions(&mut self, out_dir: &Path) -> io::Result<()> { + fs::create_dir_all(out_dir)?; + + for &shell in Shell::value_variants() { + generate_to(shell, &mut self.rage, "rage", out_dir)?; + generate_to(shell, &mut self.rage_keygen, "rage-keygen", out_dir)?; + generate_to(shell, &mut self.rage_mount, "rage-mount", out_dir)?; + } + + Ok(()) + } +} + +fn main() -> io::Result<()> { + i18n::load_languages(); + + // `OUT_DIR` is "intentionally opaque as it is only intended for `rustc` interaction" + // (https://github.com/rust-lang/cargo/issues/9858). Peek into the black box and use + // it to figure out where the target directory is. + let out_dir = match env::var_os("OUT_DIR") { + None => return Ok(()), + Some(out_dir) => PathBuf::from(out_dir) + .ancestors() + .nth(3) + .expect("should be absolute path") + .to_path_buf(), + }; + + let mut cli = Cli::build(); + cli.generate_completions(&out_dir.join("completions"))?; + + Ok(()) +} diff --git a/rage/examples/generate-completions.rs b/rage/examples/generate-completions.rs deleted file mode 100644 index 527eb4a9..00000000 --- a/rage/examples/generate-completions.rs +++ /dev/null @@ -1,127 +0,0 @@ -use clap::{Arg, ArgAction, Command}; -use clap_complete::{generate, shells, Generator}; -use std::fs::{create_dir_all, File}; - -const COMPLETIONS_DIR: &str = "./target/completions"; - -fn generate_completion>( - gen: G, - app: &mut Command, - bin_name: S, - file_name: String, -) { - let mut file = File::create(format!("{}/{}", COMPLETIONS_DIR, file_name)) - .expect("Should be able to open file in target directory"); - generate::(gen, app, bin_name, &mut file); -} - -fn generate_completions(mut app: Command, bin_name: &str) { - generate_completion( - shells::Bash, - &mut app, - bin_name, - format!("{}.bash", bin_name), - ); - generate_completion( - shells::Elvish, - &mut app, - bin_name, - format!("{}.elv", bin_name), - ); - generate_completion( - shells::Fish, - &mut app, - bin_name, - format!("{}.fish", bin_name), - ); - generate_completion( - shells::PowerShell, - &mut app, - format!("{}.exe", bin_name), - format!("{}.ps1", bin_name), - ); - generate_completion(shells::Zsh, &mut app, bin_name, format!("{}.zsh", bin_name)); -} - -fn rage_completions() { - let app = Command::new("rage") - .arg(Arg::new("input")) - .arg( - Arg::new("encrypt") - .short('e') - .long("encrypt") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("decrypt") - .short('d') - .long("decrypt") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("passphrase") - .short('p') - .long("passphrase") - .action(ArgAction::SetTrue), - ) - .arg(Arg::new("max-work-factor").long("max-work-factor")) - .arg( - Arg::new("armor") - .short('a') - .long("armor") - .action(ArgAction::SetTrue), - ) - .arg( - Arg::new("recipient") - .short('r') - .long("recipient") - .action(ArgAction::Append), - ) - .arg( - Arg::new("recipients-file") - .short('R') - .long("recipients-file") - .action(ArgAction::Append), - ) - .arg( - Arg::new("identity") - .short('i') - .long("identity") - .action(ArgAction::Append), - ) - .arg(Arg::new("plugin-name").short('j')) - .arg(Arg::new("output").short('o').long("output")); - - generate_completions(app, "rage"); -} - -fn rage_keygen_completions() { - let app = Command::new("rage-keygen").arg(Arg::new("output").short('o').long("output")); - - generate_completions(app, "rage-keygen"); -} - -fn rage_mount_completions() { - let app = Command::new("rage-mount") - .arg(Arg::new("filename")) - .arg(Arg::new("mountpoint")) - .arg(Arg::new("types").short('t').long("types")) - .arg(Arg::new("max-work-factor").long("max-work-factor")) - .arg( - Arg::new("identity") - .short('i') - .long("identity") - .action(ArgAction::Append), - ); - - generate_completions(app, "rage-mount"); -} - -fn main() { - // Create the target directory if it does not exist. - let _ = create_dir_all(COMPLETIONS_DIR); - - rage_completions(); - rage_keygen_completions(); - rage_mount_completions(); -} diff --git a/supply-chain/config.toml b/supply-chain/config.toml index dc0d8d45..fbc3bf8d 100644 --- a/supply-chain/config.toml +++ b/supply-chain/config.toml @@ -179,7 +179,7 @@ criteria = "safe-to-deploy" [[exemptions.clap_complete]] version = "4.3.2" -criteria = "safe-to-run" +criteria = "safe-to-deploy" [[exemptions.clap_derive]] version = "4.3.12"