Add flags for specifying name and path environment file (#941)
This commit is contained in:
parent
f5689617f4
commit
e72e7dd569
@ -20,7 +20,7 @@ _just() {
|
|||||||
|
|
||||||
case "${cmd}" in
|
case "${cmd}" in
|
||||||
just)
|
just)
|
||||||
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 --unstable --verbose --changelog --choose --dump --edit --evaluate --fmt --init --list --summary --variables --help --version --chooser --color --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --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 --unstable --verbose --changelog --choose --dump --edit --evaluate --fmt --init --list --summary --variables --help --version --chooser --color --list-heading --list-prefix --justfile --set --shell --shell-arg --working-directory --command --completions --show --dotenv-filename --dotenv-path <ARGUMENTS>... "
|
||||||
if [[ ${cur} == -* ]] ; then
|
if [[ ${cur} == -* ]] ; then
|
||||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||||
return 0
|
return 0
|
||||||
@ -97,6 +97,14 @@ _just() {
|
|||||||
COMPREPLY=($(compgen -f "${cur}"))
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
--dotenv-filename)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--dotenv-path)
|
||||||
|
COMPREPLY=($(compgen -f "${cur}"))
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
COMPREPLY=()
|
COMPREPLY=()
|
||||||
;;
|
;;
|
||||||
|
@ -30,6 +30,8 @@ edit:completion:arg-completer[just] = [@words]{
|
|||||||
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>'
|
||||||
|
cand --dotenv-filename 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
||||||
|
cand --dotenv-path 'Load environment file at <DOTENV-PATH> instead of searching for one'
|
||||||
cand --dry-run 'Print what just would do without doing it'
|
cand --dry-run 'Print what just would do without doing it'
|
||||||
cand --highlight 'Highlight echoed recipe lines in bold'
|
cand --highlight 'Highlight echoed recipe lines in bold'
|
||||||
cand --no-dotenv 'Don''t load `.env` file'
|
cand --no-dotenv 'Don''t load `.env` file'
|
||||||
|
@ -21,6 +21,8 @@ complete -c just -n "__fish_use_subcommand" -s d -l working-directory -d 'Use <W
|
|||||||
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" -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 dotenv-filename -d 'Search for environment file named <DOTENV-FILENAME> instead of `.env`'
|
||||||
|
complete -c just -n "__fish_use_subcommand" -l dotenv-path -d 'Load environment file at <DOTENV-PATH> instead of searching for one'
|
||||||
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'
|
||||||
complete -c just -n "__fish_use_subcommand" -l highlight -d 'Highlight echoed recipe lines in bold'
|
complete -c just -n "__fish_use_subcommand" -l highlight -d 'Highlight echoed recipe lines in bold'
|
||||||
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'
|
||||||
|
@ -35,6 +35,8 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
|||||||
[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>')
|
||||||
|
[CompletionResult]::new('--dotenv-filename', 'dotenv-filename', [CompletionResultType]::ParameterName, 'Search for environment file named <DOTENV-FILENAME> instead of `.env`')
|
||||||
|
[CompletionResult]::new('--dotenv-path', 'dotenv-path', [CompletionResultType]::ParameterName, 'Load environment file at <DOTENV-PATH> instead of searching for one')
|
||||||
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
|
[CompletionResult]::new('--dry-run', 'dry-run', [CompletionResultType]::ParameterName, 'Print what just would do without doing it')
|
||||||
[CompletionResult]::new('--highlight', 'highlight', [CompletionResultType]::ParameterName, 'Highlight echoed recipe lines in bold')
|
[CompletionResult]::new('--highlight', 'highlight', [CompletionResultType]::ParameterName, 'Highlight echoed recipe lines in bold')
|
||||||
[CompletionResult]::new('--no-dotenv', 'no-dotenv', [CompletionResultType]::ParameterName, 'Don''t load `.env` file')
|
[CompletionResult]::new('--no-dotenv', 'no-dotenv', [CompletionResultType]::ParameterName, 'Don''t load `.env` file')
|
||||||
|
@ -31,6 +31,8 @@ _just() {
|
|||||||
'--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' \
|
||||||
|
'(--dotenv-path)--dotenv-filename=[Search for environment file named <DOTENV-FILENAME> instead of `.env`]' \
|
||||||
|
'--dotenv-path=[Load environment file at <DOTENV-PATH> instead of searching for one]' \
|
||||||
'(-q --quiet)--dry-run[Print what just would do without doing it]' \
|
'(-q --quiet)--dry-run[Print what just would do without doing it]' \
|
||||||
'--highlight[Highlight echoed recipe lines in bold]' \
|
'--highlight[Highlight echoed recipe lines in bold]' \
|
||||||
'--no-dotenv[Don'\''t load `.env` file]' \
|
'--no-dotenv[Don'\''t load `.env` file]' \
|
||||||
|
@ -29,6 +29,8 @@ pub(crate) struct Config {
|
|||||||
pub(crate) subcommand: Subcommand,
|
pub(crate) subcommand: Subcommand,
|
||||||
pub(crate) unsorted: bool,
|
pub(crate) unsorted: bool,
|
||||||
pub(crate) unstable: bool,
|
pub(crate) unstable: bool,
|
||||||
|
pub(crate) dotenv_filename: Option<String>,
|
||||||
|
pub(crate) dotenv_path: Option<PathBuf>,
|
||||||
pub(crate) verbosity: Verbosity,
|
pub(crate) verbosity: Verbosity,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,6 +98,8 @@ mod arg {
|
|||||||
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
pub(crate) const SHELL_COMMAND: &str = "SHELL-COMMAND";
|
||||||
pub(crate) const UNSORTED: &str = "UNSORTED";
|
pub(crate) const UNSORTED: &str = "UNSORTED";
|
||||||
pub(crate) const UNSTABLE: &str = "UNSTABLE";
|
pub(crate) const UNSTABLE: &str = "UNSTABLE";
|
||||||
|
pub(crate) const DOTENV_FILENAME: &str = "DOTENV_FILENAME";
|
||||||
|
pub(crate) const DOTENV_PATH: &str = "DOTENV_PATH";
|
||||||
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";
|
||||||
|
|
||||||
@ -317,6 +321,19 @@ impl Config {
|
|||||||
.long("variables")
|
.long("variables")
|
||||||
.help("List names of variables"),
|
.help("List names of variables"),
|
||||||
)
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(arg::DOTENV_FILENAME)
|
||||||
|
.long("dotenv-filename")
|
||||||
|
.takes_value(true)
|
||||||
|
.help("Search for environment file named <DOTENV-FILENAME> instead of `.env`")
|
||||||
|
.conflicts_with(arg::DOTENV_PATH),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name(arg::DOTENV_PATH)
|
||||||
|
.long("dotenv-path")
|
||||||
|
.help("Load environment file at <DOTENV-PATH> instead of searching for one")
|
||||||
|
.takes_value(true),
|
||||||
|
)
|
||||||
.group(ArgGroup::with_name("SUBCOMMAND").args(cmd::ALL))
|
.group(ArgGroup::with_name("SUBCOMMAND").args(cmd::ALL))
|
||||||
.arg(
|
.arg(
|
||||||
Arg::with_name(arg::ARGUMENTS)
|
Arg::with_name(arg::ARGUMENTS)
|
||||||
@ -537,6 +554,8 @@ impl Config {
|
|||||||
shell_args,
|
shell_args,
|
||||||
shell_present,
|
shell_present,
|
||||||
subcommand,
|
subcommand,
|
||||||
|
dotenv_filename: matches.value_of(arg::DOTENV_FILENAME).map(str::to_owned),
|
||||||
|
dotenv_path: matches.value_of(arg::DOTENV_PATH).map(PathBuf::from),
|
||||||
verbosity,
|
verbosity,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -616,6 +635,12 @@ OPTIONS:
|
|||||||
--completions <SHELL>
|
--completions <SHELL>
|
||||||
Print shell completion script for <SHELL> [possible values: zsh,
|
Print shell completion script for <SHELL> [possible values: zsh,
|
||||||
bash, fish, powershell, elvish]
|
bash, fish, powershell, elvish]
|
||||||
|
--dotenv-filename <DOTENV_FILENAME>
|
||||||
|
Search for environment file named <DOTENV-FILENAME> instead of
|
||||||
|
`.env`
|
||||||
|
--dotenv-path <DOTENV_PATH>
|
||||||
|
Load environment file at <DOTENV-PATH> instead of searching for one
|
||||||
|
|
||||||
-f, --justfile <JUSTFILE> Use <JUSTFILE> as justfile
|
-f, --justfile <JUSTFILE> Use <JUSTFILE> as justfile
|
||||||
--list-heading <TEXT> Print <TEXT> before list
|
--list-heading <TEXT> Print <TEXT> before list
|
||||||
--list-prefix <TEXT>
|
--list-prefix <TEXT>
|
||||||
@ -883,6 +908,11 @@ ARGS:
|
|||||||
verbosity: Verbosity::Quiet,
|
verbosity: Verbosity::Quiet,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
error! {
|
||||||
|
name: dotenv_both_filename_and_path,
|
||||||
|
args: ["--dotenv-filename", "foo", "--dotenv-path", "bar"],
|
||||||
|
}
|
||||||
|
|
||||||
test! {
|
test! {
|
||||||
name: set_default,
|
name: set_default,
|
||||||
args: [],
|
args: [],
|
||||||
|
@ -426,7 +426,7 @@ impl<'src> ColorDisplay for Error<'src> {
|
|||||||
)?;
|
)?;
|
||||||
},
|
},
|
||||||
Dotenv { dotenv_error } => {
|
Dotenv { dotenv_error } => {
|
||||||
write!(f, "Failed to load .env: {}", dotenv_error)?;
|
write!(f, "Failed to load environment file: {}", dotenv_error)?;
|
||||||
},
|
},
|
||||||
EditorInvoke { editor, io_error } => {
|
EditorInvoke { editor, io_error } => {
|
||||||
write!(
|
write!(
|
||||||
|
@ -1,45 +1,69 @@
|
|||||||
use crate::common::*;
|
use crate::common::*;
|
||||||
|
|
||||||
|
const DEFAULT_DOTENV_FILENAME: &str = ".env";
|
||||||
|
|
||||||
pub(crate) fn load_dotenv(
|
pub(crate) fn load_dotenv(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
working_directory: &Path,
|
working_directory: &Path,
|
||||||
) -> RunResult<'static, BTreeMap<String, String>> {
|
) -> RunResult<'static, BTreeMap<String, String>> {
|
||||||
// `dotenv::from_path_iter` should eventually be un-deprecated, see:
|
if !settings.dotenv_load.unwrap_or(true)
|
||||||
// https://github.com/dotenv-rs/dotenv/issues/13
|
&& config.dotenv_filename.is_none()
|
||||||
#![allow(deprecated)]
|
&& config.dotenv_path.is_none()
|
||||||
|
{
|
||||||
if !settings.dotenv_load.unwrap_or(true) {
|
|
||||||
return Ok(BTreeMap::new());
|
return Ok(BTreeMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(path) = &config.dotenv_path {
|
||||||
|
return load_from_file(config, settings, &path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = config
|
||||||
|
.dotenv_filename
|
||||||
|
.as_deref()
|
||||||
|
.unwrap_or(DEFAULT_DOTENV_FILENAME)
|
||||||
|
.to_owned();
|
||||||
|
|
||||||
for directory in working_directory.ancestors() {
|
for directory in working_directory.ancestors() {
|
||||||
let path = directory.join(".env");
|
let path = directory.join(&filename);
|
||||||
|
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
if settings.dotenv_load.is_none()
|
return load_from_file(config, settings, &path);
|
||||||
&& config.verbosity.loud()
|
|
||||||
&& !std::env::var_os("JUST_SUPPRESS_DOTENV_LOAD_WARNING")
|
|
||||||
.map(|val| val.as_os_str().to_str() == Some("1"))
|
|
||||||
.unwrap_or(false)
|
|
||||||
{
|
|
||||||
eprintln!(
|
|
||||||
"{}",
|
|
||||||
Warning::DotenvLoad.color_display(config.color.stderr())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
let iter = dotenv::from_path_iter(&path)?;
|
|
||||||
let mut dotenv = BTreeMap::new();
|
|
||||||
for result in iter {
|
|
||||||
let (key, value) = result?;
|
|
||||||
if env::var_os(&key).is_none() {
|
|
||||||
dotenv.insert(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Ok(dotenv);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(BTreeMap::new())
|
Ok(BTreeMap::new())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_from_file(
|
||||||
|
config: &Config,
|
||||||
|
settings: &Settings,
|
||||||
|
path: &Path,
|
||||||
|
) -> RunResult<'static, BTreeMap<String, String>> {
|
||||||
|
// `dotenv::from_path_iter` should eventually be un-deprecated, see:
|
||||||
|
// https://github.com/dotenv-rs/dotenv/issues/13
|
||||||
|
#![allow(deprecated)]
|
||||||
|
|
||||||
|
if config.verbosity.loud()
|
||||||
|
&& settings.dotenv_load.is_none()
|
||||||
|
&& config.dotenv_filename.is_none()
|
||||||
|
&& config.dotenv_path.is_none()
|
||||||
|
&& !std::env::var_os("JUST_SUPPRESS_DOTENV_LOAD_WARNING")
|
||||||
|
.map(|val| val.as_os_str().to_str() == Some("1"))
|
||||||
|
.unwrap_or(false)
|
||||||
|
{
|
||||||
|
eprintln!(
|
||||||
|
"{}",
|
||||||
|
Warning::DotenvLoad.color_display(config.color.stderr())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let iter = dotenv::from_path_iter(&path)?;
|
||||||
|
let mut dotenv = BTreeMap::new();
|
||||||
|
for result in iter {
|
||||||
|
let (key, value) = result?;
|
||||||
|
if env::var_os(&key).is_none() {
|
||||||
|
dotenv.insert(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(dotenv)
|
||||||
|
}
|
||||||
|
100
tests/dotenv.rs
100
tests/dotenv.rs
@ -101,3 +101,103 @@ echo $DOTENV_KEY
|
|||||||
.suppress_dotenv_load_warning(false)
|
.suppress_dotenv_load_warning(false)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_not_found() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
foo:
|
||||||
|
echo $NAME
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.args(&["--dotenv-path", ".env.prod"])
|
||||||
|
.stderr(if cfg!(windows) {
|
||||||
|
"error: Failed to load environment file: The system cannot find the file specified. (os \
|
||||||
|
error 2)\n"
|
||||||
|
} else {
|
||||||
|
"error: Failed to load environment file: No such file or directory (os error 2)\n"
|
||||||
|
})
|
||||||
|
.status(EXIT_FAILURE)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_resolves() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
foo:
|
||||||
|
@echo $NAME
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.tree(tree! {
|
||||||
|
subdir: {
|
||||||
|
".env": "NAME=bar"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.args(&["--dotenv-path", "subdir/.env"])
|
||||||
|
.stdout("bar\n")
|
||||||
|
.status(EXIT_SUCCESS)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filename_resolves() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
foo:
|
||||||
|
@echo $NAME
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.tree(tree! {
|
||||||
|
".env.special": "NAME=bar"
|
||||||
|
})
|
||||||
|
.args(&["--dotenv-filename", ".env.special"])
|
||||||
|
.stdout("bar\n")
|
||||||
|
.status(EXIT_SUCCESS)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn filename_flag_overwrites_no_load() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load := false
|
||||||
|
|
||||||
|
foo:
|
||||||
|
@echo $NAME
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.tree(tree! {
|
||||||
|
".env.special": "NAME=bar"
|
||||||
|
})
|
||||||
|
.args(&["--dotenv-filename", ".env.special"])
|
||||||
|
.stdout("bar\n")
|
||||||
|
.status(EXIT_SUCCESS)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn path_flag_overwrites_no_load() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
set dotenv-load := false
|
||||||
|
|
||||||
|
foo:
|
||||||
|
@echo $NAME
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.tree(tree! {
|
||||||
|
subdir: {
|
||||||
|
".env": "NAME=bar"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.args(&["--dotenv-path", "subdir/.env"])
|
||||||
|
.stdout("bar\n")
|
||||||
|
.status(EXIT_SUCCESS)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user