Skip to content

Commit

Permalink
fix: add full tera context to tasks
Browse files Browse the repository at this point in the history
Fixes #3701
  • Loading branch information
jdx committed Dec 19, 2024
1 parent 7b57cf4 commit 24881cd
Show file tree
Hide file tree
Showing 11 changed files with 91 additions and 48 deletions.
14 changes: 14 additions & 0 deletions e2e/tasks/test_task_run_tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/usr/bin/env bash

cat <<EOF >mise.toml
env.BAR = "bar"
tasks.a = "echo {{ env.BAR }}"
EOF
assert "mise run a" "bar"

cat <<EOF >mise.toml
env.BAR = "a"
tasks.a = "echo a"
tasks.b.depends = ["{{ env.BAR }}"]
EOF
assert "mise run b" "a"
3 changes: 2 additions & 1 deletion src/cli/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,8 @@ impl Env {
}

fn output_dotenv(&self, config: &Config, ts: Toolset) -> Result<()> {
for (k, v) in ts.final_env(config)?.0 {
let (env, _) = ts.final_env(config)?;
for (k, v) in env {
let k = k.to_string();
let v = v.to_string();
miseprint!("{}={}\n", k, v)?;
Expand Down
2 changes: 1 addition & 1 deletion src/cli/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ impl Exec {
));
}
// TODO: env is being calculated twice with final_env and env_with_path
let env_results = ts.final_env(&config)?.1;
let (_, env_results) = ts.final_env(&config)?;
for p in ts.list_final_paths(&config, env_results)? {
cmd.push(format!(
"fish_add_path -gm {}",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ snapshot_kind: text
Toolset {
versions: {},
source: None,
tera_ctx: OnceCell(Uninit),
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ Toolset {
"/tmp/.mise.toml",
),
),
tera_ctx: OnceCell(Uninit),
}
2 changes: 1 addition & 1 deletion src/config/env_directive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ impl Display for EnvDirective {
}
}

#[derive(Default)]
#[derive(Default, Clone)]
pub struct EnvResults {
pub env: IndexMap<String, (String, PathBuf)>,
pub env_remove: BTreeSet<String>,
Expand Down
33 changes: 14 additions & 19 deletions src/task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,7 @@ impl Task {
spec.cmd.name = self.name.clone();
(spec, vec![])
} else {
let (scripts, spec) =
TaskScriptParser::new(cwd).parse_run_scripts(&self.config_root, self.run())?;
let (scripts, spec) = TaskScriptParser::new(cwd).parse_run_scripts(self, self.run())?;
(spec, scripts)
};
spec.name = self.name.clone();
Expand Down Expand Up @@ -382,8 +381,7 @@ impl Task {
}) {
let config_root = self.config_root.clone().unwrap_or_default();
let mut tera = get_tera(Some(&config_root));
let mut tera_ctx = config.tera_ctx.clone();
tera_ctx.insert("config_root", &config_root);
let tera_ctx = self.tera_ctx()?;
let dir = tera.render_str(&dir, &tera_ctx)?;
let dir = file::replace_path(&dir);
if dir.is_absolute() {
Expand All @@ -398,6 +396,14 @@ impl Task {
}
}

pub fn tera_ctx(&self) -> Result<tera::Context> {
let config = Config::get();
let ts = config.get_toolset()?;
let mut tera_ctx = ts.tera_ctx()?.clone();
tera_ctx.insert("config_root", &self.config_root);
Ok(tera_ctx)
}

pub fn cf<'a>(&self, config: &'a Config) -> Option<&'a Box<dyn ConfigFile>> {
config.config_files.get(&self.config_source)
}
Expand All @@ -418,10 +424,8 @@ impl Task {
}

pub fn render(&mut self, config_root: &Path) -> Result<()> {
let config = Config::get();
let mut tera = get_tera(Some(config_root));
let mut tera_ctx = config.tera_ctx.clone();
tera_ctx.insert("config_root", &config_root);
let tera_ctx = self.tera_ctx()?;
for a in &mut self.aliases {
*a = tera.render_str(a, &tera_ctx)?;
}
Expand All @@ -431,22 +435,13 @@ impl Task {
}
self.outputs.render(&mut tera, &tera_ctx)?;
for d in &mut self.depends {
d.task = tera.render_str(&d.task, &tera_ctx)?;
for a in &mut d.args {
*a = tera.render_str(a, &tera_ctx)?;
}
d.render(&mut tera, &tera_ctx)?;
}
for d in &mut self.depends_post {
d.task = tera.render_str(&d.task, &tera_ctx)?;
for a in &mut d.args {
*a = tera.render_str(a, &tera_ctx)?;
}
d.render(&mut tera, &tera_ctx)?;
}
for d in &mut self.wait_for {
d.task = tera.render_str(&d.task, &tera_ctx)?;
for a in &mut d.args {
*a = tera.render_str(a, &tera_ctx)?;
}
d.render(&mut tera, &tera_ctx)?;
}
for v in self.env.values_mut() {
if let EitherStringOrIntOrBool(Either::Left(s)) = v {
Expand Down
38 changes: 28 additions & 10 deletions src/task/task_dep.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use crate::config::config_file::toml::deserialize_arr;
use itertools::Itertools;
use serde::ser::SerializeSeq;
use serde::{Deserialize, Deserializer, Serialize};
use std::fmt;
Expand All @@ -12,6 +11,28 @@ pub struct TaskDep {
pub args: Vec<String>,
}

impl TaskDep {
pub fn render(
&mut self,
tera: &mut tera::Tera,
tera_ctx: &tera::Context,
) -> crate::Result<&mut Self> {
self.task = tera.render_str(&self.task, tera_ctx)?;
for a in &mut self.args {
*a = tera.render_str(a, tera_ctx)?;
}
if self.args.is_empty() {
let s = self.task.clone();
let mut split = s.split_whitespace().map(|s| s.to_string());
if let Some(task) = split.next() {
self.task = task;
}
self.args = split.collect();
}
Ok(self)
}
}

impl Display for TaskDep {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.task)?;
Expand All @@ -32,13 +53,9 @@ impl FromStr for TaskDep {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let parts = s.split_whitespace().collect_vec();
if parts.is_empty() {
return Err("Task name is required".to_string());
}
Ok(Self {
task: parts[0].to_string(),
args: parts[1..].iter().map(|s| s.to_string()).collect(),
task: s.to_string(),
args: Default::default(),
})
}
}
Expand Down Expand Up @@ -85,9 +102,10 @@ mod tests {
assert_eq!(td.task, "task");
assert!(td.args.is_empty());

let td: TaskDep = "task arg1 arg2".parse().unwrap();
assert_eq!(td.task, "task");
assert_eq!(td.args, vec!["arg1", "arg2"]);
// TODO: td.render()
// let td: TaskDep = "task arg1 arg2".parse().unwrap();
// assert_eq!(td.task, "task");
// assert_eq!(td.args, vec!["arg1", "arg2"]);
}

#[test]
Expand Down
18 changes: 8 additions & 10 deletions src/task/task_script_parser.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::config::{Config, SETTINGS};
use crate::config::SETTINGS;
use crate::exit::exit;
use crate::shell::ShellType;
use crate::task::Task;
Expand Down Expand Up @@ -27,7 +27,7 @@ impl TaskScriptParser {

pub fn parse_run_scripts(
&self,
config_root: &Option<PathBuf>,
task: &Task,
scripts: &[String],
) -> Result<(Vec<String>, usage::Spec)> {
let mut tera = self.get_tera();
Expand Down Expand Up @@ -272,9 +272,7 @@ impl TaskScriptParser {
}
}
});
let config = Config::get();
let mut ctx = config.tera_ctx.clone();
ctx.insert("config_root", config_root);
let ctx = task.tera_ctx()?;
let scripts = scripts
.iter()
.map(|s| tera.render_str(s.trim(), &ctx).unwrap())
Expand Down Expand Up @@ -384,7 +382,7 @@ mod tests {
let task = Task::default();
let parser = TaskScriptParser::new(None);
let scripts = vec!["echo {{ arg(i=0, name='foo') }}".to_string()];
let (scripts, spec) = parser.parse_run_scripts(&None, &scripts).unwrap();
let (scripts, spec) = parser.parse_run_scripts(&task, &scripts).unwrap();
assert_eq!(scripts, vec!["echo MISE_TASK_ARG:foo:MISE_TASK_ARG"]);
let arg0 = spec.cmd.args.first().unwrap();
assert_eq!(arg0.name, "foo");
Expand All @@ -403,7 +401,7 @@ mod tests {
"echo {{ arg(name='foo') }}; echo {{ arg(name='bar') }}; echo {{ arg(name='foo') }}"
.to_string(),
];
let (scripts, spec) = parser.parse_run_scripts(&None, &scripts).unwrap();
let (scripts, spec) = parser.parse_run_scripts(&task, &scripts).unwrap();
assert_eq!(scripts, vec!["echo MISE_TASK_ARG:foo:MISE_TASK_ARG; echo MISE_TASK_ARG:bar:MISE_TASK_ARG; echo MISE_TASK_ARG:foo:MISE_TASK_ARG"]);
let arg0 = spec.cmd.args.first().unwrap();
let arg1 = spec.cmd.args.get(1).unwrap();
Expand All @@ -426,7 +424,7 @@ mod tests {
let task = Task::default();
let parser = TaskScriptParser::new(None);
let scripts = vec!["echo {{ arg(var=true) }}".to_string()];
let (scripts, spec) = parser.parse_run_scripts(&None, &scripts).unwrap();
let (scripts, spec) = parser.parse_run_scripts(&task, &scripts).unwrap();
assert_eq!(scripts, vec!["echo MISE_TASK_ARG:0:MISE_TASK_ARG"]);
let arg0 = spec.cmd.args.first().unwrap();
assert_eq!(arg0.name, "0");
Expand All @@ -446,7 +444,7 @@ mod tests {
let task = Task::default();
let parser = TaskScriptParser::new(None);
let scripts = vec!["echo {{ flag(name='foo') }}".to_string()];
let (scripts, spec) = parser.parse_run_scripts(&None, &scripts).unwrap();
let (scripts, spec) = parser.parse_run_scripts(&task, &scripts).unwrap();
assert_eq!(scripts, vec!["echo MISE_TASK_ARG:foo:MISE_TASK_ARG"]);
let flag = spec.cmd.flags.iter().find(|f| &f.name == "foo").unwrap();
assert_eq!(&flag.name, "foo");
Expand All @@ -462,7 +460,7 @@ mod tests {
let task = Task::default();
let parser = TaskScriptParser::new(None);
let scripts = vec!["echo {{ option(name='foo') }}".to_string()];
let (scripts, spec) = parser.parse_run_scripts(&None, &scripts).unwrap();
let (scripts, spec) = parser.parse_run_scripts(&task, &scripts).unwrap();
assert_eq!(scripts, vec!["echo MISE_TASK_ARG:foo:MISE_TASK_ARG"]);
let option = spec.cmd.flags.iter().find(|f| &f.name == "foo").unwrap();
assert_eq!(&option.name, "foo");
Expand Down
15 changes: 13 additions & 2 deletions src/toolset/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ use console::truncate_str;
use eyre::{eyre, Result, WrapErr};
use indexmap::{IndexMap, IndexSet};
use itertools::Itertools;
use once_cell::sync::OnceCell;
pub use outdated_info::is_outdated_version;
use outdated_info::OutdatedInfo;
use rayon::prelude::*;
Expand Down Expand Up @@ -108,6 +109,7 @@ impl Default for InstallOptions {
pub struct Toolset {
pub versions: IndexMap<BackendArg, ToolVersionList>,
pub source: Option<ToolSource>,
tera_ctx: OnceCell<tera::Context>,
}

impl Toolset {
Expand Down Expand Up @@ -463,15 +465,15 @@ impl Toolset {
/// returns env_with_path but also with the existing env vars from the system
pub fn full_env(&self) -> Result<EnvMap> {
let mut env = env::PRISTINE_ENV.clone().into_iter().collect::<EnvMap>();
env.extend(self.env_with_path(&Config::get())?);
env.extend(self.env_with_path(&Config::get())?.clone());
Ok(env)
}
/// the full mise environment including all tool paths
pub fn env_with_path(&self, config: &Config) -> Result<EnvMap> {
let (mut env, env_results) = self.final_env(config)?;
let mut path_env = PathEnv::from_iter(env::PATH.clone());
for p in self.list_final_paths(config, env_results)? {
path_env.add(p);
path_env.add(p.clone());
}
env.insert(PATH_KEY.to_string(), path_env.to_string());
Ok(env)
Expand Down Expand Up @@ -585,6 +587,15 @@ impl Toolset {
let paths = env_results.env_paths.into_iter().chain(paths).collect();
Ok(paths)
}
pub fn tera_ctx(&self) -> Result<&tera::Context> {
self.tera_ctx.get_or_try_init(|| {
let config = Config::get();
let env = self.env_with_path(&config)?;
let mut ctx = config.tera_ctx.clone();
ctx.insert("env", &env);
Ok(ctx)
})
}
pub fn which(&self, bin_name: &str) -> Option<(Arc<dyn Backend>, ToolVersion)> {
self.list_current_installed_versions()
.into_par_iter()
Expand Down
12 changes: 8 additions & 4 deletions src/toolset/tool_request_set.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use std::collections::{BTreeMap, BTreeSet, HashSet};
use std::fmt::{Debug, Display};

use indexmap::IndexMap;
use itertools::Itertools;

use crate::backend::backend_type::BackendType;
use crate::cli::args::{BackendArg, ToolArg};
use crate::config::{Config, Settings};
use crate::env;
use crate::registry::REGISTRY;
use crate::toolset::{ToolRequest, ToolSource, Toolset};
use indexmap::IndexMap;
use itertools::Itertools;

#[derive(Debug, Default, Clone)]
pub struct ToolRequestSet {
Expand Down Expand Up @@ -168,7 +168,11 @@ impl ToolRequestSetBuilder {
}

fn is_disabled(&self, ba: &BackendArg) -> bool {
!ba.is_os_supported() || self.disable_tools.contains(ba)
let backend_type = ba.backend_type();
backend_type == BackendType::Unknown
|| (cfg!(windows) && backend_type == BackendType::Asdf)
|| !ba.is_os_supported()
|| self.disable_tools.contains(ba)
}

fn load_config_files(&self, trs: &mut ToolRequestSet) -> eyre::Result<()> {
Expand Down

0 comments on commit 24881cd

Please sign in to comment.