fix: bug fixes with tools=true in env (#3639)

Fixes #3631
Fixes #3632
This commit is contained in:
jdx 2024-12-17 07:35:04 -06:00 committed by GitHub
parent b0fa5e91cd
commit 1aa367089b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 68 additions and 41 deletions

13
e2e/env/test_env_tools vendored Normal file
View File

@ -0,0 +1,13 @@
#!/usr/bin/env bash
cat <<EOF >mise.toml
[env]
FOO={value="bar", tools=true}
EOF
assert_contains "mise hook-env -s bash" "export FOO=bar"
cat <<EOF >mise.toml
[env]
WHICH={value="{{ exec(command='which which') }}", tools=true}
EOF
assert_contains "mise hook-env -s bash" "export WHICH=$(which which)"

View File

@ -35,7 +35,8 @@ impl Envrc {
for cf in config.config_files.keys() {
writeln!(file, "watch_file {}", cf.to_string_lossy())?;
}
for (k, v) in ts.env(config)? {
let (env, env_results) = ts.final_env(config)?;
for (k, v) in env {
if k == *PATH_KEY {
writeln!(file, "PATH_add {}", v)?;
} else {
@ -47,7 +48,7 @@ impl Envrc {
)?;
}
}
for path in ts.list_paths().into_iter().rev() {
for path in ts.list_final_paths(config, env_results)?.into_iter().rev() {
writeln!(file, "PATH_add {}", path.to_string_lossy())?;
}

View File

@ -20,7 +20,8 @@ impl Path {
let path = env.get("PATH").cloned().unwrap_or_default();
env::split_paths(&path).collect()
} else {
ts.list_final_paths()?
let (_env, env_results) = ts.final_env(&config)?;
ts.list_final_paths(&config, env_results)?
};
for path in paths {
println!("{}", path.display());

View File

@ -125,7 +125,7 @@ impl Env {
}
fn output_dotenv(&self, config: &Config, ts: Toolset) -> Result<()> {
for (k, v) in ts.env(config)? {
for (k, v) in ts.final_env(config)?.0 {
let k = k.to_string();
let v = v.to_string();
miseprint!("{}={}\n", k, v)?;

View File

@ -53,11 +53,12 @@ pub struct Exec {
impl Exec {
pub fn run(self) -> Result<()> {
let config = Config::get();
let mut ts = measure!("toolset", {
ToolsetBuilder::new()
.with_args(&self.tool)
.with_default_to_latest(true)
.build(&Config::get())?
.build(&config)?
});
let opts = InstallOptions {
force: false,
@ -81,7 +82,7 @@ impl Exec {
});
let (program, mut args) = parse_command(&env::SHELL, &self.command, &self.c);
let env = measure!("env_with_path", { ts.env_with_path(&Config::get())? });
let env = measure!("env_with_path", { ts.env_with_path(&config)? });
if program.rsplit('/').next() == Some("fish") {
let mut cmd = vec![];
@ -92,7 +93,9 @@ impl Exec {
shell_escape::escape(v.into())
));
}
for p in ts.list_final_paths()? {
// TODO: env is being calculated twice with final_env and env_with_path
let env_results = ts.final_env(&config)?.1;
for p in ts.list_final_paths(&config, env_results)? {
cmd.push(format!(
"fish_add_path -gm {}",
shell_escape::escape(p.to_string_lossy())

View File

@ -50,13 +50,13 @@ impl HookEnv {
let ts = config.get_toolset()?;
let shell = get_shell(self.shell).expect("no shell provided, use `--shell=zsh`");
miseprint!("{}", hook_env::clear_old_env(&*shell))?;
let mut mise_env = ts.env(&config)?;
let (mut mise_env, env_results) = ts.final_env(&config)?;
mise_env.remove(&*PATH_KEY);
self.display_status(&config, ts, &mise_env)?;
let mut diff = EnvDiff::new(&env::PRISTINE_ENV, mise_env.clone());
let mut patches = diff.to_patches();
let paths = ts.list_final_paths()?;
let paths = ts.list_final_paths(&config, env_results)?;
diff.path.clone_from(&paths); // update __MISE_DIFF with the new paths for the next run
patches.extend(self.build_path_operations(&paths, &__MISE_DIFF.path)?);

View File

@ -468,20 +468,12 @@ impl Toolset {
}
/// the full mise environment including all tool paths
pub fn env_with_path(&self, config: &Config) -> Result<EnvMap> {
let mut env = self.env(config)?;
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()? {
for p in self.list_final_paths(config, env_results)? {
path_env.add(p);
}
env.insert(PATH_KEY.to_string(), path_env.to_string());
let mut ctx = config.tera_ctx.clone();
ctx.insert("env", &env);
env.extend(
self.load_post_env(ctx, &env)?
.env
.into_iter()
.map(|(k, v)| (k, v.0)),
);
Ok(env)
}
pub fn env_from_tools(&self, config: &Config) -> Vec<(String, String, String)> {
@ -501,7 +493,7 @@ impl Toolset {
.filter(|(_, k, _)| k.to_uppercase() != "PATH")
.collect::<Vec<(String, String, String)>>()
}
pub fn env(&self, config: &Config) -> Result<EnvMap> {
fn env(&self, config: &Config) -> Result<EnvMap> {
time!("env start");
let entries = self
.env_from_tools(config)
@ -513,7 +505,7 @@ impl Toolset {
.filter(|(k, _)| k == "MISE_ADD_PATH" || k == "RTX_ADD_PATH")
.map(|(_, v)| v)
.join(":");
let mut entries: EnvMap = entries
let mut env: EnvMap = entries
.into_iter()
.filter(|(k, _)| k != "RTX_ADD_PATH")
.filter(|(k, _)| k != "MISE_ADD_PATH")
@ -522,16 +514,35 @@ impl Toolset {
.rev()
.collect();
if !add_paths.is_empty() {
entries.insert(PATH_KEY.to_string(), add_paths);
env.insert(PATH_KEY.to_string(), add_paths);
}
entries.extend(config.env()?.clone());
env.extend(config.env()?.clone());
if let Some(venv) = &*UV_VENV {
for (k, v) in &venv.env {
entries.insert(k.clone(), v.clone());
env.insert(k.clone(), v.clone());
}
}
time!("env end");
Ok(entries)
Ok(env)
}
pub fn final_env(&self, config: &Config) -> Result<(EnvMap, EnvResults)> {
let mut env = self.env(config)?;
let mut tera_env = env.clone();
let mut path_env = PathEnv::from_iter(env::PATH.clone());
for p in self.list_paths().into_iter() {
path_env.add(p);
}
tera_env.insert(PATH_KEY.to_string(), path_env.to_string());
let mut ctx = config.tera_ctx.clone();
ctx.insert("env", &tera_env);
let env_results = self.load_post_env(config, ctx, &tera_env)?;
env.extend(
env_results
.env
.iter()
.map(|(k, v)| (k.clone(), v.0.clone())),
);
Ok((env, env_results))
}
pub fn list_paths(&self) -> Vec<PathBuf> {
self.list_current_installed_versions()
@ -547,36 +558,30 @@ impl Toolset {
.collect()
}
/// same as list_paths but includes config.list_paths, venv paths, and MISE_ADD_PATHs from self.env()
pub fn list_final_paths(&self) -> Result<Vec<PathBuf>> {
pub fn list_final_paths(
&self,
config: &Config,
env_results: EnvResults,
) -> Result<Vec<PathBuf>> {
let mut paths = IndexSet::new();
for p in Config::get().path_dirs()?.clone() {
for p in config.path_dirs()?.clone() {
paths.insert(p);
}
if let Some(venv) = &*UV_VENV {
paths.insert(venv.venv_path.clone());
}
if let Some(path) = self.env(&Config::get())?.get(&*PATH_KEY) {
if let Some(path) = self.env(config)?.get(&*PATH_KEY) {
paths.insert(PathBuf::from(path));
}
for p in self.list_paths() {
paths.insert(p);
}
let config = Config::get();
let mut env = self.env(&config)?;
let mut path_env = PathEnv::from_iter(env::PATH.clone());
for p in paths.clone().into_iter() {
path_env.add(p);
}
env.insert(PATH_KEY.to_string(), path_env.to_string());
let mut ctx = config.tera_ctx.clone();
ctx.insert("env", &env);
// these are returned in order, but we need to run the post_env stuff last and then put the results in the front
let paths = self
.load_post_env(ctx, &env)?
.env_paths
.into_iter()
.chain(paths)
.collect();
let paths = env_results.env_paths.into_iter().chain(paths).collect();
Ok(paths)
}
pub fn which(&self, bin_name: &str) -> Option<(Arc<dyn Backend>, ToolVersion)> {
@ -678,8 +683,12 @@ impl Toolset {
!ba.is_os_supported() || SETTINGS.disable_tools().contains(&ba.short)
}
fn load_post_env(&self, ctx: tera::Context, env: &EnvMap) -> Result<EnvResults> {
let config = Config::get();
fn load_post_env(
&self,
config: &Config,
ctx: tera::Context,
env: &EnvMap,
) -> Result<EnvResults> {
let entries = config
.config_files
.iter()