From 4d865a16450152ecdd79fe48dd253fbfd7aafd08 Mon Sep 17 00:00:00 2001 From: Casey Rodarmor Date: Sat, 21 Sep 2024 13:25:52 +0800 Subject: [PATCH] Add `--one` flag to forbid multiple recipes from being invoked on the command line (#2374) --- README.md | 42 ++++++++++++++++++++++++++++++++++++++++++ src/config.rs | 10 ++++++++++ src/error.rs | 6 ++++++ src/justfile.rs | 6 ++++++ tests/run.rs | 41 +++++++++++++++++++++++++++++++++-------- 5 files changed, 97 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 1299762c..1bee80f5 100644 --- a/README.md +++ b/README.md @@ -753,6 +753,48 @@ $ just --list --list-heading '' build ``` +### Invoking Multiple Recipes + +Multiple recipes may be invoked on the command line at once: + +```just +build: + make web + +serve: + python3 -m http.server -d out 8000 +``` + +```sh +$ just build serve +make web +python3 -m http.server -d out 8000 +``` + +Keep in mind that recipes with parameters will swallow arguments, even if they +match the names of other recipes: + +```just +build project: + make {{project}} + +serve: + python3 -m http.server -d out 8000 +``` + +```sh +$ just build serve +make: *** No rule to make target `serve'. Stop. +``` + +The `--one` flag can be used to restrict command-line invocations to a single +recipe: + +```sh +$ just --one build serve +error: Expected 1 command-line recipe invocation but found 2. +``` + ### Working Directory By default, recipes run with the working directory set to the directory that diff --git a/src/config.rs b/src/config.rs index 356c663f..d0b91d67 100644 --- a/src/config.rs +++ b/src/config.rs @@ -25,6 +25,7 @@ pub(crate) struct Config { pub(crate) load_dotenv: bool, pub(crate) no_aliases: bool, pub(crate) no_dependencies: bool, + pub(crate) one: bool, pub(crate) search_config: SearchConfig, pub(crate) shell: Option, pub(crate) shell_args: Option>, @@ -100,6 +101,7 @@ mod arg { pub(crate) const NO_DEPS: &str = "NO-DEPS"; pub(crate) const NO_DOTENV: &str = "NO-DOTENV"; pub(crate) const NO_HIGHLIGHT: &str = "NO-HIGHLIGHT"; + pub(crate) const ONE: &str = "ONE"; pub(crate) const QUIET: &str = "QUIET"; pub(crate) const SET: &str = "SET"; pub(crate) const SHELL: &str = "SHELL"; @@ -297,6 +299,13 @@ impl Config { .help("Don't highlight echoed recipe lines in bold") .overrides_with(arg::HIGHLIGHT), ) + .arg( + Arg::new(arg::ONE) + .long("one") + .env("JUST_ONE") + .action(ArgAction::SetTrue) + .help("Forbid multiple recipes from being invoked on the command line"), + ) .arg( Arg::new(arg::QUIET) .short('q') @@ -721,6 +730,7 @@ impl Config { load_dotenv: !matches.get_flag(arg::NO_DOTENV), no_aliases: matches.get_flag(arg::NO_ALIASES), no_dependencies: matches.get_flag(arg::NO_DEPS), + one: matches.get_flag(arg::ONE), search_config, shell: matches.get_one::(arg::SHELL).map(Into::into), shell_args: if matches.get_flag(arg::CLEAR_SHELL_ARGS) { diff --git a/src/error.rs b/src/error.rs index cd22311e..dd6955fe 100644 --- a/src/error.rs +++ b/src/error.rs @@ -95,6 +95,9 @@ pub(crate) enum Error<'src> { variable: String, suggestion: Option>, }, + ExcessInvocations { + invocations: usize, + }, ExpectedSubmoduleButFoundRecipe { path: String, }, @@ -373,6 +376,9 @@ impl<'src> ColorDisplay for Error<'src> { write!(f, "\n{suggestion}")?; } } + ExcessInvocations { invocations } => { + write!(f, "Expected 1 command-line recipe invocation but found {invocations}.")?; + }, ExpectedSubmoduleButFoundRecipe { path } => { write!(f, "Expected submodule at `{path}` but found recipe.")?; }, diff --git a/src/justfile.rs b/src/justfile.rs index ab5fc6db..1a4043ff 100644 --- a/src/justfile.rs +++ b/src/justfile.rs @@ -202,6 +202,12 @@ impl<'src> Justfile<'src> { )?); } + if config.one && invocations.len() > 1 { + return Err(Error::ExcessInvocations { + invocations: invocations.len(), + }); + } + let mut ran = Ran::default(); for invocation in invocations { let context = ExecutionContext { diff --git a/tests/run.rs b/tests/run.rs index 7f10cc99..9819023f 100644 --- a/tests/run.rs +++ b/tests/run.rs @@ -5,15 +5,40 @@ fn dont_run_duplicate_recipes() { Test::new() .justfile( " - foo: - # foo - ", + @foo: + echo foo + ", ) .args(["foo", "foo"]) - .stderr( - " - # foo - ", - ) + .stdout("foo\n") + .run(); +} + +#[test] +fn one_flag_only_allows_one_invocation() { + Test::new() + .justfile( + " + @foo: + echo foo + ", + ) + .args(["--one", "foo"]) + .stdout("foo\n") + .run(); + + Test::new() + .justfile( + " + @foo: + echo foo + + @bar: + echo bar + ", + ) + .args(["--one", "foo", "bar"]) + .stderr("error: Expected 1 command-line recipe invocation but found 2.\n") + .status(1) .run(); }