Add --global-justfile
flag (#1846)
This commit is contained in:
parent
104608d8cc
commit
a343f5c80c
27
README.md
27
README.md
@ -3268,13 +3268,30 @@ Before `just` was a fancy Rust program it was a tiny shell script that called
|
||||
`make`. You can find the old version in
|
||||
[contrib/just.sh](https://github.com/casey/just/blob/master/contrib/just.sh).
|
||||
|
||||
### User `justfile`s
|
||||
### Global and User `justfile`s
|
||||
|
||||
If you want some recipes to be available everywhere, you have a few options.
|
||||
|
||||
First, create a `justfile` in `~/.user.justfile` with some recipes.
|
||||
#### Global Justfile
|
||||
|
||||
#### Recipe Aliases
|
||||
`just --global-justfile`, or `just -g` for short, searches the following paths,
|
||||
in-order, for a justfile:
|
||||
|
||||
- `$XDG_CONFIG_HOME/just/justfile`
|
||||
- `$HOME/.config/just/justfile`
|
||||
- `$HOME/justfile`
|
||||
- `$HOME/.justfile`
|
||||
|
||||
You can put recipes that are used across many projects in a global justfile to
|
||||
easily invoke them from any directory.
|
||||
|
||||
#### User justfile tips
|
||||
|
||||
You can also adopt some of the following workflows. These tips assume you've
|
||||
created a `justfile` at `~/.user.justfile`, but you can put this `justfile`
|
||||
at any convenient path on your system.
|
||||
|
||||
##### Recipe Aliases
|
||||
|
||||
If you want to call the recipes in `~/.user.justfile` by name, and don't mind
|
||||
creating an alias for every recipe, add the following to your shell's
|
||||
@ -3293,7 +3310,7 @@ It took me way too long to realize that you could create recipe aliases like
|
||||
this. Notwithstanding my tardiness, I am very pleased to bring you this major
|
||||
advance in `justfile` technology.
|
||||
|
||||
#### Forwarding Alias
|
||||
##### Forwarding Alias
|
||||
|
||||
If you'd rather not create aliases for every recipe, you can create a single alias:
|
||||
|
||||
@ -3308,7 +3325,7 @@ I'm pretty sure that nobody actually uses this feature, but it's there.
|
||||
|
||||
¯\\\_(ツ)\_/¯
|
||||
|
||||
#### Customization
|
||||
##### Customization
|
||||
|
||||
You can customize the above aliases with additional options. For example, if
|
||||
you'd prefer to have the recipes in your `justfile` run in your home directory,
|
||||
|
@ -30,7 +30,7 @@ _just() {
|
||||
|
||||
case "${cmd}" in
|
||||
"$1")
|
||||
opts="-n -f -q -u -v -d -c -e -l -s -E -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --man --show --summary --variables --dotenv-filename --dotenv-path --help --version [ARGUMENTS]..."
|
||||
opts="-n -f -q -u -v -d -c -e -l -s -E -g -h -V --check --chooser --color --command-color --yes --dry-run --dump-format --highlight --list-heading --list-prefix --no-aliases --no-deps --no-dotenv --no-highlight --justfile --quiet --set --shell --shell-arg --shell-command --clear-shell-args --unsorted --unstable --verbose --working-directory --changelog --choose --command --completions --dump --edit --evaluate --fmt --init --list --man --show --summary --variables --dotenv-filename --dotenv-path --global-justfile --help --version [ARGUMENTS]..."
|
||||
if [[ ${cur} == -* ]] ; then
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
|
||||
return 0
|
||||
|
@ -69,6 +69,8 @@ set edit:completion:arg-completer[just] = {|@words|
|
||||
cand --man 'Print man page'
|
||||
cand --summary 'List names of available recipes'
|
||||
cand --variables 'List names of variables'
|
||||
cand -g 'Use global justfile'
|
||||
cand --global-justfile 'Use global justfile'
|
||||
cand -h 'Print help'
|
||||
cand --help 'Print help'
|
||||
cand -V 'Print version'
|
||||
|
@ -76,5 +76,6 @@ complete -c just -s l -l list -d 'List available recipes and their arguments'
|
||||
complete -c just -l man -d 'Print man page'
|
||||
complete -c just -l summary -d 'List names of available recipes'
|
||||
complete -c just -l variables -d 'List names of variables'
|
||||
complete -c just -s g -l global-justfile -d 'Use global justfile'
|
||||
complete -c just -s h -l help -d 'Print help'
|
||||
complete -c just -s V -l version -d 'Print version'
|
||||
|
@ -72,6 +72,8 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
|
||||
[CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page')
|
||||
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
|
||||
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
|
||||
[CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, 'Use global justfile')
|
||||
[CompletionResult]::new('--global-justfile', 'global-justfile', [CompletionResultType]::ParameterName, 'Use global justfile')
|
||||
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
|
||||
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')
|
||||
|
@ -67,6 +67,8 @@ _just() {
|
||||
'--man[Print man page]' \
|
||||
'--summary[List names of available recipes]' \
|
||||
'--variables[List names of variables]' \
|
||||
'(-f --justfile -d --working-directory)-g[Use global justfile]' \
|
||||
'(-f --justfile -d --working-directory)--global-justfile[Use global justfile]' \
|
||||
'-h[Print help]' \
|
||||
'--help[Print help]' \
|
||||
'-V[Print version]' \
|
||||
|
@ -94,6 +94,7 @@ mod arg {
|
||||
pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH";
|
||||
pub(crate) const DRY_RUN: &str = "DRY-RUN";
|
||||
pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT";
|
||||
pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL_JUSTFILE";
|
||||
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
|
||||
pub(crate) const JUSTFILE: &str = "JUSTFILE";
|
||||
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
|
||||
@ -465,6 +466,15 @@ impl Config {
|
||||
.action(ArgAction::Append)
|
||||
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new(arg::GLOBAL_JUSTFILE)
|
||||
.action(ArgAction::SetTrue)
|
||||
.long("global-justfile")
|
||||
.short('g')
|
||||
.conflicts_with(arg::JUSTFILE)
|
||||
.conflicts_with(arg::WORKING_DIRECTORY)
|
||||
.help("Use global justfile")
|
||||
)
|
||||
}
|
||||
|
||||
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
|
||||
@ -520,6 +530,39 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn search_config(matches: &ArgMatches, positional: &Positional) -> ConfigResult<SearchConfig> {
|
||||
if matches.get_flag(arg::GLOBAL_JUSTFILE) {
|
||||
return Ok(SearchConfig::GlobalJustfile);
|
||||
}
|
||||
|
||||
let justfile = matches.get_one::<PathBuf>(arg::JUSTFILE).map(Into::into);
|
||||
|
||||
let working_directory = matches
|
||||
.get_one::<PathBuf>(arg::WORKING_DIRECTORY)
|
||||
.map(Into::into);
|
||||
|
||||
if let Some(search_directory) = positional.search_directory.as_ref().map(PathBuf::from) {
|
||||
if justfile.is_some() || working_directory.is_some() {
|
||||
return Err(ConfigError::SearchDirConflict);
|
||||
}
|
||||
Ok(SearchConfig::FromSearchDirectory { search_directory })
|
||||
} else {
|
||||
match (justfile, working_directory) {
|
||||
(None, None) => Ok(SearchConfig::FromInvocationDirectory),
|
||||
(Some(justfile), None) => Ok(SearchConfig::WithJustfile { justfile }),
|
||||
(Some(justfile), Some(working_directory)) => {
|
||||
Ok(SearchConfig::WithJustfileAndWorkingDirectory {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
(None, Some(_)) => Err(ConfigError::internal(
|
||||
"--working-directory set without --justfile",
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_matches(matches: &ArgMatches) -> ConfigResult<Self> {
|
||||
let invocation_directory = env::current_dir().context(config_error::CurrentDirContext)?;
|
||||
|
||||
@ -545,39 +588,11 @@ impl Config {
|
||||
.map(|s| s.map(String::as_str)),
|
||||
);
|
||||
|
||||
for (name, value) in positional.overrides {
|
||||
for (name, value) in &positional.overrides {
|
||||
overrides.insert(name.clone(), value.clone());
|
||||
}
|
||||
|
||||
let search_config = {
|
||||
let justfile = matches.get_one::<PathBuf>(arg::JUSTFILE).map(Into::into);
|
||||
let working_directory = matches
|
||||
.get_one::<PathBuf>(arg::WORKING_DIRECTORY)
|
||||
.map(Into::into);
|
||||
|
||||
if let Some(search_directory) = positional.search_directory.map(PathBuf::from) {
|
||||
if justfile.is_some() || working_directory.is_some() {
|
||||
return Err(ConfigError::SearchDirConflict);
|
||||
}
|
||||
SearchConfig::FromSearchDirectory { search_directory }
|
||||
} else {
|
||||
match (justfile, working_directory) {
|
||||
(None, None) => SearchConfig::FromInvocationDirectory,
|
||||
(Some(justfile), None) => SearchConfig::WithJustfile { justfile },
|
||||
(Some(justfile), Some(working_directory)) => {
|
||||
SearchConfig::WithJustfileAndWorkingDirectory {
|
||||
justfile,
|
||||
working_directory,
|
||||
}
|
||||
}
|
||||
(None, Some(_)) => {
|
||||
return Err(ConfigError::internal(
|
||||
"--working-directory set without --justfile",
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
let search_config = Self::search_config(matches, &positional)?;
|
||||
|
||||
for subcommand in cmd::ARGLESS {
|
||||
if matches.get_flag(subcommand) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use {super::*, std::path::Component};
|
||||
|
||||
const DEFAULT_JUSTFILE_NAME: &str = JUSTFILE_NAMES[0];
|
||||
pub(crate) const JUSTFILE_NAMES: &[&str] = &["justfile", ".justfile"];
|
||||
pub(crate) const JUSTFILE_NAMES: [&str; 2] = ["justfile", ".justfile"];
|
||||
const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
|
||||
|
||||
pub(crate) struct Search {
|
||||
@ -10,6 +10,29 @@ pub(crate) struct Search {
|
||||
}
|
||||
|
||||
impl Search {
|
||||
fn global_justfile_paths() -> Vec<PathBuf> {
|
||||
let mut paths = Vec::new();
|
||||
|
||||
if let Some(config_dir) = dirs::config_dir() {
|
||||
paths.push(config_dir.join("just").join(DEFAULT_JUSTFILE_NAME));
|
||||
}
|
||||
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
paths.push(
|
||||
home_dir
|
||||
.join(".config")
|
||||
.join("just")
|
||||
.join(DEFAULT_JUSTFILE_NAME),
|
||||
);
|
||||
|
||||
for justfile_name in JUSTFILE_NAMES {
|
||||
paths.push(home_dir.join(justfile_name));
|
||||
}
|
||||
}
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
pub(crate) fn find(
|
||||
search_config: &SearchConfig,
|
||||
invocation_directory: &Path,
|
||||
@ -18,21 +41,24 @@ impl Search {
|
||||
SearchConfig::FromInvocationDirectory => Self::find_next(invocation_directory),
|
||||
SearchConfig::FromSearchDirectory { search_directory } => {
|
||||
let search_directory = Self::clean(invocation_directory, search_directory);
|
||||
|
||||
let justfile = Self::justfile(&search_directory)?;
|
||||
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
SearchConfig::GlobalJustfile => Ok(Self {
|
||||
justfile: Self::global_justfile_paths()
|
||||
.iter()
|
||||
.find(|path| path.exists())
|
||||
.cloned()
|
||||
.ok_or(SearchError::GlobalJustfileNotFound)?,
|
||||
working_directory: Self::project_root(invocation_directory)?,
|
||||
}),
|
||||
SearchConfig::WithJustfile { justfile } => {
|
||||
let justfile = Self::clean(invocation_directory, justfile);
|
||||
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
@ -50,9 +76,7 @@ impl Search {
|
||||
|
||||
pub(crate) fn find_next(starting_dir: &Path) -> SearchResult<Self> {
|
||||
let justfile = Self::justfile(starting_dir)?;
|
||||
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
@ -66,39 +90,30 @@ impl Search {
|
||||
match search_config {
|
||||
SearchConfig::FromInvocationDirectory => {
|
||||
let working_directory = Self::project_root(invocation_directory)?;
|
||||
|
||||
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
|
||||
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
|
||||
SearchConfig::FromSearchDirectory { search_directory } => {
|
||||
let search_directory = Self::clean(invocation_directory, search_directory);
|
||||
|
||||
let working_directory = Self::project_root(&search_directory)?;
|
||||
|
||||
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
|
||||
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
|
||||
SearchConfig::GlobalJustfile => Err(SearchError::GlobalJustfileInit),
|
||||
SearchConfig::WithJustfile { justfile } => {
|
||||
let justfile = Self::clean(invocation_directory, justfile);
|
||||
|
||||
let working_directory = Self::working_directory_from_justfile(&justfile)?;
|
||||
|
||||
Ok(Self {
|
||||
justfile,
|
||||
working_directory,
|
||||
})
|
||||
}
|
||||
|
||||
SearchConfig::WithJustfileAndWorkingDirectory {
|
||||
justfile,
|
||||
working_directory,
|
||||
|
@ -9,6 +9,8 @@ pub(crate) enum SearchConfig {
|
||||
FromInvocationDirectory,
|
||||
/// As in `Invocation`, but start from `search_directory`.
|
||||
FromSearchDirectory { search_directory: PathBuf },
|
||||
/// Search for global justfile
|
||||
GlobalJustfile,
|
||||
/// Use user-specified justfile, with the working directory set to the
|
||||
/// directory that contains it.
|
||||
WithJustfile { justfile: PathBuf },
|
||||
|
@ -3,6 +3,10 @@ use super::*;
|
||||
#[derive(Debug, Snafu)]
|
||||
#[snafu(visibility(pub(crate)))]
|
||||
pub(crate) enum SearchError {
|
||||
#[snafu(display("Cannot initialize global justfile"))]
|
||||
GlobalJustfileInit,
|
||||
#[snafu(display("Global justfile not found"))]
|
||||
GlobalJustfileNotFound,
|
||||
#[snafu(display(
|
||||
"I/O error reading directory `{}`: {}",
|
||||
directory.display(),
|
||||
|
@ -1,5 +1,6 @@
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Source<'src> {
|
||||
pub(crate) file_depth: u32,
|
||||
pub(crate) namepath: Namepath<'src>,
|
||||
|
65
tests/global.rs
Normal file
65
tests/global.rs
Normal file
@ -0,0 +1,65 @@
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(target_os = "macos")]
|
||||
fn macos() {
|
||||
let tempdir = tempdir();
|
||||
|
||||
let path = tempdir.path().to_owned();
|
||||
|
||||
Test::with_tempdir(tempdir)
|
||||
.no_justfile()
|
||||
.test_round_trip(false)
|
||||
.write(
|
||||
"Library/Application Support/just/justfile",
|
||||
"@default:\n echo foo",
|
||||
)
|
||||
.env("HOME", path.to_str().unwrap())
|
||||
.args(["--global-justfile"])
|
||||
.stdout("foo\n")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(all(unix, not(target_os = "macos")))]
|
||||
fn not_macos() {
|
||||
let tempdir = tempdir();
|
||||
|
||||
let path = tempdir.path().to_owned();
|
||||
|
||||
Test::with_tempdir(tempdir)
|
||||
.no_justfile()
|
||||
.test_round_trip(false)
|
||||
.write("just/justfile", "@default:\n echo foo")
|
||||
.env("XDG_CONFIG_HOME", path.to_str().unwrap())
|
||||
.args(["--global-justfile"])
|
||||
.stdout("foo\n")
|
||||
.run();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(unix)]
|
||||
fn unix() {
|
||||
let tempdir = tempdir();
|
||||
|
||||
let path = tempdir.path().to_owned();
|
||||
|
||||
let tempdir = Test::with_tempdir(tempdir)
|
||||
.no_justfile()
|
||||
.test_round_trip(false)
|
||||
.write("justfile", "@default:\n echo foo")
|
||||
.env("HOME", path.to_str().unwrap())
|
||||
.args(["--global-justfile"])
|
||||
.stdout("foo\n")
|
||||
.run()
|
||||
.tempdir;
|
||||
|
||||
Test::with_tempdir(tempdir)
|
||||
.no_justfile()
|
||||
.test_round_trip(false)
|
||||
.write(".config/just/justfile", "@default:\n echo bar")
|
||||
.env("HOME", path.to_str().unwrap())
|
||||
.args(["--global-justfile"])
|
||||
.stdout("bar\n")
|
||||
.run();
|
||||
}
|
@ -59,6 +59,8 @@ mod export;
|
||||
mod fallback;
|
||||
mod fmt;
|
||||
mod functions;
|
||||
#[cfg(unix)]
|
||||
mod global;
|
||||
mod ignore_comments;
|
||||
mod imports;
|
||||
mod init;
|
||||
|
Loading…
Reference in New Issue
Block a user