2021-03-23 22:14:04 +01:00
//! Global CLI Setup.
2021-03-23 22:14:04 +01:00
use clap ::{ App , AppSettings , Arg , ArgMatches , SubCommand } ;
2021-11-18 22:15:20 +01:00
use const_format ::concatcp ;
2021-11-18 07:21:00 +01:00
use lazy_static ::lazy_static ;
2021-03-23 22:14:04 +01:00
use crate ::command ;
2021-11-18 22:15:20 +01:00
/// Base URL of the manual, without the trailing slash.
2021-11-23 22:33:23 +01:00
const MANUAL_URL_BASE : & str = " https://zhaofengli.github.io/colmena " ;
2021-11-18 22:15:20 +01:00
/// URL to the manual.
///
/// We maintain CLI and Nix API stability for each minor version.
/// This ensures that the user always sees accurate documentations, and we can
/// easily perform updates to the manual after a release.
2021-11-23 22:33:23 +01:00
const MANUAL_URL : & str = concatcp! ( MANUAL_URL_BASE , " / " , env! ( " CARGO_PKG_VERSION_MAJOR " ) , " . " , env! ( " CARGO_PKG_VERSION_MINOR " ) ) ;
2021-11-18 22:15:20 +01:00
/// The note shown when the user is using a pre-release version.
///
/// API stability cannot be guaranteed for pre-release versions.
/// Links to the version currently in development automatically
/// leads the user to the unstable manual.
2021-11-23 22:33:23 +01:00
const MANUAL_DISCREPANCY_NOTE : & str = " Note: You are using a pre-release version of Colmena, so the supported options may be different from what's in the manual. " ;
2021-11-18 22:15:20 +01:00
2021-11-18 07:21:00 +01:00
lazy_static! {
2021-11-18 22:15:20 +01:00
static ref LONG_ABOUT : String = {
let mut message = format! ( r #" NixOS deployment tool
Colmena helps you deploy to multiple hosts running NixOS .
For more details , read the manual at < { } > .
" #, MANUAL_URL);
2021-11-23 22:33:23 +01:00
if ! env! ( " CARGO_PKG_VERSION_PRE " ) . is_empty ( ) {
message + = MANUAL_DISCREPANCY_NOTE ;
2021-11-18 22:15:20 +01:00
}
message
} ;
2021-11-18 07:21:00 +01:00
static ref CONFIG_HELP : String = {
format! ( r #" If this argument is not specified, Colmena will search upwards from the current working directory for a file named " flake . nix " or " hive . nix " . This behavior is disabled if --config/-f is given explicitly.
2021-11-18 22:15:20 +01:00
For a sample configuration , check the manual at < { } > .
" #, MANUAL_URL)
2021-11-18 07:21:00 +01:00
} ;
}
2021-03-23 22:14:04 +01:00
macro_rules ! register_command {
( $module :ident , $app :ident ) = > {
$app = $app . subcommand ( command ::$module ::subcommand ( ) ) ;
} ;
}
macro_rules ! handle_command {
( $module :ident , $matches :ident ) = > {
if let Some ( sub_matches ) = $matches . subcommand_matches ( stringify! ( $module ) ) {
2021-11-18 22:15:20 +01:00
crate ::troubleshooter ::run_wrapped (
& $matches , & sub_matches ,
command ::$module ::run ,
) . await ;
2021-03-23 22:14:04 +01:00
return ;
}
} ;
( $name :expr , $module :ident , $matches :ident ) = > {
if let Some ( sub_matches ) = $matches . subcommand_matches ( $name ) {
2021-11-18 22:15:20 +01:00
crate ::troubleshooter ::run_wrapped (
& $matches , & sub_matches ,
command ::$module ::run ,
) . await ;
2021-03-23 22:14:04 +01:00
return ;
}
} ;
}
2021-03-23 22:14:04 +01:00
pub fn build_cli ( include_internal : bool ) -> App < 'static , 'static > {
2021-11-18 07:21:00 +01:00
let version = env! ( " CARGO_PKG_VERSION " ) ;
2021-03-23 22:14:04 +01:00
let mut app = App ::new ( " Colmena " )
2021-03-23 22:14:04 +01:00
. bin_name ( " colmena " )
2021-11-18 07:21:00 +01:00
. version ( version )
2021-03-23 22:14:04 +01:00
. author ( " Zhaofeng Li <hello@zhaofeng.li> " )
. about ( " NixOS deployment tool " )
2021-11-18 22:15:20 +01:00
. long_about ( LONG_ABOUT . as_str ( ) )
2021-03-23 22:14:04 +01:00
. global_setting ( AppSettings ::ColoredHelp )
. setting ( AppSettings ::ArgRequiredElseHelp )
. arg ( Arg ::with_name ( " config " )
. short ( " f " )
. long ( " config " )
. value_name ( " CONFIG " )
2021-06-29 10:19:13 +02:00
. help ( " Path to a Hive expression, a flake.nix, or a Nix Flake URI " )
2021-03-23 22:14:04 +01:00
// The default value is a lie (sort of)!
//
// The default behavior is to search upwards from the
2021-06-29 10:02:43 +02:00
// current working directory for a file named "flake.nix"
// or "hive.nix". This behavior is disabled if --config/-f
// is explicitly supplied by the user (occurrences_of > 0).
2021-03-23 22:14:04 +01:00
. default_value ( " hive.nix " )
2021-11-18 07:21:00 +01:00
. long_help ( & CONFIG_HELP )
2021-03-23 22:14:04 +01:00
. global ( true ) )
. arg ( Arg ::with_name ( " show-trace " )
. long ( " show-trace " )
. help ( " Show debug information for Nix commands " )
. long_help ( " Passes --show-trace to Nix commands " )
. global ( true )
. takes_value ( false ) ) ;
2021-03-23 22:14:04 +01:00
if include_internal {
app = app . subcommand ( SubCommand ::with_name ( " gen-completions " )
. about ( " Generate shell auto-completion files (Internal) " )
. setting ( AppSettings ::Hidden )
. arg ( Arg ::with_name ( " shell " )
. index ( 1 )
. required ( true )
. takes_value ( true ) ) ) ;
2021-10-29 02:27:30 +02:00
2021-11-18 07:21:00 +01:00
app = app . subcommand ( SubCommand ::with_name ( " gen-help-markdown " )
. about ( " Generate CLI usage guide as Markdown (Internal) " )
. setting ( AppSettings ::Hidden ) ) ;
2021-10-29 02:27:30 +02:00
// deprecated alias
app = app . subcommand ( command ::eval ::deprecated_alias ( ) ) ;
2021-11-17 06:01:33 +01:00
register_command! ( test_progress , app ) ;
2021-03-23 22:14:04 +01:00
}
2021-03-23 22:14:04 +01:00
register_command! ( apply , app ) ;
register_command! ( apply_local , app ) ;
register_command! ( build , app ) ;
2021-10-29 02:27:30 +02:00
register_command! ( eval , app ) ;
2021-03-23 22:14:04 +01:00
register_command! ( upload_keys , app ) ;
register_command! ( exec , app ) ;
2021-06-29 10:02:43 +02:00
register_command! ( nix_info , app ) ;
2021-03-23 22:14:04 +01:00
app
}
pub async fn run ( ) {
2021-03-23 22:14:04 +01:00
let mut app = build_cli ( true ) ;
2021-03-23 22:14:04 +01:00
let matches = app . clone ( ) . get_matches ( ) ;
handle_command! ( apply , matches ) ;
handle_command! ( " apply-local " , apply_local , matches ) ;
handle_command! ( build , matches ) ;
2021-10-29 02:27:30 +02:00
handle_command! ( eval , matches ) ;
2021-03-23 22:14:04 +01:00
handle_command! ( " upload-keys " , upload_keys , matches ) ;
handle_command! ( exec , matches ) ;
2021-06-29 10:02:43 +02:00
handle_command! ( " nix-info " , nix_info , matches ) ;
2021-03-23 22:14:04 +01:00
2021-10-29 02:27:30 +02:00
// deprecated alias
handle_command! ( " introspect " , eval , matches ) ;
2021-11-17 06:01:33 +01:00
handle_command! ( " test-progress " , test_progress , matches ) ;
2021-03-23 22:14:04 +01:00
if let Some ( args ) = matches . subcommand_matches ( " gen-completions " ) {
return gen_completions ( args ) ;
}
2021-11-23 22:33:23 +01:00
if matches . subcommand_matches ( " gen-help-markdown " ) . is_some ( ) {
2021-11-18 07:21:00 +01:00
return gen_help_markdown ( ) ;
} ;
2021-03-23 22:14:04 +01:00
app . print_long_help ( ) . unwrap ( ) ;
println! ( ) ;
}
2021-03-23 22:14:04 +01:00
fn gen_completions ( args : & ArgMatches < '_ > ) {
let mut app = build_cli ( false ) ;
let shell : clap ::Shell = args . value_of ( " shell " ) . unwrap ( )
. parse ( ) . unwrap ( ) ;
app . gen_completions_to ( " colmena " , shell , & mut std ::io ::stdout ( ) ) ;
}
2021-11-18 07:21:00 +01:00
fn gen_help_markdown ( ) {
// This is tailered only for the manual, with output injected to `reference/cli.md`.
// <pre><div class="hljs">
let mut commands = vec! [
build_cli ( false ) ,
command ::apply ::subcommand ( ) ,
command ::apply_local ::subcommand ( ) ,
command ::build ::subcommand ( ) ,
command ::upload_keys ::subcommand ( ) ,
command ::eval ::subcommand ( ) ,
command ::exec ::subcommand ( ) ,
command ::nix_info ::subcommand ( ) ,
] ;
for command in commands . drain ( .. ) {
let full_command = match command . get_name ( ) {
" Colmena " = > " colmena " . to_string ( ) ,
sub = > format! ( " colmena {} " , sub ) ,
} ;
let mut command = {
let c = command
. setting ( AppSettings ::ColoredHelp )
. setting ( AppSettings ::ColorAlways ) ;
if full_command ! = " colmena " {
c . bin_name ( & full_command )
} else {
c
}
} ;
println! ( " ## ` {} ` " , full_command ) ;
print! ( " <pre><div class= \" hljs \" > " ) ;
let help_message = {
let mut bytes = Vec ::new ( ) ;
command . write_long_help ( & mut bytes ) . unwrap ( ) ;
String ::from_utf8 ( bytes ) . unwrap ( )
} ;
let help_html = ansi_to_html ::convert ( & help_message , true , true )
. expect ( " Could not convert terminal output to HTML " ) ;
print! ( " {} " , help_html ) ;
println! ( " </div></pre> " ) ;
}
}