From df533658ba795c68248a4bb9dbcb6de2fc11522d Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Sat, 28 Feb 2026 11:21:06 +0000 Subject: [PATCH 1/2] fix(hooks): render tera templates and fix output masking Hooks using tera template variables like `{{tools.ripgrep.path}}` were passed directly to the shell without rendering, causing silent failures. Add tera template rendering to hook execution, matching the behavior that tasks already have. Also move `mpr.footer_finish()` before postinstall hook execution so progress bars are cleared before hook output is printed. Closes #8366 Co-Authored-By: Claude Opus 4.6 --- src/hooks.rs | 7 ++++++- src/toolset/toolset_install.rs | 10 +++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/hooks.rs b/src/hooks.rs index 54462e257d..325de558f3 100644 --- a/src/hooks.rs +++ b/src/hooks.rs @@ -1,6 +1,7 @@ use crate::cmd::cmd; use crate::config::{Config, Settings, config_file}; use crate::shell::Shell; +use crate::tera::get_tera; use crate::toolset::{ToolVersion, Toolset}; use crate::{dirs, hook_env}; use eyre::Result; @@ -255,11 +256,15 @@ async fn execute( Settings::get().ensure_experimental("hooks")?; let shell = Settings::get().default_inline_shell()?; + let tera_ctx = ts.tera_ctx(config).await?; + let mut tera = get_tera(Some(root)); + let rendered_script = tera.render_str(&hook.script, tera_ctx)?; + let args = shell .iter() .skip(1) .map(|s| s.as_str()) - .chain(once(hook.script.as_str())) + .chain(once(rendered_script.as_str())) .collect_vec(); // Preinstall hooks skip `tools=true` env directives since the tools // providing those env vars aren't installed yet (fixes #6162) diff --git a/src/toolset/toolset_install.rs b/src/toolset/toolset_install.rs index 0240a8db65..10ec295b84 100644 --- a/src/toolset/toolset_install.rs +++ b/src/toolset/toolset_install.rs @@ -190,6 +190,11 @@ impl Toolset { } } + // Finish the global footer before running hooks so output isn't masked + if !opts.dry_run { + mpr.footer_finish(); + } + // Skip hooks in dry-run mode if !opts.dry_run { // Run post-install hook with installed tools info @@ -214,11 +219,6 @@ impl Toolset { .await; } - // Finish the global footer - if !opts.dry_run { - mpr.footer_finish(); - } - // Return appropriate result if all_failed.is_empty() { Ok(installed) From 6075b67f3e9f9f06f7f343f0da276727b34268c2 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Sat, 28 Feb 2026 11:31:06 +0000 Subject: [PATCH 2/2] fix(hooks): skip tools in tera context for preinstall hooks Preinstall hooks use full_env_without_tools because tools aren't installed yet. The tera context must also skip the tools map and use the same env to stay consistent. Co-Authored-By: Claude Opus 4.6 --- src/hooks.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/hooks.rs b/src/hooks.rs index 325de558f3..46e2f25731 100644 --- a/src/hooks.rs +++ b/src/hooks.rs @@ -256,9 +256,20 @@ async fn execute( Settings::get().ensure_experimental("hooks")?; let shell = Settings::get().default_inline_shell()?; - let tera_ctx = ts.tera_ctx(config).await?; + // Preinstall hooks skip `tools=true` env directives since the tools + // providing those env vars aren't installed yet (fixes #6162) + let (tera_ctx, mut env) = if hook.hook == Hooks::Preinstall { + let env = ts.full_env_without_tools(config).await?; + let mut ctx = config.tera_ctx.clone(); + ctx.insert("env", &env); + (ctx, env) + } else { + let ctx = ts.tera_ctx(config).await?.clone(); + let env = ts.full_env(config).await?; + (ctx, env) + }; let mut tera = get_tera(Some(root)); - let rendered_script = tera.render_str(&hook.script, tera_ctx)?; + let rendered_script = tera.render_str(&hook.script, &tera_ctx)?; let args = shell .iter() @@ -266,13 +277,6 @@ async fn execute( .map(|s| s.as_str()) .chain(once(rendered_script.as_str())) .collect_vec(); - // Preinstall hooks skip `tools=true` env directives since the tools - // providing those env vars aren't installed yet (fixes #6162) - let mut env = if hook.hook == Hooks::Preinstall { - ts.full_env_without_tools(config).await? - } else { - ts.full_env(config).await? - }; if let Some(cwd) = dirs::CWD.as_ref() { env.insert( "MISE_ORIGINAL_CWD".to_string(),