Allow enabling unstable features with set unstable (#2237)

This commit is contained in:
Casey Rodarmor 2024-07-07 20:45:03 -07:00 committed by GitHub
parent 564814208f
commit d6669e0b97
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 136 additions and 46 deletions

View File

@ -379,11 +379,11 @@ There will never be a `just` 2.0. Any desirable backwards-incompatible changes
will be opt-in on a per-`justfile` basis, so users may migrate at their will be opt-in on a per-`justfile` basis, so users may migrate at their
leisure. leisure.
Features that aren't yet ready for stabilization are gated behind the Features that aren't yet ready for stabilization are marked as unstable and may
`--unstable` flag. Features enabled by `--unstable` may change in backwards be changed or removed at any time. Using unstable features produces an error by
incompatible ways at any time. Unstable features can also be enabled by setting default, which can be suppressed with by passing the `--unstable` flag,
the environment variable `JUST_UNSTABLE` to any value other than `false`, `0`, `set unstable`, or setting the environment variable `JUST_UNSTABLE`, to any
or the empty string. value other than `false`, `0`, or the empty string.
Editor Support Editor Support
-------------- --------------
@ -820,6 +820,7 @@ foo:
| `positional-arguments` | boolean | `false` | Pass positional arguments. | | `positional-arguments` | boolean | `false` | Pass positional arguments. |
| `shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. | | `shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
| `tempdir` | string | - | Create temporary directories in `tempdir` instead of the system default temporary directory. | | `tempdir` | string | - | Create temporary directories in `tempdir` instead of the system default temporary directory. |
| `unstable`<sup>master</sup> | boolean | `false` | Enable unstable features. |
| `windows-powershell` | boolean | `false` | Use PowerShell on Windows as default shell. (Deprecated. Use `windows-shell` instead. | | `windows-powershell` | boolean | `false` | Use PowerShell on Windows as default shell. (Deprecated. Use `windows-shell` instead. |
| `windows-shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. | | `windows-shell` | `[COMMAND, ARGS…]` | - | Set the command used to invoke recipes and evaluate backticks. |
@ -3154,8 +3155,8 @@ Missing source files for optional imports do not produce an error.
### Modules<sup>1.19.0</sup> ### Modules<sup>1.19.0</sup>
A `justfile` can declare modules using `mod` statements. `mod` statements are A `justfile` can declare modules using `mod` statements. `mod` statements are
currently unstable, so you'll need to use the `--unstable` flag, or set the currently unstable, so you'll need to use the `--unstable` flag,
`JUST_UNSTABLE` environment variable to use them. `set unstable`, or set the `JUST_UNSTABLE` environment variable to use them.
If you have the following `justfile`: If you have the following `justfile`:

View File

@ -37,6 +37,8 @@ impl<'src> Analyzer<'src> {
let mut warnings = Vec::new(); let mut warnings = Vec::new();
let mut unstable = BTreeSet::new();
let mut modules: Table<Justfile> = Table::new(); let mut modules: Table<Justfile> = Table::new();
let mut unexports: HashSet<String> = HashSet::new(); let mut unexports: HashSet<String> = HashSet::new();
@ -92,6 +94,8 @@ impl<'src> Analyzer<'src> {
doc, doc,
.. ..
} => { } => {
unstable.insert(Unstable::Modules);
if let Some(absolute) = absolute { if let Some(absolute) = absolute {
define(*name, "module", false)?; define(*name, "module", false)?;
modules.insert(Self::analyze( modules.insert(Self::analyze(
@ -194,6 +198,7 @@ impl<'src> Analyzer<'src> {
settings, settings,
source: root.into(), source: root.into(),
unexports, unexports,
unstable,
warnings, warnings,
}) })
} }

View File

@ -252,7 +252,7 @@ mod tests {
fs::write(&path, "mod foo").unwrap(); fs::write(&path, "mod foo").unwrap();
fs::create_dir(tempdir.path().join("foo")).unwrap(); fs::create_dir(tempdir.path().join("foo")).unwrap();
fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap(); fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap();
let compilation = Compiler::compile(true, &loader, &path).unwrap(); let compilation = Compiler::compile(&loader, &path).unwrap();
assert_eq!( assert_eq!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "bar"]).unwrap(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "bar"]).unwrap(),
@ -271,7 +271,7 @@ mod tests {
fs::write(&path, "mod foo").unwrap(); fs::write(&path, "mod foo").unwrap();
fs::create_dir(tempdir.path().join("foo")).unwrap(); fs::create_dir(tempdir.path().join("foo")).unwrap();
fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap(); fs::write(tempdir.path().join("foo/mod.just"), "bar:").unwrap();
let compilation = Compiler::compile(true, &loader, &path).unwrap(); let compilation = Compiler::compile(&loader, &path).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "zzz"]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo", "zzz"]).unwrap_err(),
@ -289,7 +289,7 @@ mod tests {
tempdir.write("foo.just", "bar:"); tempdir.write("foo.just", "bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo::zzz"]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo::zzz"]).unwrap_err(),
@ -307,7 +307,7 @@ mod tests {
tempdir.write("foo.just", "bar:"); tempdir.write("foo.just", "bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &["foo::bar::baz"]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &["foo::bar::baz"]).unwrap_err(),
@ -323,7 +323,7 @@ mod tests {
tempdir.write("justfile", ""); tempdir.write("justfile", "");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),
@ -337,7 +337,7 @@ mod tests {
tempdir.write("justfile", "foo bar:"); tempdir.write("justfile", "foo bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),
@ -355,7 +355,7 @@ mod tests {
tempdir.write("foo.just", "bar:"); tempdir.write("foo.just", "bar:");
let loader = Loader::new(); let loader = Loader::new();
let compilation = Compiler::compile(true, &loader, &tempdir.path().join("justfile")).unwrap(); let compilation = Compiler::compile(&loader, &tempdir.path().join("justfile")).unwrap();
assert_matches!( assert_matches!(
ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(), ArgumentParser::parse_arguments(&compilation.justfile, &[]).unwrap_err(),

View File

@ -4,14 +4,13 @@ pub(crate) struct Compiler;
impl Compiler { impl Compiler {
pub(crate) fn compile<'src>( pub(crate) fn compile<'src>(
unstable: bool,
loader: &'src Loader, loader: &'src Loader,
root: &Path, root: &Path,
) -> RunResult<'src, Compilation<'src>> { ) -> RunResult<'src, Compilation<'src>> {
let mut asts = HashMap::<PathBuf, Ast>::new(); let mut asts = HashMap::<PathBuf, Ast>::new();
let mut loaded = Vec::new();
let mut paths = HashMap::<PathBuf, PathBuf>::new(); let mut paths = HashMap::<PathBuf, PathBuf>::new();
let mut srcs = HashMap::<PathBuf, &str>::new(); let mut srcs = HashMap::<PathBuf, &str>::new();
let mut loaded = Vec::new();
let mut stack = Vec::new(); let mut stack = Vec::new();
stack.push(Source::root(root)); stack.push(Source::root(root));
@ -42,12 +41,6 @@ impl Compiler {
relative, relative,
.. ..
} => { } => {
if !unstable {
return Err(Error::Unstable {
message: "Modules are currently unstable.".into(),
});
}
let parent = current.path.parent().unwrap(); let parent = current.path.parent().unwrap();
let import = if let Some(relative) = relative { let import = if let Some(relative) = relative {
@ -112,9 +105,9 @@ impl Compiler {
Ok(Compilation { Ok(Compilation {
asts, asts,
srcs,
justfile, justfile,
root: root.into(), root: root.into(),
srcs,
}) })
} }
@ -225,7 +218,7 @@ recipe_b: recipe_c
let loader = Loader::new(); let loader = Loader::new();
let justfile_a_path = tmp.path().join("justfile"); let justfile_a_path = tmp.path().join("justfile");
let compilation = Compiler::compile(false, &loader, &justfile_a_path).unwrap(); let compilation = Compiler::compile(&loader, &justfile_a_path).unwrap();
assert_eq!(compilation.root_src(), justfile_a); assert_eq!(compilation.root_src(), justfile_a);
} }
@ -242,7 +235,7 @@ recipe_b: recipe_c
let loader = Loader::new(); let loader = Loader::new();
let justfile_a_path = tmp.path().join("justfile"); let justfile_a_path = tmp.path().join("justfile");
let loader_output = Compiler::compile(false, &loader, &justfile_a_path).unwrap_err(); let loader_output = Compiler::compile(&loader, &justfile_a_path).unwrap_err();
assert_matches!(loader_output, Error::CircularImport { current, import } assert_matches!(loader_output, Error::CircularImport { current, import }
if current == tmp.path().join("subdir").join("b").lexiclean() && if current == tmp.path().join("subdir").join("b").lexiclean() &&

View File

@ -460,7 +460,7 @@ impl<'src> ColorDisplay for Error<'src> {
} }
} }
Unstable { message } => { Unstable { message } => {
write!(f, "{message} Invoke `just` with the `--unstable` flag to enable unstable features.")?; write!(f, "{message} Invoke `just` with `--unstable`, set the `JUST_UNSTABLE` environment variable, or add `set unstable` to your `justfile` to enable unstable features.")?;
} }
WriteJustfile { justfile, io_error } => { WriteJustfile { justfile, io_error } => {
let justfile = justfile.display(); let justfile = justfile.display();

View File

@ -27,6 +27,8 @@ pub(crate) struct Justfile<'src> {
pub(crate) source: PathBuf, pub(crate) source: PathBuf,
pub(crate) unexports: HashSet<String>, pub(crate) unexports: HashSet<String>,
pub(crate) warnings: Vec<Warning>, pub(crate) warnings: Vec<Warning>,
#[serde(skip)]
pub(crate) unstable: BTreeSet<Unstable>,
} }
impl<'src> Justfile<'src> { impl<'src> Justfile<'src> {
@ -225,6 +227,22 @@ impl<'src> Justfile<'src> {
Ok(()) Ok(())
} }
pub(crate) fn check_unstable(&self, config: &Config) -> RunResult<'src> {
if !config.unstable && !self.settings.unstable {
if let Some(unstable) = self.unstable.iter().next() {
return Err(Error::Unstable {
message: unstable.message(),
});
}
}
for module in self.modules.values() {
module.check_unstable(config)?;
}
Ok(())
}
pub(crate) fn get_alias(&self, name: &str) -> Option<&Alias<'src>> { pub(crate) fn get_alias(&self, name: &str) -> Option<&Alias<'src>> {
self.aliases.get(name) self.aliases.get(name)
} }

View File

@ -26,6 +26,7 @@ pub(crate) enum Keyword {
Tempdir, Tempdir,
True, True,
Unexport, Unexport,
Unstable,
WindowsPowershell, WindowsPowershell,
WindowsShell, WindowsShell,
X, X,

View File

@ -42,8 +42,8 @@ pub(crate) use {
shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind, shell::Shell, show_whitespace::ShowWhitespace, source::Source, string_kind::StringKind,
string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table, string_literal::StringLiteral, subcommand::Subcommand, suggestion::Suggestion, table::Table,
thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency, thunk::Thunk, token::Token, token_kind::TokenKind, unresolved_dependency::UnresolvedDependency,
unresolved_recipe::UnresolvedRecipe, use_color::UseColor, variables::Variables, unresolved_recipe::UnresolvedRecipe, unstable::Unstable, use_color::UseColor,
verbosity::Verbosity, warning::Warning, variables::Variables, verbosity::Verbosity, warning::Warning,
}, },
camino::Utf8Path, camino::Utf8Path,
clap::ValueEnum, clap::ValueEnum,
@ -204,6 +204,7 @@ mod token_kind;
mod unindent; mod unindent;
mod unresolved_dependency; mod unresolved_dependency;
mod unresolved_recipe; mod unresolved_recipe;
mod unstable;
mod use_color; mod use_color;
mod variables; mod variables;
mod verbosity; mod verbosity;

View File

@ -294,6 +294,7 @@ impl<'src> Node<'src> for Set<'src> {
| Setting::Fallback(value) | Setting::Fallback(value)
| Setting::PositionalArguments(value) | Setting::PositionalArguments(value)
| Setting::Quiet(value) | Setting::Quiet(value)
| Setting::Unstable(value)
| Setting::WindowsPowerShell(value) | Setting::WindowsPowerShell(value)
| Setting::IgnoreComments(value) => { | Setting::IgnoreComments(value) => {
set.push_mut(value.to_string()); set.push_mut(value.to_string());

View File

@ -936,6 +936,7 @@ impl<'run, 'src> Parser<'run, 'src> {
Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)), Keyword::IgnoreComments => Some(Setting::IgnoreComments(self.parse_set_bool()?)),
Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)), Keyword::PositionalArguments => Some(Setting::PositionalArguments(self.parse_set_bool()?)),
Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)), Keyword::Quiet => Some(Setting::Quiet(self.parse_set_bool()?)),
Keyword::Unstable => Some(Setting::Unstable(self.parse_set_bool()?)),
Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)), Keyword::WindowsPowershell => Some(Setting::WindowsPowerShell(self.parse_set_bool()?)),
_ => None, _ => None,
}; };

View File

@ -15,6 +15,7 @@ pub(crate) enum Setting<'src> {
Quiet(bool), Quiet(bool),
Shell(Shell<'src>), Shell(Shell<'src>),
Tempdir(String), Tempdir(String),
Unstable(bool),
WindowsPowerShell(bool), WindowsPowerShell(bool),
WindowsShell(Shell<'src>), WindowsShell(Shell<'src>),
} }
@ -31,6 +32,7 @@ impl<'src> Display for Setting<'src> {
| Self::IgnoreComments(value) | Self::IgnoreComments(value)
| Self::PositionalArguments(value) | Self::PositionalArguments(value)
| Self::Quiet(value) | Self::Quiet(value)
| Self::Unstable(value)
| Self::WindowsPowerShell(value) => write!(f, "{value}"), | Self::WindowsPowerShell(value) => write!(f, "{value}"),
Self::Shell(shell) | Self::WindowsShell(shell) => write!(f, "{shell}"), Self::Shell(shell) | Self::WindowsShell(shell) => write!(f, "{shell}"),
Self::DotenvFilename(value) | Self::DotenvPath(value) | Self::Tempdir(value) => { Self::DotenvFilename(value) | Self::DotenvPath(value) | Self::Tempdir(value) => {

View File

@ -20,6 +20,7 @@ pub(crate) struct Settings<'src> {
pub(crate) quiet: bool, pub(crate) quiet: bool,
pub(crate) shell: Option<Shell<'src>>, pub(crate) shell: Option<Shell<'src>>,
pub(crate) tempdir: Option<String>, pub(crate) tempdir: Option<String>,
pub(crate) unstable: bool,
pub(crate) windows_powershell: bool, pub(crate) windows_powershell: bool,
pub(crate) windows_shell: Option<Shell<'src>>, pub(crate) windows_shell: Option<Shell<'src>>,
} }
@ -66,6 +67,9 @@ impl<'src> Settings<'src> {
Setting::Shell(shell) => { Setting::Shell(shell) => {
settings.shell = Some(shell); settings.shell = Some(shell);
} }
Setting::Unstable(unstable) => {
settings.unstable = unstable;
}
Setting::WindowsPowerShell(windows_powershell) => { Setting::WindowsPowerShell(windows_powershell) => {
settings.windows_powershell = windows_powershell; settings.windows_powershell = windows_powershell;
} }

View File

@ -190,7 +190,9 @@ impl Subcommand {
loader: &'src Loader, loader: &'src Loader,
search: &Search, search: &Search,
) -> RunResult<'src, Compilation<'src>> { ) -> RunResult<'src, Compilation<'src>> {
let compilation = Compiler::compile(config.unstable, loader, &search.justfile)?; let compilation = Compiler::compile(loader, &search.justfile)?;
compilation.justfile.check_unstable(config)?;
if config.verbosity.loud() { if config.verbosity.loud() {
for warning in &compilation.justfile.warnings { for warning in &compilation.justfile.warnings {

View File

@ -28,7 +28,7 @@ mod full {
pub fn summary(path: &Path) -> io::Result<Result<Summary, String>> { pub fn summary(path: &Path) -> io::Result<Result<Summary, String>> {
let loader = Loader::new(); let loader = Loader::new();
match Compiler::compile(false, &loader, path) { match Compiler::compile(&loader, path) {
Ok(compilation) => Ok(Ok(Summary::new(&compilation.justfile))), Ok(compilation) => Ok(Ok(Summary::new(&compilation.justfile))),
Err(error) => Ok(Err(if let Error::Compile { compile_error } = error { Err(error) => Ok(Err(if let Error::Compile { compile_error } = error {
compile_error.to_string() compile_error.to_string()

12
src/unstable.rs Normal file
View File

@ -0,0 +1,12 @@
#[derive(Copy, Clone, Debug, PartialEq, Ord, Eq, PartialOrd)]
pub(crate) enum Unstable {
Modules,
}
impl Unstable {
pub(crate) fn message(self) -> String {
match self {
Self::Modules => "Modules are currently unstable.".into(),
}
}
}

View File

@ -4,10 +4,7 @@ test! {
name: unstable_not_passed, name: unstable_not_passed,
justfile: "", justfile: "",
args: ("--fmt"), args: ("--fmt"),
stderr: " stderr_regex: "error: The `--fmt` command is currently unstable..*",
error: The `--fmt` command is currently unstable. \
Invoke `just` with the `--unstable` flag to enable unstable features.
",
status: EXIT_FAILURE, status: EXIT_FAILURE,
} }

View File

@ -56,7 +56,9 @@ fn alias() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -98,6 +100,7 @@ fn assignment() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -153,6 +156,7 @@ fn body() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -220,6 +224,7 @@ fn dependencies() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -325,6 +330,7 @@ fn dependency_argument() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -392,6 +398,7 @@ fn duplicate_recipes() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -437,6 +444,7 @@ fn duplicate_variables() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -485,6 +493,7 @@ fn doc_comment() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -519,6 +528,7 @@ fn empty_justfile() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -674,6 +684,7 @@ fn parameters() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -762,6 +773,7 @@ fn priors() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -810,6 +822,7 @@ fn private() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -858,6 +871,7 @@ fn quiet() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -921,6 +935,7 @@ fn settings() {
"command": "a", "command": "a",
}, },
"tempdir": null, "tempdir": null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -972,6 +987,7 @@ fn shebang() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir": null, "tempdir": null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -1020,6 +1036,7 @@ fn simple() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir": null, "tempdir": null,
"unstable": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
}, },
@ -1070,6 +1087,7 @@ fn attribute() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
@ -1136,6 +1154,7 @@ fn module() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,
@ -1158,6 +1177,7 @@ fn module() {
"quiet": false, "quiet": false,
"shell": null, "shell": null,
"tempdir" : null, "tempdir" : null,
"unstable": false,
"ignore_comments": false, "ignore_comments": false,
"windows_powershell": false, "windows_powershell": false,
"windows_shell": null, "windows_shell": null,

View File

@ -8,10 +8,8 @@ fn modules_are_unstable() {
mod foo mod foo
", ",
) )
.stderr( .write("foo.just", "")
"error: Modules are currently unstable. \ .stderr_regex("error: Modules are currently unstable..*")
Invoke `just` with the `--unstable` flag to enable unstable features.\n",
)
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.run(); .run();
} }

View File

@ -150,7 +150,7 @@ impl Test {
} }
pub(crate) fn stderr_regex(mut self, stderr_regex: impl AsRef<str>) -> Self { pub(crate) fn stderr_regex(mut self, stderr_regex: impl AsRef<str>) -> Self {
self.stderr_regex = Some(Regex::new(&format!("^{}$", stderr_regex.as_ref())).unwrap()); self.stderr_regex = Some(Regex::new(&format!("^(?s){}$", stderr_regex.as_ref())).unwrap());
self self
} }

View File

@ -26,12 +26,12 @@ default:
"#; "#;
for val in ["0", "", "false"] { for val in ["0", "", "false"] {
Test::new() Test::new()
.justfile(justfile) .justfile(justfile)
.args(["--fmt"]) .args(["--fmt"])
.env("JUST_UNSTABLE", val) .env("JUST_UNSTABLE", val)
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.stderr("error: The `--fmt` command is currently unstable. Invoke `just` with the `--unstable` flag to enable unstable features.\n") .stderr_regex("error: The `--fmt` command is currently unstable.*")
.run(); .run();
} }
} }
@ -45,6 +45,40 @@ default:
.justfile(justfile) .justfile(justfile)
.args(["--fmt"]) .args(["--fmt"])
.status(EXIT_FAILURE) .status(EXIT_FAILURE)
.stderr("error: The `--fmt` command is currently unstable. Invoke `just` with the `--unstable` flag to enable unstable features.\n") .stderr_regex("error: The `--fmt` command is currently unstable.*")
.run();
}
#[test]
fn set_unstable_with_setting() {
Test::new()
.justfile(
"
set unstable
mod foo
",
)
.write("foo.just", "@bar:\n echo BAR")
.args(["foo", "bar"])
.stdout("BAR\n")
.run();
}
#[test]
fn unstable_setting_does_not_affect_submodules() {
Test::new()
.justfile(
"
set unstable
mod foo
",
)
.write("foo.just", "mod bar")
.write("bar.just", "baz:\n echo hello")
.args(["foo", "bar"])
.stderr_regex("error: Modules are currently unstable.*")
.status(EXIT_FAILURE)
.run(); .run();
} }