Skip to content

Commit a343f5c

Browse files
authored
Add --global-justfile flag (#1846)
1 parent 104608d commit a343f5c

13 files changed

+182
-54
lines changed

README.md

+22-5
Original file line numberDiff line numberDiff line change
@@ -3268,13 +3268,30 @@ Before `just` was a fancy Rust program it was a tiny shell script that called
32683268
`make`. You can find the old version in
32693269
[contrib/just.sh](https://github.com/casey/just/blob/master/contrib/just.sh).
32703270

3271-
### User `justfile`s
3271+
### Global and User `justfile`s
32723272

32733273
If you want some recipes to be available everywhere, you have a few options.
32743274

3275-
First, create a `justfile` in `~/.user.justfile` with some recipes.
3275+
#### Global Justfile
32763276

3277-
#### Recipe Aliases
3277+
`just --global-justfile`, or `just -g` for short, searches the following paths,
3278+
in-order, for a justfile:
3279+
3280+
- `$XDG_CONFIG_HOME/just/justfile`
3281+
- `$HOME/.config/just/justfile`
3282+
- `$HOME/justfile`
3283+
- `$HOME/.justfile`
3284+
3285+
You can put recipes that are used across many projects in a global justfile to
3286+
easily invoke them from any directory.
3287+
3288+
#### User justfile tips
3289+
3290+
You can also adopt some of the following workflows. These tips assume you've
3291+
created a `justfile` at `~/.user.justfile`, but you can put this `justfile`
3292+
at any convenient path on your system.
3293+
3294+
##### Recipe Aliases
32783295

32793296
If you want to call the recipes in `~/.user.justfile` by name, and don't mind
32803297
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
32933310
this. Notwithstanding my tardiness, I am very pleased to bring you this major
32943311
advance in `justfile` technology.
32953312

3296-
#### Forwarding Alias
3313+
##### Forwarding Alias
32973314

32983315
If you'd rather not create aliases for every recipe, you can create a single alias:
32993316

@@ -3308,7 +3325,7 @@ I'm pretty sure that nobody actually uses this feature, but it's there.
33083325

33093326
¯\\\_(ツ)\_
33103327

3311-
#### Customization
3328+
##### Customization
33123329

33133330
You can customize the above aliases with additional options. For example, if
33143331
you'd prefer to have the recipes in your `justfile` run in your home directory,

completions/just.bash

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ _just() {
3030

3131
case "${cmd}" in
3232
"$1")
33-
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]..."
33+
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]..."
3434
if [[ ${cur} == -* ]] ; then
3535
COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
3636
return 0

completions/just.elvish

+2
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ set edit:completion:arg-completer[just] = {|@words|
6969
cand --man 'Print man page'
7070
cand --summary 'List names of available recipes'
7171
cand --variables 'List names of variables'
72+
cand -g 'Use global justfile'
73+
cand --global-justfile 'Use global justfile'
7274
cand -h 'Print help'
7375
cand --help 'Print help'
7476
cand -V 'Print version'

completions/just.fish

+1
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,6 @@ complete -c just -s l -l list -d 'List available recipes and their arguments'
7676
complete -c just -l man -d 'Print man page'
7777
complete -c just -l summary -d 'List names of available recipes'
7878
complete -c just -l variables -d 'List names of variables'
79+
complete -c just -s g -l global-justfile -d 'Use global justfile'
7980
complete -c just -s h -l help -d 'Print help'
8081
complete -c just -s V -l version -d 'Print version'

completions/just.powershell

+2
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ Register-ArgumentCompleter -Native -CommandName 'just' -ScriptBlock {
7272
[CompletionResult]::new('--man', 'man', [CompletionResultType]::ParameterName, 'Print man page')
7373
[CompletionResult]::new('--summary', 'summary', [CompletionResultType]::ParameterName, 'List names of available recipes')
7474
[CompletionResult]::new('--variables', 'variables', [CompletionResultType]::ParameterName, 'List names of variables')
75+
[CompletionResult]::new('-g', 'g', [CompletionResultType]::ParameterName, 'Use global justfile')
76+
[CompletionResult]::new('--global-justfile', 'global-justfile', [CompletionResultType]::ParameterName, 'Use global justfile')
7577
[CompletionResult]::new('-h', 'h', [CompletionResultType]::ParameterName, 'Print help')
7678
[CompletionResult]::new('--help', 'help', [CompletionResultType]::ParameterName, 'Print help')
7779
[CompletionResult]::new('-V', 'V ', [CompletionResultType]::ParameterName, 'Print version')

completions/just.zsh

+2
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ _just() {
6767
'--man[Print man page]' \
6868
'--summary[List names of available recipes]' \
6969
'--variables[List names of variables]' \
70+
'(-f --justfile -d --working-directory)-g[Use global justfile]' \
71+
'(-f --justfile -d --working-directory)--global-justfile[Use global justfile]' \
7072
'-h[Print help]' \
7173
'--help[Print help]' \
7274
'-V[Print version]' \

src/config.rs

+45-30
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ mod arg {
9494
pub(crate) const DOTENV_PATH: &str = "DOTENV-PATH";
9595
pub(crate) const DRY_RUN: &str = "DRY-RUN";
9696
pub(crate) const DUMP_FORMAT: &str = "DUMP-FORMAT";
97+
pub(crate) const GLOBAL_JUSTFILE: &str = "GLOBAL_JUSTFILE";
9798
pub(crate) const HIGHLIGHT: &str = "HIGHLIGHT";
9899
pub(crate) const JUSTFILE: &str = "JUSTFILE";
99100
pub(crate) const LIST_HEADING: &str = "LIST-HEADING";
@@ -465,6 +466,15 @@ impl Config {
465466
.action(ArgAction::Append)
466467
.help("Overrides and recipe(s) to run, defaulting to the first recipe in the justfile"),
467468
)
469+
.arg(
470+
Arg::new(arg::GLOBAL_JUSTFILE)
471+
.action(ArgAction::SetTrue)
472+
.long("global-justfile")
473+
.short('g')
474+
.conflicts_with(arg::JUSTFILE)
475+
.conflicts_with(arg::WORKING_DIRECTORY)
476+
.help("Use global justfile")
477+
)
468478
}
469479

470480
fn color_from_matches(matches: &ArgMatches) -> ConfigResult<Color> {
@@ -520,6 +530,39 @@ impl Config {
520530
}
521531
}
522532

533+
fn search_config(matches: &ArgMatches, positional: &Positional) -> ConfigResult<SearchConfig> {
534+
if matches.get_flag(arg::GLOBAL_JUSTFILE) {
535+
return Ok(SearchConfig::GlobalJustfile);
536+
}
537+
538+
let justfile = matches.get_one::<PathBuf>(arg::JUSTFILE).map(Into::into);
539+
540+
let working_directory = matches
541+
.get_one::<PathBuf>(arg::WORKING_DIRECTORY)
542+
.map(Into::into);
543+
544+
if let Some(search_directory) = positional.search_directory.as_ref().map(PathBuf::from) {
545+
if justfile.is_some() || working_directory.is_some() {
546+
return Err(ConfigError::SearchDirConflict);
547+
}
548+
Ok(SearchConfig::FromSearchDirectory { search_directory })
549+
} else {
550+
match (justfile, working_directory) {
551+
(None, None) => Ok(SearchConfig::FromInvocationDirectory),
552+
(Some(justfile), None) => Ok(SearchConfig::WithJustfile { justfile }),
553+
(Some(justfile), Some(working_directory)) => {
554+
Ok(SearchConfig::WithJustfileAndWorkingDirectory {
555+
justfile,
556+
working_directory,
557+
})
558+
}
559+
(None, Some(_)) => Err(ConfigError::internal(
560+
"--working-directory set without --justfile",
561+
)),
562+
}
563+
}
564+
}
565+
523566
pub(crate) fn from_matches(matches: &ArgMatches) -> ConfigResult<Self> {
524567
let invocation_directory = env::current_dir().context(config_error::CurrentDirContext)?;
525568

@@ -545,39 +588,11 @@ impl Config {
545588
.map(|s| s.map(String::as_str)),
546589
);
547590

548-
for (name, value) in positional.overrides {
591+
for (name, value) in &positional.overrides {
549592
overrides.insert(name.clone(), value.clone());
550593
}
551594

552-
let search_config = {
553-
let justfile = matches.get_one::<PathBuf>(arg::JUSTFILE).map(Into::into);
554-
let working_directory = matches
555-
.get_one::<PathBuf>(arg::WORKING_DIRECTORY)
556-
.map(Into::into);
557-
558-
if let Some(search_directory) = positional.search_directory.map(PathBuf::from) {
559-
if justfile.is_some() || working_directory.is_some() {
560-
return Err(ConfigError::SearchDirConflict);
561-
}
562-
SearchConfig::FromSearchDirectory { search_directory }
563-
} else {
564-
match (justfile, working_directory) {
565-
(None, None) => SearchConfig::FromInvocationDirectory,
566-
(Some(justfile), None) => SearchConfig::WithJustfile { justfile },
567-
(Some(justfile), Some(working_directory)) => {
568-
SearchConfig::WithJustfileAndWorkingDirectory {
569-
justfile,
570-
working_directory,
571-
}
572-
}
573-
(None, Some(_)) => {
574-
return Err(ConfigError::internal(
575-
"--working-directory set without --justfile",
576-
))
577-
}
578-
}
579-
}
580-
};
595+
let search_config = Self::search_config(matches, &positional)?;
581596

582597
for subcommand in cmd::ARGLESS {
583598
if matches.get_flag(subcommand) {

src/search.rs

+33-18
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use {super::*, std::path::Component};
22

33
const DEFAULT_JUSTFILE_NAME: &str = JUSTFILE_NAMES[0];
4-
pub(crate) const JUSTFILE_NAMES: &[&str] = &["justfile", ".justfile"];
4+
pub(crate) const JUSTFILE_NAMES: [&str; 2] = ["justfile", ".justfile"];
55
const PROJECT_ROOT_CHILDREN: &[&str] = &[".bzr", ".git", ".hg", ".svn", "_darcs"];
66

77
pub(crate) struct Search {
@@ -10,6 +10,29 @@ pub(crate) struct Search {
1010
}
1111

1212
impl Search {
13+
fn global_justfile_paths() -> Vec<PathBuf> {
14+
let mut paths = Vec::new();
15+
16+
if let Some(config_dir) = dirs::config_dir() {
17+
paths.push(config_dir.join("just").join(DEFAULT_JUSTFILE_NAME));
18+
}
19+
20+
if let Some(home_dir) = dirs::home_dir() {
21+
paths.push(
22+
home_dir
23+
.join(".config")
24+
.join("just")
25+
.join(DEFAULT_JUSTFILE_NAME),
26+
);
27+
28+
for justfile_name in JUSTFILE_NAMES {
29+
paths.push(home_dir.join(justfile_name));
30+
}
31+
}
32+
33+
paths
34+
}
35+
1336
pub(crate) fn find(
1437
search_config: &SearchConfig,
1538
invocation_directory: &Path,
@@ -18,21 +41,24 @@ impl Search {
1841
SearchConfig::FromInvocationDirectory => Self::find_next(invocation_directory),
1942
SearchConfig::FromSearchDirectory { search_directory } => {
2043
let search_directory = Self::clean(invocation_directory, search_directory);
21-
2244
let justfile = Self::justfile(&search_directory)?;
23-
2445
let working_directory = Self::working_directory_from_justfile(&justfile)?;
25-
2646
Ok(Self {
2747
justfile,
2848
working_directory,
2949
})
3050
}
51+
SearchConfig::GlobalJustfile => Ok(Self {
52+
justfile: Self::global_justfile_paths()
53+
.iter()
54+
.find(|path| path.exists())
55+
.cloned()
56+
.ok_or(SearchError::GlobalJustfileNotFound)?,
57+
working_directory: Self::project_root(invocation_directory)?,
58+
}),
3159
SearchConfig::WithJustfile { justfile } => {
3260
let justfile = Self::clean(invocation_directory, justfile);
33-
3461
let working_directory = Self::working_directory_from_justfile(&justfile)?;
35-
3662
Ok(Self {
3763
justfile,
3864
working_directory,
@@ -50,9 +76,7 @@ impl Search {
5076

5177
pub(crate) fn find_next(starting_dir: &Path) -> SearchResult<Self> {
5278
let justfile = Self::justfile(starting_dir)?;
53-
5479
let working_directory = Self::working_directory_from_justfile(&justfile)?;
55-
5680
Ok(Self {
5781
justfile,
5882
working_directory,
@@ -66,39 +90,30 @@ impl Search {
6690
match search_config {
6791
SearchConfig::FromInvocationDirectory => {
6892
let working_directory = Self::project_root(invocation_directory)?;
69-
7093
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
71-
7294
Ok(Self {
7395
justfile,
7496
working_directory,
7597
})
7698
}
77-
7899
SearchConfig::FromSearchDirectory { search_directory } => {
79100
let search_directory = Self::clean(invocation_directory, search_directory);
80-
81101
let working_directory = Self::project_root(&search_directory)?;
82-
83102
let justfile = working_directory.join(DEFAULT_JUSTFILE_NAME);
84-
85103
Ok(Self {
86104
justfile,
87105
working_directory,
88106
})
89107
}
90-
108+
SearchConfig::GlobalJustfile => Err(SearchError::GlobalJustfileInit),
91109
SearchConfig::WithJustfile { justfile } => {
92110
let justfile = Self::clean(invocation_directory, justfile);
93-
94111
let working_directory = Self::working_directory_from_justfile(&justfile)?;
95-
96112
Ok(Self {
97113
justfile,
98114
working_directory,
99115
})
100116
}
101-
102117
SearchConfig::WithJustfileAndWorkingDirectory {
103118
justfile,
104119
working_directory,

src/search_config.rs

+2
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ pub(crate) enum SearchConfig {
99
FromInvocationDirectory,
1010
/// As in `Invocation`, but start from `search_directory`.
1111
FromSearchDirectory { search_directory: PathBuf },
12+
/// Search for global justfile
13+
GlobalJustfile,
1214
/// Use user-specified justfile, with the working directory set to the
1315
/// directory that contains it.
1416
WithJustfile { justfile: PathBuf },

src/search_error.rs

+4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,10 @@ use super::*;
33
#[derive(Debug, Snafu)]
44
#[snafu(visibility(pub(crate)))]
55
pub(crate) enum SearchError {
6+
#[snafu(display("Cannot initialize global justfile"))]
7+
GlobalJustfileInit,
8+
#[snafu(display("Global justfile not found"))]
9+
GlobalJustfileNotFound,
610
#[snafu(display(
711
"I/O error reading directory `{}`: {}",
812
directory.display(),

src/source.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::*;
22

3+
#[derive(Debug)]
34
pub(crate) struct Source<'src> {
45
pub(crate) file_depth: u32,
56
pub(crate) namepath: Namepath<'src>,

0 commit comments

Comments
 (0)