Add the --command
subcommand (#824)
This commit is contained in:
parent
4cb82e0c1f
commit
50cd24d37b
@ -1111,7 +1111,7 @@ echo:
|
|||||||
The interpreter path `/bin/sh` will be translated to a Windows-style path using
|
The interpreter path `/bin/sh` will be translated to a Windows-style path using
|
||||||
`cygpath` before being executed.
|
`cygpath` before being executed.
|
||||||
|
|
||||||
If the interpreter path does not contain a `/` it will be executed without being translated. This is useful if `cygpath` is not available, or you wish to use a Windows style path to the interpreter.
|
If the interpreter path does not contain a `/` it will be executed without being translated. This is useful if `cygpath` is not available, or you wish to pass a Windows style path to the interpreter.
|
||||||
|
|
||||||
=== Setting Variables in a Recipe
|
=== Setting Variables in a Recipe
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ _just() {
|
|||||||
|
|
||||||
case "${cmd}" in
|
case "${cmd}" in
|
||||||
just)
|
just)
|
||||||
opts=" -q -u -v -e -l -h -V -f -d -s --dry-run --highlight --no-dotenv --no-highlight --quiet --clear-shell-args --unsorted --verbose --choose --dump --edit --evaluate --init --list --summary --variables --help --version --chooser --color --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --completions --show <ARGUMENTS>... "
|
opts=" -q -u -v -e -l -h -V -f -d -c -s --dry-run --highlight --no-dotenv --no-highlight --quiet --shell-command --clear-shell-args --unsorted --verbose --choose --dump --edit --evaluate --init --list --summary --variables --help --version --chooser --color --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --completions --show <ARGUMENTS>... "
|
||||||
if [[ ${cur} == -* ]] ; then
|
if [[ ${cur} == -* ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
@ -77,6 +77,14 @@ _just() {
|
|||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
--command)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-c)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
--completions)
|
--completions)
|
||||||
COMPREPLY=($(compgen -W "zsh bash fish powershell elvish" -- "${cur}"))
|
COMPREPLY=($(compgen -W "zsh bash fish powershell elvish" -- "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
|
@ -25,6 +25,8 @@ edit:completion:arg-completer[just] = [@words]{
|
|||||||
cand --shell-arg 'Invoke shell with <SHELL-ARG> as an argument'
|
cand --shell-arg 'Invoke shell with <SHELL-ARG> as an argument'
|
||||||
cand -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
cand -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
||||||
cand --working-directory 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
cand --working-directory 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
||||||
|
cand -c 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
|
cand --command 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
cand --completions 'Print shell completion script for <SHELL>'
|
cand --completions 'Print shell completion script for <SHELL>'
|
||||||
cand -s 'Show information about <RECIPE>'
|
cand -s 'Show information about <RECIPE>'
|
||||||
cand --show 'Show information about <RECIPE>'
|
cand --show 'Show information about <RECIPE>'
|
||||||
@ -34,6 +36,7 @@ edit:completion:arg-completer[just] = [@words]{
|
|||||||
cand --no-highlight 'Don''t highlight echoed recipe lines in bold'
|
cand --no-highlight 'Don''t highlight echoed recipe lines in bold'
|
||||||
cand -q 'Suppress all output'
|
cand -q 'Suppress all output'
|
||||||
cand --quiet 'Suppress all output'
|
cand --quiet 'Suppress all output'
|
||||||
|
cand --shell-command 'Invoke <COMMAND> with the shell used to run recipe lines and backticks'
|
||||||
cand --clear-shell-args 'Clear shell arguments'
|
cand --clear-shell-args 'Clear shell arguments'
|
||||||
cand -u 'Return list and summary entries in source order'
|
cand -u 'Return list and summary entries in source order'
|
||||||
cand --unsorted 'Return list and summary entries in source order'
|
cand --unsorted 'Return list and summary entries in source order'
|
||||||
|
@ -18,6 +18,7 @@ complete -c just -n "__fish_use_subcommand" -l set -d 'Override <VARIABLE> with
|
|||||||
complete -c just -n "__fish_use_subcommand" -l shell -d 'Invoke <SHELL> to run recipes'
|
complete -c just -n "__fish_use_subcommand" -l shell -d 'Invoke <SHELL> to run recipes'
|
||||||
complete -c just -n "__fish_use_subcommand" -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument'
|
complete -c just -n "__fish_use_subcommand" -l shell-arg -d 'Invoke shell with <SHELL-ARG> as an argument'
|
||||||
complete -c just -n "__fish_use_subcommand" -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
complete -c just -n "__fish_use_subcommand" -s d -l working-directory -d 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set'
|
||||||
|
complete -c just -n "__fish_use_subcommand" -s c -l command -d 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set'
|
||||||
complete -c just -n "__fish_use_subcommand" -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "zsh bash fish powershell elvish"
|
complete -c just -n "__fish_use_subcommand" -l completions -d 'Print shell completion script for <SHELL>' -r -f -a "zsh bash fish powershell elvish"
|
||||||
complete -c just -n "__fish_use_subcommand" -s s -l show -d 'Show information about <RECIPE>'
|
complete -c just -n "__fish_use_subcommand" -s s -l show -d 'Show information about <RECIPE>'
|
||||||
complete -c just -n "__fish_use_subcommand" -l dry-run -d 'Print what just would do without doing it'
|
complete -c just -n "__fish_use_subcommand" -l dry-run -d 'Print what just would do without doing it'
|
||||||
@ -25,6 +26,7 @@ complete -c just -n "__fish_use_subcommand" -l highlight -d 'Highlight echoed re
|
|||||||
complete -c just -n "__fish_use_subcommand" -l no-dotenv -d 'Don\'t load `.env` file'
|
complete -c just -n "__fish_use_subcommand" -l no-dotenv -d 'Don\'t load `.env` file'
|
||||||
complete -c just -n "__fish_use_subcommand" -l no-highlight -d 'Don\'t highlight echoed recipe lines in bold'
|
complete -c just -n "__fish_use_subcommand" -l no-highlight -d 'Don\'t highlight echoed recipe lines in bold'
|
||||||
complete -c just -n "__fish_use_subcommand" -s q -l quiet -d 'Suppress all output'
|
complete -c just -n "__fish_use_subcommand" -s q -l quiet -d 'Suppress all output'
|
||||||
|
complete -c just -n "__fish_use_subcommand" -l shell-command -d 'Invoke <COMMAND> with the shell used to run recipe lines and backticks'
|
||||||
complete -c just -n "__fish_use_subcommand" -l clear-shell-args -d 'Clear shell arguments'
|
complete -c just -n "__fish_use_subcommand" -l clear-shell-args -d 'Clear shell arguments'
|
||||||
complete -c just -n "__fish_use_subcommand" -s u -l unsorted -d 'Return list and summary entries in source order'
|
complete -c just -n "__fish_use_subcommand" -s u -l unsorted -d 'Return list and summary entries in source order'
|
||||||
complete -c just -n "__fish_use_subcommand" -s v -l verbose -d 'Use verbose output'
|
complete -c just -n "__fish_use_subcommand" -s v -l verbose -d 'Use verbose output'
|
||||||
|
@ -30,6 +30,8 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|||||||
[CompletionResult]::new('--shell-arg', 'shell-arg', [CompletionResultType]::ParameterName, 'Invoke shell with <SHELL-ARG> as an argument')
|
[CompletionResult]::new('--shell-arg', 'shell-arg', [CompletionResultType]::ParameterName, 'Invoke shell with <SHELL-ARG> as an argument')
|
||||||
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set')
|
[CompletionResult]::new('-d', 'd', [CompletionResultType]::ParameterName, 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set')
|
||||||
[CompletionResult]::new('--working-directory', 'working-directory', [CompletionResultType]::ParameterName, 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set')
|
[CompletionResult]::new('--working-directory', 'working-directory', [CompletionResultType]::ParameterName, 'Use <WORKING-DIRECTORY> as working directory. --justfile must also be set')
|
||||||
|
[CompletionResult]::new('-c', 'c', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
||||||
|
[CompletionResult]::new('--command', 'command', [CompletionResultType]::ParameterName, 'Run an arbitrary command with the working directory, `.env`, overrides, and exports set')
|
||||||
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
[CompletionResult]::new('--completions', 'completions', [CompletionResultType]::ParameterName, 'Print shell completion script for <SHELL>')
|
||||||
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
[CompletionResult]::new('-s', 's', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
||||||
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
[CompletionResult]::new('--show', 'show', [CompletionResultType]::ParameterName, 'Show information about <RECIPE>')
|
||||||
@ -39,6 +41,7 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|||||||
[CompletionResult]::new('--no-highlight', 'no-highlight', [CompletionResultType]::ParameterName, 'Don''t highlight echoed recipe lines in bold')
|
[CompletionResult]::new('--no-highlight', 'no-highlight', [CompletionResultType]::ParameterName, 'Don''t highlight echoed recipe lines in bold')
|
||||||
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Suppress all output')
|
[CompletionResult]::new('-q', 'q', [CompletionResultType]::ParameterName, 'Suppress all output')
|
||||||
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Suppress all output')
|
[CompletionResult]::new('--quiet', 'quiet', [CompletionResultType]::ParameterName, 'Suppress all output')
|
||||||
|
[CompletionResult]::new('--shell-command', 'shell-command', [CompletionResultType]::ParameterName, 'Invoke <COMMAND> with the shell used to run recipe lines and backticks')
|
||||||
[CompletionResult]::new('--clear-shell-args', 'clear-shell-args', [CompletionResultType]::ParameterName, 'Clear shell arguments')
|
[CompletionResult]::new('--clear-shell-args', 'clear-shell-args', [CompletionResultType]::ParameterName, 'Clear shell arguments')
|
||||||
[CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'Return list and summary entries in source order')
|
[CompletionResult]::new('-u', 'u', [CompletionResultType]::ParameterName, 'Return list and summary entries in source order')
|
||||||
[CompletionResult]::new('--unsorted', 'unsorted', [CompletionResultType]::ParameterName, 'Return list and summary entries in source order')
|
[CompletionResult]::new('--unsorted', 'unsorted', [CompletionResultType]::ParameterName, 'Return list and summary entries in source order')
|
||||||
|
@ -26,6 +26,8 @@ _just() {
|
|||||||
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]' \
|
'*--shell-arg=[Invoke shell with <SHELL-ARG> as an argument]' \
|
||||||
'-d+[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \
|
'-d+[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \
|
||||||
'--working-directory=[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \
|
'--working-directory=[Use <WORKING-DIRECTORY> as working directory. --justfile must also be set]' \
|
||||||
|
'-c+[Run an arbitrary command with the working directory, `.env`, overrides, and exports set]' \
|
||||||
|
'--command=[Run an arbitrary command with the working directory, `.env`, overrides, and exports set]' \
|
||||||
'--completions=[Print shell completion script for <SHELL>]: :(zsh bash fish powershell elvish)' \
|
'--completions=[Print shell completion script for <SHELL>]: :(zsh bash fish powershell elvish)' \
|
||||||
'-s+[Show information about <RECIPE>]: :_just_commands' \
|
'-s+[Show information about <RECIPE>]: :_just_commands' \
|
||||||
'--show=[Show information about <RECIPE>]: :_just_commands' \
|
'--show=[Show information about <RECIPE>]: :_just_commands' \
|
||||||
@ -35,6 +37,7 @@ _just() {
|
|||||||
'--no-highlight[Don'\''t highlight echoed recipe lines in bold]' \
|
'--no-highlight[Don'\''t highlight echoed recipe lines in bold]' \
|
||||||
'(--dry-run)-q[Suppress all output]' \
|
'(--dry-run)-q[Suppress all output]' \
|
||||||
'(--dry-run)--quiet[Suppress all output]' \
|
'(--dry-run)--quiet[Suppress all output]' \
|
||||||
|
'--shell-command[Invoke <COMMAND> with the shell used to run recipe lines and backticks]' \
|
||||||
'--clear-shell-args[Clear shell arguments]' \
|
'--clear-shell-args[Clear shell arguments]' \
|
||||||
'-u[Return list and summary entries in source order]' \
|
'-u[Return list and summary entries in source order]' \
|
||||||
'--unsorted[Return list and summary entries in source order]' \
|
'--unsorted[Return list and summary entries in source order]' \
|
||||||
|
@ -5,10 +5,10 @@ error_on_unformatted = true
|
|||||||
format_code_in_doc_comments = true
|
format_code_in_doc_comments = true
|
||||||
format_macro_bodies = true
|
format_macro_bodies = true
|
||||||
format_strings = true
|
format_strings = true
|
||||||
|
imports_granularity = "Crate"
|
||||||
match_arm_blocks = false
|
match_arm_blocks = false
|
||||||
match_block_trailing_comma = true
|
match_block_trailing_comma = true
|
||||||
max_width = 100
|
max_width = 100
|
||||||
merge_imports = true
|
|
||||||
newline_style = "Unix"
|
newline_style = "Unix"
|
||||||
normalize_comments = true
|
normalize_comments = true
|
||||||
overflow_delimited_expr = true
|
overflow_delimited_expr = true
|
||||||
|
@ -3,7 +3,7 @@ pub(crate) use std::{
|
|||||||
cmp,
|
cmp,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
env,
|
env,
|
||||||
ffi::OsString,
|
ffi::{OsStr, OsString},
|
||||||
fmt::{self, Debug, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
fs,
|
fs,
|
||||||
io::{self, Cursor, Write},
|
io::{self, Cursor, Write},
|
||||||
|
@ -26,6 +26,7 @@ pub(crate) struct Config {
|
|||||||
pub(crate) shell: String,
|
pub(crate) shell: String,
|
||||||
pub(crate) shell_args: Vec<String>,
|
pub(crate) shell_args: Vec<String>,
|
||||||
pub(crate) shell_present: bool,
|
pub(crate) shell_present: bool,
|
||||||
|
pub(crate) shell_command: bool,
|
||||||
pub(crate) subcommand: Subcommand,
|
pub(crate) subcommand: Subcommand,
|
||||||
pub(crate) unsorted: bool,
|
pub(crate) unsorted: bool,
|
||||||
pub(crate) verbosity: Verbosity,
|
pub(crate) verbosity: Verbosity,
|
||||||
@ -42,14 +43,16 @@ mod cmd {
|
|||||||
pub(crate) const SHOW: &str = "SHOW";
|
pub(crate) const SHOW: &str = "SHOW";
|
||||||
pub(crate) const SUMMARY: &str = "SUMMARY";
|
pub(crate) const SUMMARY: &str = "SUMMARY";
|
||||||
pub(crate) const VARIABLES: &str = "VARIABLES";
|
pub(crate) const VARIABLES: &str = "VARIABLES";
|
||||||
|
pub(crate) const COMMAND: &str = "COMMAND";
|
||||||
|
|
||||||
pub(crate) const ALL: &[&str] = &[
|
pub(crate) const ALL: &[&str] = &[
|
||||||
CHOOSE,
|
CHOOSE,
|
||||||
|
COMMAND,
|
||||||
COMPLETIONS,
|
COMPLETIONS,
|
||||||
DUMP,
|
DUMP,
|
||||||
EDIT,
|
EDIT,
|
||||||
INIT,
|
|
||||||
EVALUATE,
|
EVALUATE,
|
||||||
|
INIT,
|
||||||
LIST,
|
LIST,
|
||||||
SHOW,
|
SHOW,
|
||||||
SUMMARY,
|
SUMMARY,
|
||||||
@ -75,15 +78,16 @@ mod arg {
|
|||||||
pub(crate) const COLOR: &str = "COLOR";
|
pub(crate) const COLOR: &str = "COLOR";
|
||||||
pub(crate) const DRY_RUN: &str = "DRY-RUN";
|
pub(crate) const DRY_RUN: &str = "DRY-RUN";
|
||||||
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
|
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
|
||||||
|
pub(crate) const JUSTFILE: &str = "JUSTFILE";
|
||||||
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
|
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
|
||||||
pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX";
|
pub(crate) const LIST_PREFIX: &str = "LIST-PREFIX";
|
||||||
pub(crate) const JUSTFILE: &str = "JUSTFILE";
|
|
||||||
pub(crate) const NO_DOTENV: &str = "NO-DOTENV";
|
pub(crate) const NO_DOTENV: &str = "NO-DOTENV";
|
||||||
pub(crate) const NO_HIGHLIGHT: &str = "NO-HIGHLIGHT";
|
pub(crate) const NO_HIGHLIGHT: &str = "NO-HIGHLIGHT";
|
||||||
pub(crate) const QUIET: &str = "QUIET";
|
pub(crate) const QUIET: &str = "QUIET";
|
||||||
pub(crate) const SET: &str = "SET";
|
pub(crate) const SET: &str = "SET";
|
||||||
pub(crate) const SHELL: &str = "SHELL";
|
pub(crate) const SHELL: &str = "SHELL";
|
||||||
pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
|
pub(crate) const SHELL_ARG: &str = "SHELL-ARG";
|
||||||
|
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
||||||
pub(crate) const UNSORTED: &str = "UNSORTED";
|
pub(crate) const UNSORTED: &str = "UNSORTED";
|
||||||
pub(crate) const VERBOSE: &str = "VERBOSE";
|
pub(crate) const VERBOSE: &str = "VERBOSE";
|
||||||
pub(crate) const WORKING_DIRECTORY: &str = "WORKING-DIRECTORY";
|
pub(crate) const WORKING_DIRECTORY: &str = "WORKING-DIRECTORY";
|
||||||
@ -193,6 +197,12 @@ impl Config {
|
|||||||
.overrides_with(arg::CLEAR_SHELL_ARGS)
|
.overrides_with(arg::CLEAR_SHELL_ARGS)
|
||||||
.help("Invoke shell with <SHELL-ARG> as an argument"),
|
.help("Invoke shell with <SHELL-ARG> as an argument"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(arg::SHELL_COMMAND)
|
||||||
|
.long("shell-command")
|
||||||
|
.requires(cmd::COMMAND)
|
||||||
|
.help("Invoke <COMMAND> with the shell used to run recipe lines and backticks"),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(arg::CLEAR_SHELL_ARGS)
|
Arg::with_name(arg::CLEAR_SHELL_ARGS)
|
||||||
.long("clear-shell-args")
|
.long("clear-shell-args")
|
||||||
@ -220,12 +230,18 @@ impl Config {
|
|||||||
.help("Use <WORKING-DIRECTORY> as working directory. --justfile must also be set")
|
.help("Use <WORKING-DIRECTORY> as working directory. --justfile must also be set")
|
||||||
.requires(arg::JUSTFILE),
|
.requires(arg::JUSTFILE),
|
||||||
)
|
)
|
||||||
.arg(
|
|
||||||
Arg::with_name(arg::ARGUMENTS)
|
|
||||||
.multiple(true)
|
|
||||||
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
|
|
||||||
)
|
|
||||||
.arg(Arg::with_name(cmd::CHOOSE).long("choose").help(CHOOSE_HELP))
|
.arg(Arg::with_name(cmd::CHOOSE).long("choose").help(CHOOSE_HELP))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(cmd::COMMAND)
|
||||||
|
.long("command")
|
||||||
|
.short("c")
|
||||||
|
.min_values(1)
|
||||||
|
.allow_hyphen_values(true)
|
||||||
|
.help(
|
||||||
|
"Run an arbitrary command with the working directory, `.env`, overrides, and exports \
|
||||||
|
set",
|
||||||
|
),
|
||||||
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(cmd::COMPLETIONS)
|
Arg::with_name(cmd::COMPLETIONS)
|
||||||
.long("completions")
|
.long("completions")
|
||||||
@ -279,7 +295,12 @@ impl Config {
|
|||||||
.long("variables")
|
.long("variables")
|
||||||
.help("List names of variables"),
|
.help("List names of variables"),
|
||||||
)
|
)
|
||||||
.group(ArgGroup::with_name("SUBCOMMAND").args(cmd::ALL));
|
.group(ArgGroup::with_name("SUBCOMMAND").args(cmd::ALL))
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(arg::ARGUMENTS)
|
||||||
|
.multiple(true)
|
||||||
|
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
|
||||||
|
);
|
||||||
|
|
||||||
if cfg!(feature = "help4help2man") {
|
if cfg!(feature = "help4help2man") {
|
||||||
app.version(env!("CARGO_PKG_VERSION")).about(concat!(
|
app.version(env!("CARGO_PKG_VERSION")).about(concat!(
|
||||||
@ -401,6 +422,16 @@ impl Config {
|
|||||||
chooser: matches.value_of(arg::CHOOSER).map(str::to_owned),
|
chooser: matches.value_of(arg::CHOOSER).map(str::to_owned),
|
||||||
overrides,
|
overrides,
|
||||||
}
|
}
|
||||||
|
} else if let Some(values) = matches.values_of_os(cmd::COMMAND) {
|
||||||
|
let mut arguments = values
|
||||||
|
.into_iter()
|
||||||
|
.map(OsStr::to_owned)
|
||||||
|
.collect::<Vec<OsString>>();
|
||||||
|
Subcommand::Command {
|
||||||
|
binary: arguments.remove(0),
|
||||||
|
arguments,
|
||||||
|
overrides,
|
||||||
|
}
|
||||||
} else if let Some(shell) = matches.value_of(cmd::COMPLETIONS) {
|
} else if let Some(shell) = matches.value_of(cmd::COMPLETIONS) {
|
||||||
Subcommand::Completions {
|
Subcommand::Completions {
|
||||||
shell: shell.to_owned(),
|
shell: shell.to_owned(),
|
||||||
@ -463,6 +494,7 @@ impl Config {
|
|||||||
highlight: !matches.is_present(arg::NO_HIGHLIGHT),
|
highlight: !matches.is_present(arg::NO_HIGHLIGHT),
|
||||||
shell: matches.value_of(arg::SHELL).unwrap().to_owned(),
|
shell: matches.value_of(arg::SHELL).unwrap().to_owned(),
|
||||||
load_dotenv: !matches.is_present(arg::NO_DOTENV),
|
load_dotenv: !matches.is_present(arg::NO_DOTENV),
|
||||||
|
shell_command: matches.is_present(arg::SHELL_COMMAND),
|
||||||
unsorted: matches.is_present(arg::UNSORTED),
|
unsorted: matches.is_present(arg::UNSORTED),
|
||||||
list_heading: matches
|
list_heading: matches
|
||||||
.value_of(arg::LIST_HEADING)
|
.value_of(arg::LIST_HEADING)
|
||||||
@ -522,6 +554,7 @@ impl Config {
|
|||||||
match &self.subcommand {
|
match &self.subcommand {
|
||||||
Choose { overrides, chooser } =>
|
Choose { overrides, chooser } =>
|
||||||
self.choose(justfile, &search, overrides, chooser.as_deref())?,
|
self.choose(justfile, &search, overrides, chooser.as_deref())?,
|
||||||
|
Command { overrides, .. } => self.run(justfile, &search, overrides, &[])?,
|
||||||
Dump => Self::dump(justfile),
|
Dump => Self::dump(justfile),
|
||||||
Evaluate { overrides, .. } => self.run(justfile, &search, overrides, &[])?,
|
Evaluate { overrides, .. } => self.run(justfile, &search, overrides, &[])?,
|
||||||
List => self.list(justfile),
|
List => self.list(justfile),
|
||||||
@ -893,6 +926,8 @@ FLAGS:
|
|||||||
--no-dotenv Don't load `.env` file
|
--no-dotenv Don't load `.env` file
|
||||||
--no-highlight Don't highlight echoed recipe lines in bold
|
--no-highlight Don't highlight echoed recipe lines in bold
|
||||||
-q, --quiet Suppress all output
|
-q, --quiet Suppress all output
|
||||||
|
--shell-command Invoke <COMMAND> with the shell used to run recipe lines and \
|
||||||
|
backticks
|
||||||
--summary List names of available recipes
|
--summary List names of available recipes
|
||||||
-u, --unsorted Return list and summary entries in source order
|
-u, --unsorted Return list and summary entries in source order
|
||||||
--variables List names of variables
|
--variables List names of variables
|
||||||
@ -903,6 +938,9 @@ OPTIONS:
|
|||||||
--color <COLOR>
|
--color <COLOR>
|
||||||
Print colorful output [default: auto] [possible values: auto, always, never]
|
Print colorful output [default: auto] [possible values: auto, always, never]
|
||||||
|
|
||||||
|
-c, --command <COMMAND>
|
||||||
|
Run an arbitrary command with the working directory, `.env`, overrides, and exports set
|
||||||
|
|
||||||
--completions <SHELL>
|
--completions <SHELL>
|
||||||
Print shell completion script for <SHELL> [possible values: zsh, bash, fish, \
|
Print shell completion script for <SHELL> [possible values: zsh, bash, fish, \
|
||||||
powershell, elvish]
|
powershell, elvish]
|
||||||
|
@ -127,34 +127,70 @@ impl<'src> Justfile<'src> {
|
|||||||
)?
|
)?
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Subcommand::Evaluate { variable, .. } = &config.subcommand {
|
match &config.subcommand {
|
||||||
if let Some(variable) = variable {
|
Subcommand::Command {
|
||||||
if let Some(value) = scope.value(variable) {
|
binary, arguments, ..
|
||||||
print!("{}", value);
|
} => {
|
||||||
|
let mut command = if config.shell_command {
|
||||||
|
let mut command = self.settings.shell_command(&config);
|
||||||
|
command.arg(binary);
|
||||||
|
command
|
||||||
} else {
|
} else {
|
||||||
return Err(RuntimeError::EvalUnknownVariable {
|
Command::new(binary)
|
||||||
suggestion: self.suggest_variable(&variable),
|
};
|
||||||
variable: variable.clone(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut width = 0;
|
|
||||||
|
|
||||||
for name in scope.names() {
|
command.args(arguments);
|
||||||
width = cmp::max(name.len(), width);
|
|
||||||
|
command.current_dir(&search.working_directory);
|
||||||
|
|
||||||
|
let scope = scope.child();
|
||||||
|
|
||||||
|
command.export(&self.settings, &dotenv, &scope);
|
||||||
|
|
||||||
|
let status = InterruptHandler::guard(|| command.status()).map_err(|io_error| {
|
||||||
|
RuntimeError::CommandInvocation {
|
||||||
|
binary: binary.clone(),
|
||||||
|
arguments: arguments.clone(),
|
||||||
|
io_error,
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
if !status.success() {
|
||||||
|
process::exit(status.code().unwrap_or(EXIT_FAILURE));
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
},
|
||||||
|
Subcommand::Evaluate { variable, .. } => {
|
||||||
|
if let Some(variable) = variable {
|
||||||
|
if let Some(value) = scope.value(variable) {
|
||||||
|
print!("{}", value);
|
||||||
|
} else {
|
||||||
|
return Err(RuntimeError::EvalUnknownVariable {
|
||||||
|
suggestion: self.suggest_variable(&variable),
|
||||||
|
variable: variable.clone(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let mut width = 0;
|
||||||
|
|
||||||
|
for name in scope.names() {
|
||||||
|
width = cmp::max(name.len(), width);
|
||||||
|
}
|
||||||
|
|
||||||
|
for binding in scope.bindings() {
|
||||||
|
println!(
|
||||||
|
"{0:1$} := \"{2}\"",
|
||||||
|
binding.name.lexeme(),
|
||||||
|
width,
|
||||||
|
binding.value
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for binding in scope.bindings() {
|
return Ok(());
|
||||||
println!(
|
},
|
||||||
"{0:1$} := \"{2}\"",
|
_ => {},
|
||||||
binding.name.lexeme(),
|
|
||||||
width,
|
|
||||||
binding.value
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let argvec: Vec<&str> = if !arguments.is_empty() {
|
let argvec: Vec<&str> = if !arguments.is_empty() {
|
||||||
|
@ -18,6 +18,11 @@ pub(crate) enum RuntimeError<'src> {
|
|||||||
line_number: Option<usize>,
|
line_number: Option<usize>,
|
||||||
code: i32,
|
code: i32,
|
||||||
},
|
},
|
||||||
|
CommandInvocation {
|
||||||
|
binary: OsString,
|
||||||
|
arguments: Vec<OsString>,
|
||||||
|
io_error: io::Error,
|
||||||
|
},
|
||||||
Cygpath {
|
Cygpath {
|
||||||
recipe: &'src str,
|
recipe: &'src str,
|
||||||
output_error: OutputError,
|
output_error: OutputError,
|
||||||
@ -201,6 +206,22 @@ impl<'src> Display for RuntimeError<'src> {
|
|||||||
} else {
|
} else {
|
||||||
write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?;
|
write!(f, "Recipe `{}` failed with exit code {}", recipe, code)?;
|
||||||
},
|
},
|
||||||
|
CommandInvocation {
|
||||||
|
binary,
|
||||||
|
arguments,
|
||||||
|
io_error,
|
||||||
|
} => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Failed to invoke {}: {}",
|
||||||
|
iter::once(binary)
|
||||||
|
.chain(arguments)
|
||||||
|
.map(|value| Enclosure::tick(value.to_string_lossy()).to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join(" "),
|
||||||
|
io_error,
|
||||||
|
)?;
|
||||||
|
},
|
||||||
Cygpath {
|
Cygpath {
|
||||||
recipe,
|
recipe,
|
||||||
output_error,
|
output_error,
|
||||||
|
@ -6,6 +6,11 @@ pub(crate) enum Subcommand {
|
|||||||
overrides: BTreeMap<String, String>,
|
overrides: BTreeMap<String, String>,
|
||||||
chooser: Option<String>,
|
chooser: Option<String>,
|
||||||
},
|
},
|
||||||
|
Command {
|
||||||
|
arguments: Vec<OsString>,
|
||||||
|
binary: OsString,
|
||||||
|
overrides: BTreeMap<String, String>,
|
||||||
|
},
|
||||||
Completions {
|
Completions {
|
||||||
shell: String,
|
shell: String,
|
||||||
},
|
},
|
||||||
|
134
tests/command.rs
Normal file
134
tests/command.rs
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
use crate::common::*;
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: long,
|
||||||
|
justfile: "
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("--command", "printf", "foo"),
|
||||||
|
stdout: "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: short,
|
||||||
|
justfile: "
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("-c", "printf", "foo"),
|
||||||
|
stdout: "foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: no_binary,
|
||||||
|
justfile: "
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("--command"),
|
||||||
|
stderr: &format!("
|
||||||
|
error: The argument '--command <COMMAND>' requires a value but none was supplied
|
||||||
|
|
||||||
|
USAGE:
|
||||||
|
just{} --color <COLOR> --shell <SHELL> --shell-arg <SHELL-ARG>... \
|
||||||
|
<--choose|--command <COMMAND>|--completions <SHELL>|--dump|--edit|\
|
||||||
|
--evaluate|--init|--list|--show <RECIPE>|--summary|--variables>
|
||||||
|
|
||||||
|
For more information try --help
|
||||||
|
", EXE_SUFFIX),
|
||||||
|
status: EXIT_FAILURE,
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: env_is_loaded,
|
||||||
|
justfile: "
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("--command", "sh", "-c", "printf $DOTENV_KEY"),
|
||||||
|
stdout: "dotenv-value",
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: exports_are_available,
|
||||||
|
justfile: "
|
||||||
|
export FOO := 'bar'
|
||||||
|
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("--command", "sh", "-c", "printf $FOO"),
|
||||||
|
stdout: "bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: set_overrides_work,
|
||||||
|
justfile: "
|
||||||
|
export FOO := 'bar'
|
||||||
|
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("--set", "FOO", "baz", "--command", "sh", "-c", "printf $FOO"),
|
||||||
|
stdout: "baz",
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: run_in_shell,
|
||||||
|
justfile: "
|
||||||
|
set shell := ['printf']
|
||||||
|
",
|
||||||
|
args: ("--shell-command", "--command", "bar baz"),
|
||||||
|
stdout: "bar baz",
|
||||||
|
shell: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
test! {
|
||||||
|
name: exit_status,
|
||||||
|
justfile: "
|
||||||
|
x:
|
||||||
|
echo XYZ
|
||||||
|
",
|
||||||
|
args: ("--command", "false"),
|
||||||
|
status: EXIT_FAILURE,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn working_directory_is_correct() {
|
||||||
|
let tmp = tempdir();
|
||||||
|
|
||||||
|
fs::write(tmp.path().join("justfile"), "").unwrap();
|
||||||
|
fs::write(tmp.path().join("bar"), "baz").unwrap();
|
||||||
|
fs::create_dir(tmp.path().join("foo")).unwrap();
|
||||||
|
|
||||||
|
let output = Command::new(&executable_path("just"))
|
||||||
|
.args(&["--command", "cat", "bar"])
|
||||||
|
.current_dir(tmp.path().join("foo"))
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert_eq!(str::from_utf8(&output.stderr).unwrap(), "");
|
||||||
|
|
||||||
|
assert!(output.status.success());
|
||||||
|
|
||||||
|
assert_eq!(str::from_utf8(&output.stdout).unwrap(), "baz");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn command_not_found() {
|
||||||
|
let tmp = tempdir();
|
||||||
|
|
||||||
|
fs::write(tmp.path().join("justfile"), "").unwrap();
|
||||||
|
|
||||||
|
let output = Command::new(&executable_path("just"))
|
||||||
|
.args(&["--command", "asdfasdfasdfasdfadfsadsfadsf", "bar"])
|
||||||
|
.output()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
assert!(str::from_utf8(&output.stderr)
|
||||||
|
.unwrap()
|
||||||
|
.starts_with("error: Failed to invoke `asdfasdfasdfasdfadfsadsfadsf` `bar`:"));
|
||||||
|
|
||||||
|
assert!(!output.status.success());
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
pub(crate) use std::{
|
pub(crate) use std::{
|
||||||
collections::BTreeMap,
|
collections::BTreeMap,
|
||||||
env, fs,
|
env::{self, consts::EXE_SUFFIX},
|
||||||
|
fs,
|
||||||
io::Write,
|
io::Write,
|
||||||
iter,
|
iter,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
mod unix {
|
mod unix {
|
||||||
use executable_path::executable_path;
|
use executable_path::executable_path;
|
||||||
|
use just::unindent;
|
||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs,
|
||||||
process::Command,
|
process::Command,
|
||||||
@ -14,16 +15,17 @@ mod unix {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn interrupt_test(justfile: &str) {
|
fn interrupt_test(arguments: &[&str], justfile: &str) {
|
||||||
let tmp = tempdir();
|
let tmp = tempdir();
|
||||||
let mut justfile_path = tmp.path().to_path_buf();
|
let mut justfile_path = tmp.path().to_path_buf();
|
||||||
justfile_path.push("justfile");
|
justfile_path.push("justfile");
|
||||||
fs::write(justfile_path, justfile).unwrap();
|
fs::write(justfile_path, unindent(justfile)).unwrap();
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
|
|
||||||
let mut child = Command::new(&executable_path("just"))
|
let mut child = Command::new(&executable_path("just"))
|
||||||
.current_dir(&tmp)
|
.current_dir(&tmp)
|
||||||
|
.args(arguments)
|
||||||
.spawn()
|
.spawn()
|
||||||
.expect("just invocation failed");
|
.expect("just invocation failed");
|
||||||
|
|
||||||
@ -50,11 +52,12 @@ mod unix {
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
fn interrupt_shebang() {
|
fn interrupt_shebang() {
|
||||||
interrupt_test(
|
interrupt_test(
|
||||||
|
&[],
|
||||||
"
|
"
|
||||||
default:
|
default:
|
||||||
#!/usr/bin/env sh
|
#!/usr/bin/env sh
|
||||||
sleep 1
|
sleep 1
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,10 +65,11 @@ default:
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
fn interrupt_line() {
|
fn interrupt_line() {
|
||||||
interrupt_test(
|
interrupt_test(
|
||||||
|
&[],
|
||||||
"
|
"
|
||||||
default:
|
default:
|
||||||
@sleep 1
|
@sleep 1
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,12 +77,19 @@ default:
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
fn interrupt_backtick() {
|
fn interrupt_backtick() {
|
||||||
interrupt_test(
|
interrupt_test(
|
||||||
|
&[],
|
||||||
"
|
"
|
||||||
foo := `sleep 1`
|
foo := `sleep 1`
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@echo {{foo}}
|
@echo {{foo}}
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn interrupt_command() {
|
||||||
|
interrupt_test(&["--command", "sleep", "1"], "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ mod test;
|
|||||||
mod common;
|
mod common;
|
||||||
|
|
||||||
mod choose;
|
mod choose;
|
||||||
|
mod command;
|
||||||
mod completions;
|
mod completions;
|
||||||
mod conditional;
|
mod conditional;
|
||||||
mod delimiters;
|
mod delimiters;
|
||||||
|
Loading…
Reference in New Issue
Block a user