nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: casey/just
Pull request #1417 (2022-11-19 20:28:27)

Do not include shebang lines on Windows


Patch:
diff --git a/src/shebang.rs b/src/shebang.rs
index 1b7852a3..00cfe26e 100644
--- a/src/shebang.rs
+++ b/src/shebang.rs
@@ -47,7 +47,7 @@ impl<'line> Shebang<'line> {
   }
 
   pub(crate) fn include_shebang_line(&self) -> bool {
-    !matches!(self.interpreter_filename(), "cmd" | "cmd.exe")
+    !cfg!(windows) && !matches!(self.interpreter_filename(), "cmd" | "cmd.exe")
   }
 }
 
@@ -201,7 +201,14 @@ mod tests {
   }
 
   #[test]
-  fn include_shebang_line_other() {
+  #[cfg(not(windows))]
+  fn include_shebang_line_other_not_windows() {
     assert!(Shebang::new("#!foo -c").unwrap().include_shebang_line());
   }
+
+  #[test]
+  #[cfg(windows)]
+  fn include_shebang_line_other_windows() {
+    assert!(!Shebang::new("#!foo -c").unwrap().include_shebang_line());
+  }
 }


Test patch:
diff --git a/tests/misc.rs b/tests/misc.rs
index 1c5ef204..4a903bca 100644
--- a/tests/misc.rs
+++ b/tests/misc.rs
@@ -1081,6 +1081,7 @@ test! {
   stderr:   "#!/bin/sh\necho hello\n",
 }
 
+#[cfg(not(windows))]
 test! {
   name:     shebang_line_numbers,
   justfile: r#"
@@ -1100,6 +1101,36 @@ test! {
     #!/usr/bin/env cat
 
 
+    a
+
+    b
+
+
+    c
+  ",
+}
+
+#[cfg(windows)]
+test! {
+  name:     shebang_line_numbers,
+  justfile: r#"
+    quiet:
+      #!/usr/bin/env cat
+
+      a
+
+      b
+
+
+      c
+
+
+  "#,
+  stdout:   "
+
+
+
+
     a
 
     b
diff --git a/tests/tempdir.rs b/tests/tempdir.rs
index 843048be..72e50212 100644
--- a/tests/tempdir.rs
+++ b/tests/tempdir.rs
@@ -24,13 +24,21 @@ fn test_tempdir_is_set() {
       }
     })
     .current_dir("foo")
-    .stdout(
+    .stdout(if cfg!(windows) {
+      "
+
+
+
+
+      cat just*/foo
+      "
+    } else {
       "
       #!/usr/bin/env bash
 
 
       cat just*/foo
-      ",
-    )
+      "
+    })
     .run();
 }
Repository: casey/just
Pull request #1422 (2022-11-23 00:22:32)

Add `[private]` recipe attribute


Patch:
diff --git a/README.md b/README.md
index 4285fbcb..c05dd726 100644
--- a/README.md
+++ b/README.md
@@ -2007,6 +2007,21 @@ $ just --summary
 test
 ```
 
+The `[private]` attribute<sup>master</sup> may also be used to hide recipes without needing to change the name:
+
+```make
+[private]
+foo:
+
+bar:
+```
+
+```sh
+$ just --list
+Available recipes:
+    bar
+```
+
 This is useful for helper recipes which are only meant to be used as dependencies of other recipes.
 
 ### Quiet Recipes
diff --git a/src/attribute.rs b/src/attribute.rs
index c205f340..ffd532f4 100644
--- a/src/attribute.rs
+++ b/src/attribute.rs
@@ -10,6 +10,7 @@ pub(crate) enum Attribute {
   Macos,
   NoCd,
   NoExitMessage,
+  Private,
   Unix,
   Windows,
 }
diff --git a/src/recipe.rs b/src/recipe.rs
index 5fda4496..fa0aedb5 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -63,7 +63,7 @@ impl<'src, D> Recipe<'src, D> {
   }
 
   pub(crate) fn public(&self) -> bool {
-    !self.private
+    !self.private && !self.attributes.contains(&Attribute::Private)
   }
 
   pub(crate) fn change_directory(&self) -> bool {


Test patch:
diff --git a/tests/lib.rs b/tests/lib.rs
index e6c7ed49..11d4d191 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -65,6 +65,7 @@ mod no_exit_message;
 mod os_attributes;
 mod parser;
 mod positional_arguments;
+mod private;
 mod quiet;
 mod quote;
 mod readme;
diff --git a/tests/private.rs b/tests/private.rs
new file mode 100644
index 00000000..82e3d4f6
--- /dev/null
+++ b/tests/private.rs
@@ -0,0 +1,19 @@
+use super::*;
+
+#[test]
+fn attribute() {
+  Test::new()
+    .justfile(
+      "
+      [private]
+      foo:
+      ",
+    )
+    .args(&["--list"])
+    .stdout(
+      "
+      Available recipes:
+      ",
+    )
+    .run();
+}
Repository: casey/just
Pull request #2776 (2025-06-15 01:09:47)

Add link to justfile created by `--init`


Patch:
diff --git a/src/lib.rs b/src/lib.rs
index 1bdf9ad1..81722a2a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -151,7 +151,7 @@ use request::Request;
 
 // Used in integration tests.
 #[doc(hidden)]
-pub use {request::Response, unindent::unindent};
+pub use {request::Response, subcommand::INIT_JUSTFILE, unindent::unindent};
 
 type CompileResult<'a, T = ()> = Result<T, CompileError<'a>>;
 type ConfigResult<T> = Result<T, ConfigError>;
diff --git a/src/subcommand.rs b/src/subcommand.rs
index 885244ca..2a7d62fd 100644
--- a/src/subcommand.rs
+++ b/src/subcommand.rs
@@ -1,6 +1,11 @@
 use {super::*, clap_mangen::Man};
 
-const INIT_JUSTFILE: &str = "default:\n    echo 'Hello, world!'\n";
+pub const INIT_JUSTFILE: &str = "\
+# https://just.systems
+
+foo:
+    echo 'Hello, world!'
+";
 
 fn backtick_re() -> &'static Regex {
   static BACKTICK_RE: OnceLock<Regex> = OnceLock::new();


Test patch:
diff --git a/tests/init.rs b/tests/init.rs
index 118d0537..dc92721a 100644
--- a/tests/init.rs
+++ b/tests/init.rs
@@ -1,6 +1,4 @@
-use super::*;
-
-const EXPECTED: &str = "default:\n    echo 'Hello, world!'\n";
+use {super::*, just::INIT_JUSTFILE};
 
 #[test]
 fn current_dir() {
@@ -16,7 +14,7 @@ fn current_dir() {
 
   assert_eq!(
     fs::read_to_string(tmp.path().join("justfile")).unwrap(),
-    EXPECTED
+    INIT_JUSTFILE
   );
 }
 
@@ -72,7 +70,7 @@ fn invocation_directory() {
     .arg("--init")
     .run();
 
-  assert_eq!(fs::read_to_string(justfile_path).unwrap(), EXPECTED);
+  assert_eq!(fs::read_to_string(justfile_path).unwrap(), INIT_JUSTFILE);
 }
 
 #[test]
@@ -92,7 +90,7 @@ fn parent_dir() {
 
   assert_eq!(
     fs::read_to_string(tmp.path().join("justfile")).unwrap(),
-    EXPECTED
+    INIT_JUSTFILE
   );
 }
 
@@ -112,7 +110,7 @@ fn alternate_marker() {
 
   assert_eq!(
     fs::read_to_string(tmp.path().join("justfile")).unwrap(),
-    EXPECTED
+    INIT_JUSTFILE
   );
 }
 
@@ -135,7 +133,7 @@ fn search_directory() {
 
   assert_eq!(
     fs::read_to_string(tmp.path().join("sub/justfile")).unwrap(),
-    EXPECTED
+    INIT_JUSTFILE
   );
 }
 
@@ -159,7 +157,7 @@ fn justfile() {
 
   assert_eq!(
     fs::read_to_string(tmp.path().join("justfile")).unwrap(),
-    EXPECTED
+    INIT_JUSTFILE
   );
 }
 
@@ -185,7 +183,7 @@ fn justfile_and_working_directory() {
 
   assert_eq!(
     fs::read_to_string(tmp.path().join("justfile")).unwrap(),
-    EXPECTED
+    INIT_JUSTFILE
   );
 }
Repository: casey/just
Pull request #2815 (2025-07-14 20:45:00)

Only override root-justfile variable assignments
Fixes #2814.


Patch:
diff --git a/src/justfile.rs b/src/justfile.rs
index 183ac98a..50a4354b 100644
--- a/src/justfile.rs
+++ b/src/justfile.rs
@@ -86,7 +86,15 @@ impl<'src> Justfile<'src> {
     scopes.get(&self.module_path).unwrap();
 
     for module in self.modules.values() {
-      module.evaluate_assignments(arena, config, dotenv, overrides, root, scopes, search)?;
+      module.evaluate_assignments(
+        arena,
+        config,
+        dotenv,
+        &BTreeMap::new(),
+        root,
+        scopes,
+        search,
+      )?;
     }
 
     Ok(())


Test patch:
diff --git a/tests/modules.rs b/tests/modules.rs
index 904c13da..b27ee592 100644
--- a/tests/modules.rs
+++ b/tests/modules.rs
@@ -987,7 +987,7 @@ fn empty_doc_attribute_on_module() {
   Test::new()
     .write("foo.just", "")
     .justfile(
-      r"
+      "
         # Suppressed comment
         [doc]
         mod foo
@@ -998,3 +998,23 @@ fn empty_doc_attribute_on_module() {
     .stdout("Available recipes:\n    foo ...\n")
     .run();
 }
+
+#[test]
+fn overrides_work_when_submodule_is_present() {
+  Test::new()
+    .write("bar.just", "")
+    .justfile(
+      "
+        mod bar
+
+        x := 'a'
+
+        foo:
+          @echo {{ x }}
+      ",
+    )
+    .test_round_trip(false)
+    .arg("x=b")
+    .stdout("b\n")
+    .run();
+}
Repository: casey/just
Pull request #2820 (2025-07-15 20:41:14)

Fix nested scope lookup


Patch:
diff --git a/src/recipe.rs b/src/recipe.rs
index eb9c55a3..6b5727b6 100644
--- a/src/recipe.rs
+++ b/src/recipe.rs
@@ -59,7 +59,7 @@ impl<'src, D> Recipe<'src, D> {
 
   pub(crate) fn module_path(&self) -> String {
     let mut path = self.namepath.to_string();
-    path.truncate(path.find("::").unwrap_or_default());
+    path.truncate(path.rfind("::").unwrap_or_default());
     path
   }
 


Test patch:
diff --git a/tests/scope.rs b/tests/scope.rs
index b76919f4..97c36c07 100644
--- a/tests/scope.rs
+++ b/tests/scope.rs
@@ -32,3 +32,22 @@ fn aliases_in_submodules_run_with_submodule_scope() {
     .stderr("echo X X\n")
     .run();
 }
+
+#[test]
+fn dependencies_in_nested_submodules_run_with_submodule_scope() {
+  Test::new()
+    .write(
+      "b.just",
+      "
+x := 'y'
+
+foo:
+    @echo {{ x }}
+",
+    )
+    .write("a.just", "mod b")
+    .stdout("y\n")
+    .justfile("mod a")
+    .args(["a", "b", "foo"])
+    .run();
+}
Repository: casey/just
Pull request #2412 (2024-10-05 05:55:03)

Forbid invalid attributes on assignments


Patch:
diff --git a/src/parser.rs b/src/parser.rs
index da2ff76f..c9b499d0 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -480,18 +480,31 @@ impl<'run, 'src> Parser<'run, 'src> {
   fn parse_assignment(
     &mut self,
     export: bool,
-    attributes: BTreeSet<Attribute<'_>>,
+    attributes: BTreeSet<Attribute<'src>>,
   ) -> CompileResult<'src, Assignment<'src>> {
     let name = self.parse_name()?;
     self.presume(ColonEquals)?;
     let value = self.parse_expression()?;
     self.expect_eol()?;
+
+    let private = attributes.contains(&Attribute::Private);
+
+    for attribute in attributes {
+      if attribute != Attribute::Private {
+        return Err(name.error(CompileErrorKind::InvalidAttribute {
+          item_kind: "Assignment",
+          item_name: name.lexeme(),
+          attribute,
+        }));
+      }
+    }
+
     Ok(Assignment {
       file_depth: self.file_depth,
       export,
       name,
       value,
-      private: name.lexeme().starts_with('_') || attributes.contains(&Attribute::Private),
+      private: private || name.lexeme().starts_with('_'),
     })
   }
 


Test patch:
diff --git a/tests/assignment.rs b/tests/assignment.rs
index ae6ea6eb..efacc2c6 100644
--- a/tests/assignment.rs
+++ b/tests/assignment.rs
@@ -3,29 +3,54 @@ use super::*;
 test! {
   name: set_export_parse_error,
   justfile: "
-  set export := fals
+    set export := fals
   ",
   stdout: "",
   stderr: "
     error: Expected keyword `true` or `false` but found identifier `fals`
-      |
-    1 | set export := fals
-      |               ^^^^
+     ——▶ justfile:1:15
+      │
+    1 │ set export := fals
+      │               ^^^^
   ",
   status: EXIT_FAILURE,
 }
 
 test! {
-  name: set_export_parse_error_EOL,
+  name: set_export_parse_error_eol,
   justfile: "
-  set export := fals
+    set export :=
   ",
   stdout: "",
   stderr: "
-    error: Expected keyword `true` or `false` but found `end of line`
-      |
-    1 | set export := 
-      |               ^
+    error: Expected identifier, but found end of line
+     ——▶ justfile:1:14
+      │
+    1 │ set export :=
+      │              ^
   ",
   status: EXIT_FAILURE,
-}
\ No newline at end of file
+}
+
+#[test]
+fn invalid_attributes_are_an_error() {
+  Test::new()
+    .justfile(
+      "
+        [group: 'bar']
+        x := 'foo'
+      ",
+    )
+    .args(["--evaluate", "x"])
+    .stderr(
+      "
+        error: Assignment `x` has invalid attribute `group`
+         ——▶ justfile:2:1
+          │
+        2 │ x := 'foo'
+          │ ^
+      ",
+    )
+    .status(EXIT_FAILURE)
+    .run();
+}
diff --git a/tests/lib.rs b/tests/lib.rs
index 681207f3..ec71c665 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -36,6 +36,7 @@ mod allow_duplicate_variables;
 mod assert_stdout;
 mod assert_success;
 mod assertions;
+mod assignment;
 mod attributes;
 mod backticks;
 mod byte_order_mark;
Repository: casey/just
Pull request #2415 (2024-10-05 21:17:23)

Recipes with private attribute have private set to true in JSON dump


Patch:
diff --git a/src/parser.rs b/src/parser.rs
index c9b499d0..ea727204 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -901,6 +901,8 @@ impl<'run, 'src> Parser<'run, 'src> {
       }));
     }
 
+    let private = name.lexeme().starts_with('_') || attributes.contains(&Attribute::Private);
+
     Ok(Recipe {
       shebang: shebang || script,
       attributes,
@@ -914,7 +916,7 @@ impl<'run, 'src> Parser<'run, 'src> {
       namepath: self.module_namepath.join(name),
       parameters: positional.into_iter().chain(variadic).collect(),
       priors,
-      private: name.lexeme().starts_with('_'),
+      private,
       quiet,
     })
   }


Test patch:
diff --git a/tests/json.rs b/tests/json.rs
index bf962a1e..3819c040 100644
--- a/tests/json.rs
+++ b/tests/json.rs
@@ -1375,3 +1375,57 @@ fn module_group() {
     ))
     .run();
 }
+
+#[test]
+fn recipes_with_private_attribute_are_private() {
+  case(
+    "
+      [private]
+      foo:
+    ",
+    json!({
+      "aliases": {},
+      "assignments": {},
+      "first": "foo",
+      "doc": null,
+      "groups": [],
+      "modules": {},
+      "recipes": {
+        "foo": {
+          "attributes": ["private"],
+          "body": [],
+          "dependencies": [],
+          "doc": null,
+          "name": "foo",
+          "namepath": "foo",
+          "parameters": [],
+          "priors": 0,
+          "private": true,
+          "quiet": false,
+          "shebang": false,
+        }
+      },
+      "settings": {
+        "allow_duplicate_recipes": false,
+        "allow_duplicate_variables": false,
+        "dotenv_filename": null,
+        "dotenv_load": false,
+        "dotenv_path": null,
+        "dotenv_required": false,
+        "export": false,
+        "fallback": false,
+        "ignore_comments": false,
+        "positional_arguments": false,
+        "quiet": false,
+        "shell": null,
+        "tempdir" : null,
+        "unstable": false,
+        "windows_powershell": false,
+        "windows_shell": null,
+        "working_directory" : null,
+      },
+      "unexports": [],
+      "warnings": [],
+    }),
+  );
+}
Repository: casey/just
Pull request #2469 (2024-11-17 00:18:56)

Change doc backtick color to cyan


Patch:
diff --git a/src/color.rs b/src/color.rs
index ba437eff..953b8ae9 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -67,7 +67,7 @@ impl Color {
   }
 
   pub(crate) fn doc_backtick(self) -> Self {
-    self.restyle(Style::new().fg(White).on(Black))
+    self.restyle(Style::new().fg(Cyan))
   }
 
   pub(crate) fn error(self) -> Self {


Test patch:
diff --git a/tests/list.rs b/tests/list.rs
index f7ea7528..53a1c737 100644
--- a/tests/list.rs
+++ b/tests/list.rs
@@ -452,7 +452,7 @@ fn backticks_highlighted() {
     .stdout(
       "
         Available recipes:
-            recipe \u{1b}[34m#\u{1b}[0m \u{1b}[34mComment \u{1b}[0m\u{1b}[40;37m``\u{1b}[0m\u{1b}[34m \u{1b}[0m\u{1b}[40;37m`with backticks`\u{1b}[0m\u{1b}[34m and trailing text\u{1b}[0m
+            recipe \u{1b}[34m#\u{1b}[0m \u{1b}[34mComment \u{1b}[0m\u{1b}[36m``\u{1b}[0m\u{1b}[34m \u{1b}[0m\u{1b}[36m`with backticks`\u{1b}[0m\u{1b}[34m and trailing text\u{1b}[0m
       ")
     .run();
 }
@@ -470,7 +470,7 @@ fn unclosed_backticks() {
     .stdout(
       "
         Available recipes:
-            recipe \u{1b}[34m#\u{1b}[0m \u{1b}[34mComment \u{1b}[0m\u{1b}[40;37m`with unclosed backick\u{1b}[0m
+            recipe \u{1b}[34m#\u{1b}[0m \u{1b}[34mComment \u{1b}[0m\u{1b}[36m`with unclosed backick\u{1b}[0m
       ")
     .run();
 }
Repository: casey/just
Pull request #2507 (2024-12-07 11:03:17)

Add "file_content(filename)" function
This PR adds `file_content()` function, which loads a file content as a string.

In task runners like `just`, one often needs to load from the file system (like "VERSION.txt" etc.).


Patch:
diff --git a/README.md b/README.md
index 4e0dcf5c..f076218b 100644
--- a/README.md
+++ b/README.md
@@ -1842,6 +1842,8 @@ which will halt execution.
 - `path_exists(path)` - Returns `true` if the path points at an existing entity
   and `false` otherwise. Traverses symbolic links, and returns `false` if the
   path is inaccessible or points to a broken symlink.
+- `read_to_string(path)`<sup>master</sup> - Returns the content of file at
+  `path` as string.
 
 ##### Error Reporting
 
diff --git a/src/function.rs b/src/function.rs
index abeae943..d292955a 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -87,6 +87,7 @@ pub(crate) fn get(name: &str) -> Option<Function> {
     "path_exists" => Unary(path_exists),
     "prepend" => Binary(prepend),
     "quote" => Unary(quote),
+    "read_to_string" => Unary(read_to_string),
     "replace" => Ternary(replace),
     "replace_regex" => Ternary(replace_regex),
     "semver_matches" => Binary(semver_matches),
@@ -528,6 +529,11 @@ fn quote(_context: Context, s: &str) -> FunctionResult {
   Ok(format!("'{}'", s.replace('\'', "'\\''")))
 }
 
+fn read_to_string(context: Context, filename: &str) -> FunctionResult {
+  fs::read_to_string(context.evaluator.context.working_directory().join(filename))
+    .map_err(|err| format!("I/O error reading `{filename}`: {err}"))
+}
+
 fn replace(_context: Context, s: &str, from: &str, to: &str) -> FunctionResult {
   Ok(s.replace(from, to))
 }


Test patch:
diff --git a/tests/functions.rs b/tests/functions.rs
index d68b3946..e29e0411 100644
--- a/tests/functions.rs
+++ b/tests/functions.rs
@@ -1258,3 +1258,23 @@ fn style_unknown() {
     .status(EXIT_FAILURE)
     .run();
 }
+
+#[test]
+fn read_to_string() {
+  Test::new()
+    .justfile("foo := read_to_string('bar')")
+    .write("bar", "baz")
+    .args(["--evaluate", "foo"])
+    .stdout("baz")
+    .run();
+}
+
+#[test]
+fn read_to_string_not_found() {
+  Test::new()
+    .justfile("foo := read_to_string('bar')")
+    .args(["--evaluate", "foo"])
+    .stderr_regex(r"error: Call to function `read_to_string` failed: I/O error reading `bar`: .*")
+    .status(EXIT_FAILURE)
+    .run();
+}
Repository: casey/just
Pull request #2518 (2024-12-10 22:56:34)

Rename `read_to_string` to `read`


Patch:
diff --git a/README.md b/README.md
index f076218b..586ad997 100644
--- a/README.md
+++ b/README.md
@@ -1842,8 +1842,8 @@ which will halt execution.
 - `path_exists(path)` - Returns `true` if the path points at an existing entity
   and `false` otherwise. Traverses symbolic links, and returns `false` if the
   path is inaccessible or points to a broken symlink.
-- `read_to_string(path)`<sup>master</sup> - Returns the content of file at
-  `path` as string.
+- `read(path)`<sup>master</sup> - Returns the content of file at `path` as
+  string.
 
 ##### Error Reporting
 
diff --git a/src/function.rs b/src/function.rs
index d292955a..104aeb02 100644
--- a/src/function.rs
+++ b/src/function.rs
@@ -87,7 +87,7 @@ pub(crate) fn get(name: &str) -> Option<Function> {
     "path_exists" => Unary(path_exists),
     "prepend" => Binary(prepend),
     "quote" => Unary(quote),
-    "read_to_string" => Unary(read_to_string),
+    "read" => Unary(read),
     "replace" => Ternary(replace),
     "replace_regex" => Ternary(replace_regex),
     "semver_matches" => Binary(semver_matches),
@@ -529,7 +529,7 @@ fn quote(_context: Context, s: &str) -> FunctionResult {
   Ok(format!("'{}'", s.replace('\'', "'\\''")))
 }
 
-fn read_to_string(context: Context, filename: &str) -> FunctionResult {
+fn read(context: Context, filename: &str) -> FunctionResult {
   fs::read_to_string(context.evaluator.context.working_directory().join(filename))
     .map_err(|err| format!("I/O error reading `{filename}`: {err}"))
 }


Test patch:
diff --git a/tests/functions.rs b/tests/functions.rs
index e29e0411..e4766325 100644
--- a/tests/functions.rs
+++ b/tests/functions.rs
@@ -1260,9 +1260,9 @@ fn style_unknown() {
 }
 
 #[test]
-fn read_to_string() {
+fn read() {
   Test::new()
-    .justfile("foo := read_to_string('bar')")
+    .justfile("foo := read('bar')")
     .write("bar", "baz")
     .args(["--evaluate", "foo"])
     .stdout("baz")
@@ -1270,11 +1270,11 @@ fn read_to_string() {
 }
 
 #[test]
-fn read_to_string_not_found() {
+fn read_file_not_found() {
   Test::new()
-    .justfile("foo := read_to_string('bar')")
+    .justfile("foo := read('bar')")
     .args(["--evaluate", "foo"])
-    .stderr_regex(r"error: Call to function `read_to_string` failed: I/O error reading `bar`: .*")
+    .stderr_regex(r"error: Call to function `read` failed: I/O error reading `bar`: .*")
     .status(EXIT_FAILURE)
     .run();
 }
Repository: casey/just
Pull request #2522 (2024-12-11 23:22:45)

Fix truncated bang operator error message


Patch:
diff --git a/src/compile_error.rs b/src/compile_error.rs
index 593f4f59..ce53c12f 100644
--- a/src/compile_error.rs
+++ b/src/compile_error.rs
@@ -255,7 +255,7 @@ impl Display for CompileError<'_> {
       UnexpectedEndOfToken { expected } => {
         write!(
           f,
-          "Expected character `{}` but found end-of-file",
+          "Expected character {} but found end-of-file",
           List::or_ticked(expected),
         )
       }


Test patch:
diff --git a/tests/parser.rs b/tests/parser.rs
index 3512991c..32a57a92 100644
--- a/tests/parser.rs
+++ b/tests/parser.rs
@@ -32,3 +32,20 @@ error: Expected character `=` or `~`
     )
     .run();
 }
+
+#[test]
+fn truncated_bang_operator() {
+  Test::new()
+    .justfile("x := if '' !")
+    .status(1)
+    .stderr(
+      r"
+error: Expected character `=` or `~` but found end-of-file
+ ——▶ justfile:1:13
+  │
+1 │ x := if '' !
+  │             ^
+",
+    )
+    .run();
+}
Repository: casey/just
Pull request #2622 (2025-02-07 18:45:20)

Make `--list-submodules` require `--list`


Patch:
diff --git a/src/config.rs b/src/config.rs
index 9ed26fca..3e17ed0c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -289,7 +289,7 @@ impl Config {
           .env("JUST_LIST_SUBMODULES")
           .help("List recipes in submodules")
           .action(ArgAction::SetTrue)
-          .env("JUST_LIST_SUBMODULES"),
+          .requires(cmd::LIST),
       )
       .arg(
         Arg::new(arg::NO_ALIASES)


Test patch:
diff --git a/tests/list.rs b/tests/list.rs
index 53a1c737..1d937718 100644
--- a/tests/list.rs
+++ b/tests/list.rs
@@ -474,3 +474,12 @@ fn unclosed_backticks() {
       ")
     .run();
 }
+
+#[test]
+fn list_submodules_requires_list() {
+  Test::new()
+    .arg("--list-submodules")
+    .stderr_regex("error: the following required arguments were not provided:\n  --list .*")
+    .status(2)
+    .run();
+}
Repository: casey/just
Pull request #328 (2018-07-01 01:49:21)

Improve invalid escape sequence error messages
The invalid escape sequence error message is delimited with backticks and isn't used as input to other programs, so tweak the escaping rules slightly when printing invalid escape sequences. In particular, `, \, ', and " should not be escaped.

Improves one of the messages that @runimp saw in #327


Patch:
diff --git a/src/compilation_error.rs b/src/compilation_error.rs
index 4d629a22..0fc9fc55 100644
--- a/src/compilation_error.rs
+++ b/src/compilation_error.rs
@@ -67,9 +67,16 @@ impl<'a> Display for CompilationError<'a> {
                       variable, circle.join(" -> "))?;
         }
       }
+
       InvalidEscapeSequence{character} => {
-        writeln!(f, "`\\{}` is not a valid escape sequence",
-                    character.escape_default().collect::<String>())?;
+        let representation = match character {
+          '`'  => r"\`".to_string(),
+          '\\' => r"\".to_string(),
+          '\'' => r"'".to_string(),
+          '"' => r#"""#.to_string(),
+          _ => character.escape_default().collect(),
+        };
+        writeln!(f, "`\\{}` is not a valid escape sequence", representation)?;
       }
       DuplicateParameter{recipe, parameter} => {
         writeln!(f, "Recipe `{}` has duplicate parameter `{}`", recipe, parameter)?;


Test patch:
diff --git a/tests/integration.rs b/tests/integration.rs
index cd999772..5bf39bef 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -1800,3 +1800,18 @@ echo:
    stderr:   "echo dotenv-value\necho dotenv-value\n",
    status:   EXIT_SUCCESS,
 }
+
+integration_test! {
+   name:     invalid_escape_sequence_message,
+   justfile: r#"
+X = "\'"
+"#,
+   args:     (),
+   stdout:   "",
+   stderr:   r#"error: `\'` is not a valid escape sequence
+  |
+2 | X = "\'"
+  |     ^^^^
+"#,
+   status:   EXIT_FAILURE,
+}