From 40840a042fb1799cd5c57831a80c8538fae41776 Mon Sep 17 00:00:00 2001 From: Razvan Maracine <56192552+R3ZV@users.noreply.github.com> Date: Sat, 23 Nov 2024 22:22:01 +0200 Subject: [PATCH] Add `--allow-missing` to ignore missing recipe and submodule errors (#2460) --- src/config.rs | 10 +++++++++ src/error.rs | 2 +- src/subcommand.rs | 9 ++++++++ tests/allow_missing.rs | 50 ++++++++++++++++++++++++++++++++++++++++++ tests/fallback.rs | 10 ++++----- tests/lib.rs | 1 + tests/misc.rs | 10 ++++----- tests/modules.rs | 12 +++++----- tests/show.rs | 8 +++---- 9 files changed, 91 insertions(+), 21 deletions(-) create mode 100644 tests/allow_missing.rs diff --git a/src/config.rs b/src/config.rs index d0b91d671b..6eb8d23b1d 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,6 +9,7 @@ use { #[derive(Debug, PartialEq)] pub(crate) struct Config { + pub(crate) allow_missing: bool, pub(crate) check: bool, pub(crate) color: Color, pub(crate) command_color: Option, @@ -80,6 +81,7 @@ mod cmd { } mod arg { + pub(crate) const ALLOW_MISSING: &str = "ALLOW-MISSING"; pub(crate) const ARGUMENTS: &str = "ARGUMENTS"; pub(crate) const CHECK: &str = "CHECK"; pub(crate) const CHOOSER: &str = "CHOOSER"; @@ -315,6 +317,13 @@ impl Config { .help("Suppress all output") .conflicts_with(arg::DRY_RUN), ) + .arg( + Arg::new(arg::ALLOW_MISSING) + .long("allow-missing") + .env("JUST_ALLOW_MISSING") + .action(ArgAction::SetTrue) + .help("Ignore missing recipe and module errors"), + ) .arg( Arg::new(arg::SET) .long("set") @@ -706,6 +715,7 @@ impl Config { let explain = matches.get_flag(arg::EXPLAIN); Ok(Self { + allow_missing: matches.get_flag(arg::ALLOW_MISSING), check: matches.get_flag(arg::CHECK), color: (*matches.get_one::(arg::COLOR).unwrap()).into(), command_color: matches diff --git a/src/error.rs b/src/error.rs index dd6955fe41..72438048b2 100644 --- a/src/error.rs +++ b/src/error.rs @@ -468,7 +468,7 @@ impl<'src> ColorDisplay for Error<'src> { write!(f, "{count} {overrides} overridden on the command line but not present in justfile")?; } UnknownRecipe { recipe, suggestion } => { - write!(f, "Justfile does not contain recipe `{recipe}`.")?; + write!(f, "Justfile does not contain recipe `{recipe}`")?; if let Some(suggestion) = suggestion { write!(f, "\n{suggestion}")?; } diff --git a/src/subcommand.rs b/src/subcommand.rs index 848bd59f38..cc47d879a1 100644 --- a/src/subcommand.rs +++ b/src/subcommand.rs @@ -145,6 +145,15 @@ impl Subcommand { } } + if config.allow_missing + && matches!( + result, + Err(Error::UnknownRecipe { .. } | Error::UnknownSubmodule { .. }) + ) + { + return Ok(()); + } + return result; } } diff --git a/tests/allow_missing.rs b/tests/allow_missing.rs new file mode 100644 index 0000000000..b2221120f1 --- /dev/null +++ b/tests/allow_missing.rs @@ -0,0 +1,50 @@ +use super::*; + +#[test] +fn allow_missing_recipes_in_run_invocation() { + Test::new() + .arg("foo") + .stderr("error: Justfile does not contain recipe `foo`\n") + .status(EXIT_FAILURE) + .run(); + + Test::new().args(["--allow-missing", "foo"]).run(); +} + +#[test] +fn allow_missing_modules_in_run_invocation() { + Test::new() + .arg("foo::bar") + .stderr("error: Justfile does not contain submodule `foo`\n") + .status(EXIT_FAILURE) + .run(); + + Test::new().args(["--allow-missing", "foo::bar"]).run(); +} + +#[test] +fn allow_missing_does_not_apply_to_compilation_errors() { + Test::new() + .justfile("bar: foo") + .args(["--allow-missing", "foo"]) + .stderr( + " + error: Recipe `bar` has unknown dependency `foo` + ——▶ justfile:1:6 + │ + 1 │ bar: foo + │ ^^^ + ", + ) + .status(EXIT_FAILURE) + .run(); +} + +#[test] +fn allow_missing_does_not_apply_to_other_subcommands() { + Test::new() + .args(["--allow-missing", "--show", "foo"]) + .stderr("error: Justfile does not contain recipe `foo`\n") + .status(EXIT_FAILURE) + .run(); +} diff --git a/tests/fallback.rs b/tests/fallback.rs index 238a12a7c8..62e96b5966 100644 --- a/tests/fallback.rs +++ b/tests/fallback.rs @@ -176,7 +176,7 @@ fn requires_setting() { .args(["foo"]) .current_dir("bar") .status(EXIT_FAILURE) - .stderr("error: Justfile does not contain recipe `foo`.\n") + .stderr("error: Justfile does not contain recipe `foo`\n") .run(); } @@ -230,7 +230,7 @@ fn doesnt_work_with_justfile() { .args(["--justfile", "justfile", "foo"]) .current_dir("bar") .status(EXIT_FAILURE) - .stderr("error: Justfile does not contain recipe `foo`.\n") + .stderr("error: Justfile does not contain recipe `foo`\n") .run(); } @@ -254,7 +254,7 @@ fn doesnt_work_with_justfile_and_working_directory() { .args(["--justfile", "justfile", "--working-directory", ".", "foo"]) .current_dir("bar") .status(EXIT_FAILURE) - .stderr("error: Justfile does not contain recipe `foo`.\n") + .stderr("error: Justfile does not contain recipe `foo`\n") .run(); } @@ -282,7 +282,7 @@ fn prints_correct_error_message_when_recipe_not_found() { .status(EXIT_FAILURE) .stderr( " - error: Justfile does not contain recipe `foo`. + error: Justfile does not contain recipe `foo` ", ) .run(); @@ -355,7 +355,7 @@ fn stop_fallback_when_fallback_is_false() { .current_dir("a/b") .stderr( " - error: Justfile does not contain recipe `baz`. + error: Justfile does not contain recipe `baz` Did you mean `bar`? ", ) diff --git a/tests/lib.rs b/tests/lib.rs index ce6ab1393c..3e8a4c12e7 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -33,6 +33,7 @@ mod test; mod allow_duplicate_recipes; mod allow_duplicate_variables; +mod allow_missing; mod assert_stdout; mod assert_success; mod assertions; diff --git a/tests/misc.rs b/tests/misc.rs index 590c13cd35..ab16d5e208 100644 --- a/tests/misc.rs +++ b/tests/misc.rs @@ -594,7 +594,7 @@ foo A B: ", args: ("foo", "ONE", "TWO", "THREE"), stdout: "", - stderr: "error: Justfile does not contain recipe `THREE`.\n", + stderr: "error: Justfile does not contain recipe `THREE`\n", status: EXIT_FAILURE, } @@ -618,7 +618,7 @@ foo A B='B': ", args: ("foo", "ONE", "TWO", "THREE"), stdout: "", - stderr: "error: Justfile does not contain recipe `THREE`.\n", + stderr: "error: Justfile does not contain recipe `THREE`\n", status: EXIT_FAILURE, } @@ -643,7 +643,7 @@ test! { justfile: "hello:", args: ("foo"), stdout: "", - stderr: "error: Justfile does not contain recipe `foo`.\n", + stderr: "error: Justfile does not contain recipe `foo`\n", status: EXIT_FAILURE, } @@ -652,7 +652,7 @@ test! { justfile: "hello:", args: ("foo", "bar"), stdout: "", - stderr: "error: Justfile does not contain recipe `foo`.\n", + stderr: "error: Justfile does not contain recipe `foo`\n", status: EXIT_FAILURE, } @@ -999,7 +999,7 @@ a Z="\t z": "#, args: ("hell"), stdout: "", - stderr: "error: Justfile does not contain recipe `hell`.\nDid you mean `hello`?\n", + stderr: "error: Justfile does not contain recipe `hell`\nDid you mean `hello`?\n", status: EXIT_FAILURE, } diff --git a/tests/modules.rs b/tests/modules.rs index 1d8911af8e..904c13da08 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -77,19 +77,19 @@ fn nested_module_recipes_can_be_run_with_path_syntax() { fn invalid_path_syntax() { Test::new() .arg(":foo::foo") - .stderr("error: Justfile does not contain recipe `:foo::foo`.\n") + .stderr("error: Justfile does not contain recipe `:foo::foo`\n") .status(EXIT_FAILURE) .run(); Test::new() .arg("foo::foo:") - .stderr("error: Justfile does not contain recipe `foo::foo:`.\n") + .stderr("error: Justfile does not contain recipe `foo::foo:`\n") .status(EXIT_FAILURE) .run(); Test::new() .arg("foo:::foo") - .stderr("error: Justfile does not contain recipe `foo:::foo`.\n") + .stderr("error: Justfile does not contain recipe `foo:::foo`\n") .status(EXIT_FAILURE) .run(); } @@ -99,7 +99,7 @@ fn missing_recipe_after_invalid_path() { Test::new() .arg(":foo::foo") .arg("bar") - .stderr("error: Justfile does not contain recipe `:foo::foo`.\n") + .stderr("error: Justfile does not contain recipe `:foo::foo`\n") .status(EXIT_FAILURE) .run(); } @@ -655,7 +655,7 @@ fn submodule_recipe_not_found_spaced_error_message() { ", ) .args(["foo", "baz"]) - .stderr("error: Justfile does not contain recipe `foo baz`.\nDid you mean `bar`?\n") + .stderr("error: Justfile does not contain recipe `foo baz`\nDid you mean `bar`?\n") .status(1) .run(); } @@ -670,7 +670,7 @@ fn submodule_recipe_not_found_colon_separated_error_message() { ", ) .args(["foo::baz"]) - .stderr("error: Justfile does not contain recipe `foo::baz`.\nDid you mean `bar`?\n") + .stderr("error: Justfile does not contain recipe `foo::baz`\nDid you mean `bar`?\n") .status(1) .run(); } diff --git a/tests/show.rs b/tests/show.rs index a66f9089e8..f2b3d26804 100644 --- a/tests/show.rs +++ b/tests/show.rs @@ -48,7 +48,7 @@ a Z="\t z": "#, args: ("--show", "hell"), stdout: "", - stderr: "error: Justfile does not contain recipe `hell`.\nDid you mean `hello`?\n", + stderr: "error: Justfile does not contain recipe `hell`\nDid you mean `hello`?\n", status: EXIT_FAILURE, } @@ -65,7 +65,7 @@ a Z="\t z": args: ("--show", "fo"), stdout: "", stderr: " - error: Justfile does not contain recipe `fo`. + error: Justfile does not contain recipe `fo` Did you mean `foo`, an alias for `hello`? ", status: EXIT_FAILURE, @@ -81,7 +81,7 @@ a Z="\t z": "#, args: ("--show", "hell"), stdout: "", - stderr: "error: Justfile does not contain recipe `hell`.\n", + stderr: "error: Justfile does not contain recipe `hell`\n", status: EXIT_FAILURE, } @@ -97,7 +97,7 @@ a Z="\t z": "#, args: ("--show", "fooooooo"), stdout: "", - stderr: "error: Justfile does not contain recipe `fooooooo`.\n", + stderr: "error: Justfile does not contain recipe `fooooooo`\n", status: EXIT_FAILURE, }