Allow unexporting environment variables (#2098)
This commit is contained in:
parent
3ddd1b1683
commit
38873dcb74
17
README.md
17
README.md
@ -2053,6 +2053,23 @@ a $A $B=`echo $A`:
|
|||||||
When [export](#export) is set, all `just` variables are exported as environment
|
When [export](#export) is set, all `just` variables are exported as environment
|
||||||
variables.
|
variables.
|
||||||
|
|
||||||
|
#### Unexporting Environment Variables<sup>master</sup>
|
||||||
|
|
||||||
|
Environment variables can be unexported with the `unexport keyword`:
|
||||||
|
|
||||||
|
```just
|
||||||
|
unexport FOO
|
||||||
|
|
||||||
|
@foo:
|
||||||
|
echo $FOO
|
||||||
|
```
|
||||||
|
|
||||||
|
```
|
||||||
|
$ export FOO=bar
|
||||||
|
$ just foo
|
||||||
|
sh: FOO: unbound variable
|
||||||
|
```
|
||||||
|
|
||||||
#### Getting Environment Variables from the environment
|
#### Getting Environment Variables from the environment
|
||||||
|
|
||||||
Environment variables from the environment are passed automatically to the
|
Environment variables from the environment are passed automatically to the
|
||||||
|
@ -37,6 +37,8 @@ impl<'src> Analyzer<'src> {
|
|||||||
|
|
||||||
let mut modules: Table<Justfile> = Table::new();
|
let mut modules: Table<Justfile> = Table::new();
|
||||||
|
|
||||||
|
let mut unexports: HashSet<String> = HashSet::new();
|
||||||
|
|
||||||
let mut definitions: HashMap<&str, (&'static str, Name)> = HashMap::new();
|
let mut definitions: HashMap<&str, (&'static str, Name)> = HashMap::new();
|
||||||
|
|
||||||
let mut define = |name: Name<'src>,
|
let mut define = |name: Name<'src>,
|
||||||
@ -98,6 +100,13 @@ impl<'src> Analyzer<'src> {
|
|||||||
self.analyze_set(set)?;
|
self.analyze_set(set)?;
|
||||||
self.sets.insert(set.clone());
|
self.sets.insert(set.clone());
|
||||||
}
|
}
|
||||||
|
Item::Unexport { name } => {
|
||||||
|
if !unexports.insert(name.lexeme().to_string()) {
|
||||||
|
return Err(name.token.error(DuplicateUnexport {
|
||||||
|
variable: name.lexeme(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,21 +118,23 @@ impl<'src> Analyzer<'src> {
|
|||||||
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
|
let mut recipe_table: Table<'src, UnresolvedRecipe<'src>> = Table::default();
|
||||||
|
|
||||||
for assignment in assignments {
|
for assignment in assignments {
|
||||||
if !settings.allow_duplicate_variables
|
let variable = assignment.name.lexeme();
|
||||||
&& self.assignments.contains_key(assignment.name.lexeme())
|
|
||||||
{
|
if !settings.allow_duplicate_variables && self.assignments.contains_key(variable) {
|
||||||
return Err(assignment.name.token.error(DuplicateVariable {
|
return Err(assignment.name.token.error(DuplicateVariable { variable }));
|
||||||
variable: assignment.name.lexeme(),
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.assignments
|
.assignments
|
||||||
.get(assignment.name.lexeme())
|
.get(variable)
|
||||||
.map_or(true, |original| assignment.depth <= original.depth)
|
.map_or(true, |original| assignment.depth <= original.depth)
|
||||||
{
|
{
|
||||||
self.assignments.insert(assignment.clone());
|
self.assignments.insert(assignment.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if unexports.contains(variable) {
|
||||||
|
return Err(assignment.name.token.error(ExportUnexported { variable }));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AssignmentResolver::resolve_assignments(&self.assignments)?;
|
AssignmentResolver::resolve_assignments(&self.assignments)?;
|
||||||
@ -167,6 +178,7 @@ impl<'src> Analyzer<'src> {
|
|||||||
recipes,
|
recipes,
|
||||||
settings,
|
settings,
|
||||||
source: root.into(),
|
source: root.into(),
|
||||||
|
unexports,
|
||||||
warnings,
|
warnings,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,25 +1,41 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(crate) trait CommandExt {
|
pub(crate) trait CommandExt {
|
||||||
fn export(&mut self, settings: &Settings, dotenv: &BTreeMap<String, String>, scope: &Scope);
|
fn export(
|
||||||
|
&mut self,
|
||||||
|
settings: &Settings,
|
||||||
|
dotenv: &BTreeMap<String, String>,
|
||||||
|
scope: &Scope,
|
||||||
|
unexports: &HashSet<String>,
|
||||||
|
);
|
||||||
|
|
||||||
fn export_scope(&mut self, settings: &Settings, scope: &Scope);
|
fn export_scope(&mut self, settings: &Settings, scope: &Scope, unexports: &HashSet<String>);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandExt for Command {
|
impl CommandExt for Command {
|
||||||
fn export(&mut self, settings: &Settings, dotenv: &BTreeMap<String, String>, scope: &Scope) {
|
fn export(
|
||||||
|
&mut self,
|
||||||
|
settings: &Settings,
|
||||||
|
dotenv: &BTreeMap<String, String>,
|
||||||
|
scope: &Scope,
|
||||||
|
unexports: &HashSet<String>,
|
||||||
|
) {
|
||||||
for (name, value) in dotenv {
|
for (name, value) in dotenv {
|
||||||
self.env(name, value);
|
self.env(name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(parent) = scope.parent() {
|
if let Some(parent) = scope.parent() {
|
||||||
self.export_scope(settings, parent);
|
self.export_scope(settings, parent, unexports);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn export_scope(&mut self, settings: &Settings, scope: &Scope) {
|
fn export_scope(&mut self, settings: &Settings, scope: &Scope, unexports: &HashSet<String>) {
|
||||||
if let Some(parent) = scope.parent() {
|
if let Some(parent) = scope.parent() {
|
||||||
self.export_scope(settings, parent);
|
self.export_scope(settings, parent, unexports);
|
||||||
|
}
|
||||||
|
|
||||||
|
for unexport in unexports {
|
||||||
|
self.env_remove(unexport);
|
||||||
}
|
}
|
||||||
|
|
||||||
for binding in scope.bindings() {
|
for binding in scope.bindings() {
|
||||||
|
@ -131,6 +131,9 @@ impl Display for CompileError<'_> {
|
|||||||
DuplicateVariable { variable } => {
|
DuplicateVariable { variable } => {
|
||||||
write!(f, "Variable `{variable}` has multiple definitions")
|
write!(f, "Variable `{variable}` has multiple definitions")
|
||||||
}
|
}
|
||||||
|
DuplicateUnexport { variable } => {
|
||||||
|
write!(f, "Variable `{variable}` is unexported multiple times")
|
||||||
|
}
|
||||||
ExpectedKeyword { expected, found } => {
|
ExpectedKeyword { expected, found } => {
|
||||||
let expected = List::or_ticked(expected);
|
let expected = List::or_ticked(expected);
|
||||||
if found.kind == TokenKind::Identifier {
|
if found.kind == TokenKind::Identifier {
|
||||||
@ -143,6 +146,9 @@ impl Display for CompileError<'_> {
|
|||||||
write!(f, "Expected keyword {expected} but found `{}`", found.kind)
|
write!(f, "Expected keyword {expected} but found `{}`", found.kind)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ExportUnexported { variable } => {
|
||||||
|
write!(f, "Variable {variable} is both exported and unexported")
|
||||||
|
}
|
||||||
ExtraLeadingWhitespace => write!(f, "Recipe line has extra leading whitespace"),
|
ExtraLeadingWhitespace => write!(f, "Recipe line has extra leading whitespace"),
|
||||||
FunctionArgumentCountMismatch {
|
FunctionArgumentCountMismatch {
|
||||||
function,
|
function,
|
||||||
|
@ -52,10 +52,16 @@ pub(crate) enum CompileErrorKind<'src> {
|
|||||||
DuplicateVariable {
|
DuplicateVariable {
|
||||||
variable: &'src str,
|
variable: &'src str,
|
||||||
},
|
},
|
||||||
|
DuplicateUnexport {
|
||||||
|
variable: &'src str,
|
||||||
|
},
|
||||||
ExpectedKeyword {
|
ExpectedKeyword {
|
||||||
expected: Vec<Keyword>,
|
expected: Vec<Keyword>,
|
||||||
found: Token<'src>,
|
found: Token<'src>,
|
||||||
},
|
},
|
||||||
|
ExportUnexported {
|
||||||
|
variable: &'src str,
|
||||||
|
},
|
||||||
ExtraLeadingWhitespace,
|
ExtraLeadingWhitespace,
|
||||||
FunctionArgumentCountMismatch {
|
FunctionArgumentCountMismatch {
|
||||||
function: &'src str,
|
function: &'src str,
|
||||||
|
@ -8,6 +8,7 @@ pub(crate) struct Evaluator<'src: 'run, 'run> {
|
|||||||
pub(crate) scope: Scope<'src, 'run>,
|
pub(crate) scope: Scope<'src, 'run>,
|
||||||
pub(crate) search: &'run Search,
|
pub(crate) search: &'run Search,
|
||||||
pub(crate) settings: &'run Settings<'run>,
|
pub(crate) settings: &'run Settings<'run>,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src, 'run> Evaluator<'src, 'run> {
|
impl<'src, 'run> Evaluator<'src, 'run> {
|
||||||
@ -19,6 +20,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: Scope<'src, 'run>,
|
scope: Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
settings: &'run Settings<'run>,
|
settings: &'run Settings<'run>,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
) -> RunResult<'src, Scope<'src, 'run>> {
|
) -> RunResult<'src, Scope<'src, 'run>> {
|
||||||
let mut evaluator = Self {
|
let mut evaluator = Self {
|
||||||
assignments: Some(assignments),
|
assignments: Some(assignments),
|
||||||
@ -28,6 +30,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope,
|
scope,
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
|
unsets,
|
||||||
};
|
};
|
||||||
|
|
||||||
for assignment in assignments.values() {
|
for assignment in assignments.values() {
|
||||||
@ -217,7 +220,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
cmd.arg(command);
|
cmd.arg(command);
|
||||||
cmd.args(args);
|
cmd.args(args);
|
||||||
cmd.current_dir(&self.search.working_directory);
|
cmd.current_dir(&self.search.working_directory);
|
||||||
cmd.export(self.settings, self.dotenv, &self.scope);
|
cmd.export(self.settings, self.dotenv, &self.scope, self.unsets);
|
||||||
cmd.stdin(Stdio::inherit());
|
cmd.stdin(Stdio::inherit());
|
||||||
cmd.stderr(if self.config.verbosity.quiet() {
|
cmd.stderr(if self.config.verbosity.quiet() {
|
||||||
Stdio::null()
|
Stdio::null()
|
||||||
@ -261,6 +264,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
settings: &'run Settings,
|
settings: &'run Settings,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
) -> RunResult<'src, (Scope<'src, 'run>, Vec<String>)> {
|
||||||
let mut evaluator = Self {
|
let mut evaluator = Self {
|
||||||
assignments: None,
|
assignments: None,
|
||||||
@ -270,6 +274,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: scope.child(),
|
scope: scope.child(),
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
|
unsets,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut scope = scope.child();
|
let mut scope = scope.child();
|
||||||
@ -316,6 +321,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: &'run Scope<'src, 'run>,
|
scope: &'run Scope<'src, 'run>,
|
||||||
search: &'run Search,
|
search: &'run Search,
|
||||||
settings: &'run Settings,
|
settings: &'run Settings,
|
||||||
|
unsets: &'run HashSet<String>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
assignments: None,
|
assignments: None,
|
||||||
@ -325,6 +331,7 @@ impl<'src, 'run> Evaluator<'src, 'run> {
|
|||||||
scope: Scope::child(scope),
|
scope: Scope::child(scope),
|
||||||
search,
|
search,
|
||||||
settings,
|
settings,
|
||||||
|
unsets,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ pub(crate) enum Item<'src> {
|
|||||||
},
|
},
|
||||||
Recipe(UnresolvedRecipe<'src>),
|
Recipe(UnresolvedRecipe<'src>),
|
||||||
Set(Set<'src>),
|
Set(Set<'src>),
|
||||||
|
Unexport {
|
||||||
|
name: Name<'src>,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'src> Display for Item<'src> {
|
impl<'src> Display for Item<'src> {
|
||||||
@ -61,6 +64,7 @@ impl<'src> Display for Item<'src> {
|
|||||||
}
|
}
|
||||||
Self::Recipe(recipe) => write!(f, "{}", recipe.color_display(Color::never())),
|
Self::Recipe(recipe) => write!(f, "{}", recipe.color_display(Color::never())),
|
||||||
Self::Set(set) => write!(f, "{set}"),
|
Self::Set(set) => write!(f, "{set}"),
|
||||||
|
Self::Unexport { name } => write!(f, "unexport {name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,7 @@ pub(crate) struct Justfile<'src> {
|
|||||||
pub(crate) settings: Settings<'src>,
|
pub(crate) settings: Settings<'src>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub(crate) source: PathBuf,
|
pub(crate) source: PathBuf,
|
||||||
|
pub(crate) unexports: HashSet<String>,
|
||||||
pub(crate) warnings: Vec<Warning>,
|
pub(crate) warnings: Vec<Warning>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +114,7 @@ impl<'src> Justfile<'src> {
|
|||||||
scope,
|
scope,
|
||||||
search,
|
search,
|
||||||
&self.settings,
|
&self.settings,
|
||||||
|
&self.unexports,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,7 +165,7 @@ impl<'src> Justfile<'src> {
|
|||||||
|
|
||||||
let scope = scope.child();
|
let scope = scope.child();
|
||||||
|
|
||||||
command.export(&self.settings, &dotenv, &scope);
|
command.export(&self.settings, &dotenv, &scope, &self.unexports);
|
||||||
|
|
||||||
let status = InterruptHandler::guard(|| command.status()).map_err(|io_error| {
|
let status = InterruptHandler::guard(|| command.status()).map_err(|io_error| {
|
||||||
Error::CommandInvoke {
|
Error::CommandInvoke {
|
||||||
@ -286,6 +288,7 @@ impl<'src> Justfile<'src> {
|
|||||||
scope: invocation.scope,
|
scope: invocation.scope,
|
||||||
search,
|
search,
|
||||||
settings: invocation.settings,
|
settings: invocation.settings,
|
||||||
|
unexports: &self.unexports,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::run_recipe(
|
Self::run_recipe(
|
||||||
@ -441,6 +444,7 @@ impl<'src> Justfile<'src> {
|
|||||||
context.scope,
|
context.scope,
|
||||||
search,
|
search,
|
||||||
context.settings,
|
context.settings,
|
||||||
|
context.unexports,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let scope = outer.child();
|
let scope = outer.child();
|
||||||
@ -452,6 +456,7 @@ impl<'src> Justfile<'src> {
|
|||||||
&scope,
|
&scope,
|
||||||
search,
|
search,
|
||||||
context.settings,
|
context.settings,
|
||||||
|
context.unexports,
|
||||||
);
|
);
|
||||||
|
|
||||||
if !context.config.no_dependencies {
|
if !context.config.no_dependencies {
|
||||||
|
@ -25,6 +25,7 @@ pub(crate) enum Keyword {
|
|||||||
Shell,
|
Shell,
|
||||||
Tempdir,
|
Tempdir,
|
||||||
True,
|
True,
|
||||||
|
Unexport,
|
||||||
WindowsPowershell,
|
WindowsPowershell,
|
||||||
WindowsShell,
|
WindowsShell,
|
||||||
X,
|
X,
|
||||||
|
@ -42,7 +42,7 @@ pub(crate) use {
|
|||||||
std::{
|
std::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
cmp,
|
cmp,
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
collections::{BTreeMap, BTreeSet, HashMap, HashSet},
|
||||||
env,
|
env,
|
||||||
ffi::OsString,
|
ffi::OsString,
|
||||||
fmt::{self, Debug, Display, Formatter},
|
fmt::{self, Debug, Display, Formatter},
|
||||||
|
@ -54,6 +54,11 @@ impl<'src> Node<'src> for Item<'src> {
|
|||||||
}
|
}
|
||||||
Self::Recipe(recipe) => recipe.tree(),
|
Self::Recipe(recipe) => recipe.tree(),
|
||||||
Self::Set(set) => set.tree(),
|
Self::Set(set) => set.tree(),
|
||||||
|
Self::Unexport { name } => {
|
||||||
|
let mut unexport = Tree::atom(Keyword::Unexport.lexeme());
|
||||||
|
unexport.push_mut(name.lexeme().replace('-', "_"));
|
||||||
|
unexport
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,6 +340,11 @@ impl<'run, 'src> Parser<'run, 'src> {
|
|||||||
self.presume_keyword(Keyword::Export)?;
|
self.presume_keyword(Keyword::Export)?;
|
||||||
items.push(Item::Assignment(self.parse_assignment(true)?));
|
items.push(Item::Assignment(self.parse_assignment(true)?));
|
||||||
}
|
}
|
||||||
|
Some(Keyword::Unexport) => {
|
||||||
|
self.presume_keyword(Keyword::Unexport)?;
|
||||||
|
let name = self.parse_name()?;
|
||||||
|
items.push(Item::Unexport { name });
|
||||||
|
}
|
||||||
Some(Keyword::Import)
|
Some(Keyword::Import)
|
||||||
if self.next_are(&[Identifier, StringToken])
|
if self.next_are(&[Identifier, StringToken])
|
||||||
|| self.next_are(&[Identifier, Identifier, StringToken])
|
|| self.next_are(&[Identifier, Identifier, StringToken])
|
||||||
|
@ -169,6 +169,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
scope,
|
scope,
|
||||||
context.search,
|
context.search,
|
||||||
context.settings,
|
context.settings,
|
||||||
|
context.unexports,
|
||||||
);
|
);
|
||||||
|
|
||||||
if self.shebang {
|
if self.shebang {
|
||||||
@ -279,7 +280,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
cmd.stdout(Stdio::null());
|
cmd.stdout(Stdio::null());
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.export(context.settings, context.dotenv, scope);
|
cmd.export(context.settings, context.dotenv, scope, context.unexports);
|
||||||
|
|
||||||
match InterruptHandler::guard(|| cmd.status()) {
|
match InterruptHandler::guard(|| cmd.status()) {
|
||||||
Ok(exit_status) => {
|
Ok(exit_status) => {
|
||||||
@ -425,7 +426,7 @@ impl<'src, D> Recipe<'src, D> {
|
|||||||
command.args(positional);
|
command.args(positional);
|
||||||
}
|
}
|
||||||
|
|
||||||
command.export(context.settings, context.dotenv, scope);
|
command.export(context.settings, context.dotenv, scope, context.unexports);
|
||||||
|
|
||||||
// run it!
|
// run it!
|
||||||
match InterruptHandler::guard(|| command.status()) {
|
match InterruptHandler::guard(|| command.status()) {
|
||||||
|
@ -7,4 +7,5 @@ pub(crate) struct RecipeContext<'src: 'run, 'run> {
|
|||||||
pub(crate) scope: &'run Scope<'src, 'run>,
|
pub(crate) scope: &'run Scope<'src, 'run>,
|
||||||
pub(crate) search: &'run Search,
|
pub(crate) search: &'run Search,
|
||||||
pub(crate) settings: &'run Settings<'src>,
|
pub(crate) settings: &'run Settings<'src>,
|
||||||
|
pub(crate) unexports: &'run HashSet<String>,
|
||||||
}
|
}
|
||||||
|
@ -29,10 +29,10 @@ fn bash() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn replacements() {
|
fn replacements() {
|
||||||
for shell in ["bash", "elvish", "fish", "powershell", "zsh"] {
|
for shell in ["bash", "elvish", "fish", "powershell", "zsh"] {
|
||||||
let status = Command::new(executable_path("just"))
|
let output = Command::new(executable_path("just"))
|
||||||
.args(["--completions", shell])
|
.args(["--completions", shell])
|
||||||
.status()
|
.output()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert!(status.success());
|
assert!(output.status.success());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,7 @@ fn alias() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -98,6 +99,7 @@ fn assignment() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -151,6 +153,7 @@ fn body() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -216,6 +219,7 @@ fn dependencies() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -319,6 +323,7 @@ fn dependency_argument() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -384,6 +389,7 @@ fn duplicate_recipes() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -427,6 +433,7 @@ fn duplicate_variables() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -473,6 +480,7 @@ fn doc_comment() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -505,6 +513,7 @@ fn empty_justfile() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -658,6 +667,7 @@ fn parameters() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -744,6 +754,7 @@ fn priors() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -790,6 +801,7 @@ fn private() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -836,6 +848,7 @@ fn quiet() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -897,6 +910,7 @@ fn settings() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -946,6 +960,7 @@ fn shebang() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -992,6 +1007,7 @@ fn simple() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1041,6 +1057,7 @@ fn attribute() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1103,6 +1120,7 @@ fn module() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1124,6 +1142,7 @@ fn module() {
|
|||||||
"windows_powershell": false,
|
"windows_powershell": false,
|
||||||
"windows_shell": null,
|
"windows_shell": null,
|
||||||
},
|
},
|
||||||
|
"unexports": [],
|
||||||
"warnings": [],
|
"warnings": [],
|
||||||
}))
|
}))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -104,6 +104,7 @@ mod summary;
|
|||||||
mod tempdir;
|
mod tempdir;
|
||||||
mod timestamps;
|
mod timestamps;
|
||||||
mod undefined_variables;
|
mod undefined_variables;
|
||||||
|
mod unexport;
|
||||||
mod unstable;
|
mod unstable;
|
||||||
#[cfg(target_family = "windows")]
|
#[cfg(target_family = "windows")]
|
||||||
mod windows_shell;
|
mod windows_shell;
|
||||||
|
104
tests/unexport.rs
Normal file
104
tests/unexport.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unexport_environment_variable_linewise() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
unexport JUST_TEST_VARIABLE
|
||||||
|
|
||||||
|
@recipe:
|
||||||
|
echo ${JUST_TEST_VARIABLE:-unset}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.env("JUST_TEST_VARIABLE", "foo")
|
||||||
|
.stdout("unset\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unexport_environment_variable_shebang() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
unexport JUST_TEST_VARIABLE
|
||||||
|
|
||||||
|
recipe:
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
echo ${JUST_TEST_VARIABLE:-unset}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.env("JUST_TEST_VARIABLE", "foo")
|
||||||
|
.stdout("unset\n")
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn duplicate_unexport_fails() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
unexport JUST_TEST_VARIABLE
|
||||||
|
|
||||||
|
recipe:
|
||||||
|
echo \"variable: $JUST_TEST_VARIABLE\"
|
||||||
|
|
||||||
|
unexport JUST_TEST_VARIABLE
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.env("JUST_TEST_VARIABLE", "foo")
|
||||||
|
.stderr(
|
||||||
|
"
|
||||||
|
error: Variable `JUST_TEST_VARIABLE` is unexported multiple times
|
||||||
|
——▶ justfile:6:10
|
||||||
|
│
|
||||||
|
6 │ unexport JUST_TEST_VARIABLE
|
||||||
|
│ ^^^^^^^^^^^^^^^^^^
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn export_unexport_conflict() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
unexport JUST_TEST_VARIABLE
|
||||||
|
|
||||||
|
recipe:
|
||||||
|
echo variable: $JUST_TEST_VARIABLE
|
||||||
|
|
||||||
|
export JUST_TEST_VARIABLE := 'foo'
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.stderr(
|
||||||
|
"
|
||||||
|
error: Variable JUST_TEST_VARIABLE is both exported and unexported
|
||||||
|
——▶ justfile:6:8
|
||||||
|
│
|
||||||
|
6 │ export JUST_TEST_VARIABLE := 'foo'
|
||||||
|
│ ^^^^^^^^^^^^^^^^^^
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.status(1)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn unexport_doesnt_override_local_recipe_export() {
|
||||||
|
Test::new()
|
||||||
|
.justfile(
|
||||||
|
"
|
||||||
|
unexport JUST_TEST_VARIABLE
|
||||||
|
|
||||||
|
recipe $JUST_TEST_VARIABLE:
|
||||||
|
@echo \"variable: $JUST_TEST_VARIABLE\"
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.args(["recipe", "value"])
|
||||||
|
.stdout("variable: value\n")
|
||||||
|
.status(0)
|
||||||
|
.run();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user