Skip to content
Merged
2 changes: 1 addition & 1 deletion docs/environments/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ mise en

## Environment in tasks

Its also possible to define environment inside a task
It is also possible to define environment inside a task

```toml [mise.toml]
[tasks.print]
Expand Down
15 changes: 15 additions & 0 deletions e2e/tasks/test_task_env_propagation
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash

cat <<EOF >mise.toml
[tasks.echo]
env = { "SUB_TASK" = "sub_task_env" }
run = 'echo "\$SUB_TASK \$MY_VAR"'


[tasks.propagation]
run = [{ task = "echo" }]
env = { "MY_VAR" = "my_variable" }
EOF

assert_contains "mise run echo" "sub_task_env " # with trailing space
assert_contains "mise run propagation" "sub_task_env my_variable"
27 changes: 20 additions & 7 deletions src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::time::{Duration, SystemTime};
use super::args::ToolArg;
use crate::cli::Cli;
use crate::cmd::CmdLineRunner;
use crate::config::{Config, Settings};
use crate::config::{Config, Settings, env_directive::EnvDirective};
use crate::env_diff::EnvMap;
use crate::file::display_path;
use crate::task::task_file_providers::TaskFileProvidersBuilder;
Expand Down Expand Up @@ -687,7 +687,7 @@ impl Run {
ts_build_start.elapsed().as_millis()
);
let env_render_start = std::time::Instant::now();
let mut env = task.render_env(config, &ts).await?;
let (mut env, task_env) = task.render_env(config, &ts).await?;
trace!(
"task {} render_env took {}ms",
task.name,
Expand Down Expand Up @@ -740,8 +740,15 @@ impl Run {
.await?;

let exec_start = std::time::Instant::now();
self.exec_task_run_entries(config, task, &env, &prefix, rendered_run_scripts, sched_tx)
.await?;
self.exec_task_run_entries(
config,
task,
(&env, &task_env),
&prefix,
rendered_run_scripts,
sched_tx,
)
.await?;
trace!(
"task {} exec_task_run_entries took {}ms (total {}ms)",
task.name,
Expand Down Expand Up @@ -769,11 +776,12 @@ impl Run {
&self,
config: &Arc<Config>,
task: &Task,
env: &BTreeMap<String, String>,
full_env: (&BTreeMap<String, String>, &[(String, String)]),
prefix: &str,
rendered_scripts: Vec<(String, Vec<String>)>,
sched_tx: Arc<mpsc::UnboundedSender<(Task, Arc<Mutex<Deps>>)>>,
) -> Result<()> {
let (env, task_env) = full_env;
use crate::task::RunEntry;
let mut script_iter = rendered_scripts.into_iter();
for entry in task.run() {
Expand All @@ -784,11 +792,11 @@ impl Run {
}
}
RunEntry::SingleTask { task: spec } => {
self.inject_and_wait(config, &[spec.to_string()], sched_tx.clone())
self.inject_and_wait(config, &[spec.to_string()], task_env, sched_tx.clone())
.await?;
}
RunEntry::TaskGroup { tasks } => {
self.inject_and_wait(config, tasks, sched_tx.clone())
self.inject_and_wait(config, tasks, task_env, sched_tx.clone())
.await?;
}
}
Expand All @@ -800,6 +808,7 @@ impl Run {
&self,
config: &Arc<Config>,
specs: &[String],
task_env: &[(String, String)],
sched_tx: Arc<mpsc::UnboundedSender<(Task, Arc<Mutex<Deps>>)>>,
) -> Result<()> {
trace!("inject start: {}", specs.join(", "));
Expand All @@ -821,6 +830,8 @@ impl Run {

// Pump subgraph into scheduler and signal completion via oneshot when done
let (done_tx, done_rx) = oneshot::channel::<()>();
let task_env_directives: Vec<EnvDirective> =
task_env.iter().cloned().map(Into::into).collect();
{
let sub_deps_clone = sub_deps.clone();
let sched_tx = sched_tx.clone();
Expand All @@ -832,6 +843,7 @@ impl Run {
match rx.try_recv() {
Ok(Some(task)) => {
any = true;
let task = task.derive_env(&task_env_directives);
trace!("inject initial leaf: {} {}", task.name, task.args.join(" "));
let _ = sched_tx.send((task, sub_deps_clone.clone()));
}
Expand Down Expand Up @@ -862,6 +874,7 @@ impl Run {
task.name,
task.args.join(" ")
);
let task = task.derive_env(&task_env_directives);
let _ = sched_tx.send((task, sub_deps_clone.clone()));
}
None => {
Expand Down
20 changes: 15 additions & 5 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::cli::version::VERSION;
use crate::config::config_file::mise_toml::EnvList;
use crate::config::config_file::toml::{TomlParser, deserialize_arr};
use crate::config::env_directive::{EnvResolveOptions, EnvResults, ToolsFilter};
use crate::config::env_directive::{EnvDirective, EnvResolveOptions, EnvResults, ToolsFilter};
use crate::config::{self, Config};
use crate::path_env::PathEnv;
use crate::task::task_script_parser::{TaskScriptParser, has_any_args_defined};
Expand Down Expand Up @@ -232,6 +232,12 @@ impl Task {
Ok(task)
}

pub fn derive_env(&self, env_directives: &[EnvDirective]) -> Self {
let mut new_task = self.clone();
new_task.env.0.extend_from_slice(env_directives);
new_task
}

/// prints the task name without an extension
pub fn display_name(&self, all_tasks: &BTreeMap<String, Task>) -> String {
let display_name = self
Expand Down Expand Up @@ -563,7 +569,11 @@ impl Task {
self.name.replace(':', path::MAIN_SEPARATOR_STR).into()
}

pub async fn render_env(&self, config: &Arc<Config>, ts: &Toolset) -> Result<EnvMap> {
pub async fn render_env(
&self,
config: &Arc<Config>,
ts: &Toolset,
) -> Result<(EnvMap, Vec<(String, String)>)> {
let mut tera_ctx = ts.tera_ctx(config).await?.clone();
let mut env = ts.full_env(config).await?;
if let Some(root) = &config.project_root {
Expand Down Expand Up @@ -591,9 +601,9 @@ impl Task {
},
)
.await?;

let task_env = env_results.env.into_iter().map(|(k, (v, _))| (k, v));
// Apply the resolved environment variables
env.extend(env_results.env.into_iter().map(|(k, (v, _))| (k, v)));
env.extend(task_env.clone());

// Remove environment variables that were explicitly unset
for key in &env_results.env_remove {
Expand All @@ -611,7 +621,7 @@ impl Task {
env.insert(env::PATH_KEY.to_string(), path_env.to_string());
}

Ok(env)
Ok((env, task_env.collect()))
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: Iterator Exhaustion Causes Empty Environment Variables

The task_env iterator is consumed by env.extend(task_env.clone()). This exhausts the iterator, causing the subsequent task_env.collect() call to return an empty vector. As a result, environment variables are not correctly propagated to child tasks.

Fix in Cursor Fix in Web

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure on this one

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there is no bug my dear bot

$ ./target/debug/mise run propagation
Map { iter: [("MY_VAR", ("my_variable", "e2e/tasks/mise.toml"))] }
Map { iter: [("MY_VAR", ("my_variable", "e2e/tasks/mise.toml"))] }
Map { iter: [("SUB_TASK", ("sub_task_env", "e2e/tasks/mise.toml")), ("MY_VAR", ("my_variable", "e2e/tasks/mise.toml"))] }
Map { iter: [("SUB_TASK", ("sub_task_env", "e2e/tasks/mise.toml")), ("MY_VAR", ("my_variable", "e2e/tasks/mise.toml"))] }
[echo] $ echo "$SUB_TASK $MY_VAR"
sub_task_env my_variable

}
}

Expand Down
Loading