Skip to content

Commit

Permalink
refactor: env parsing (#1515)
Browse files Browse the repository at this point in the history
This lazy-loads the env vars from config files.
This is a prereq for #1262 and other env var changes. It will make it easier to perform the template logic in order across the env vars rather than when the file is parsed
  • Loading branch information
jdx authored Jan 24, 2024
1 parent 032e325 commit a5573cc
Show file tree
Hide file tree
Showing 17 changed files with 68 additions and 63 deletions.
2 changes: 1 addition & 1 deletion src/cli/direnv/envrc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl Envrc {
for cf in config.config_files.keys() {
writeln!(file, "watch_file {}", cf.to_string_lossy())?;
}
for (k, v) in ts.env(config) {
for (k, v) in ts.env(config)? {
if k == "PATH" {
writeln!(file, "PATH_add {}", v)?;
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/cli/direnv/exec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ impl DirenvExec {

let mut cmd = env_cmd();

for (k, v) in ts.env_with_path(config) {
for (k, v) in ts.env_with_path(config)? {
cmd = cmd.env(k, v);
}

Expand Down
4 changes: 2 additions & 2 deletions src/cli/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ impl Env {
}

fn output_json(&self, config: &Config, ts: Toolset) -> Result<()> {
let env = ts.env_with_path(config);
let env = ts.env_with_path(config)?;
miseprintln!("{}", serde_json::to_string_pretty(&env)?);
Ok(())
}

fn output_shell(&self, config: &Config, ts: Toolset) -> Result<()> {
let default_shell = get_shell(Some(ShellType::Bash)).unwrap();
let shell = get_shell(self.shell).unwrap_or(default_shell);
for (k, v) in ts.env_with_path(config) {
for (k, v) in ts.env_with_path(config)? {
let k = k.to_string();
let v = v.to_string();
miseprint!("{}", shell.set_env(&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 @@ -62,7 +62,7 @@ impl Exec {
ts.notify_if_versions_missing();

let (program, args) = parse_command(&env::SHELL, &self.command, &self.c);
let env = ts.env_with_path(&config);
let env = ts.env_with_path(&config)?;

self.exec(program, args, env)
}
Expand Down
9 changes: 5 additions & 4 deletions src/cli/hook_env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl HookEnv {
let ts = ToolsetBuilder::new().build(&config)?;
let shell = get_shell(self.shell).expect("no shell provided, use `--shell=zsh`");
miseprint!("{}", hook_env::clear_old_env(&*shell));
let mut env = ts.env(&config);
let mut env = ts.env(&config)?;
let env_path = env.remove("PATH");
let mut diff = EnvDiff::new(&env::PRISTINE_ENV, env);
let mut patches = diff.to_patches();
Expand All @@ -66,14 +66,14 @@ impl HookEnv {
let output = hook_env::build_env_commands(&*shell, &patches);
miseprint!("{output}");
if self.status {
self.display_status(&config, &ts);
self.display_status(&config, &ts)?;
ts.notify_if_versions_missing();
}

Ok(())
}

fn display_status(&self, config: &Config, ts: &Toolset) {
fn display_status(&self, config: &Config, ts: &Toolset) -> Result<()> {
let installed_versions = ts
.list_current_installed_versions()
.into_iter()
Expand All @@ -84,11 +84,12 @@ impl HookEnv {
let status = installed_versions.into_iter().rev().join(" ");
info!("{}", truncate_str(&status, TERM_WIDTH.max(60) - 5, "…"));
}
let env_diff = EnvDiff::new(&env::PRISTINE_ENV, config.env.clone()).to_patches();
let env_diff = EnvDiff::new(&env::PRISTINE_ENV, config.env()?.clone()).to_patches();
if !env_diff.is_empty() {
let env_diff = env_diff.into_iter().map(patch_to_status).join(" ");
info!("{}", truncate_str(&env_diff, TERM_WIDTH.max(60) - 5, "…"));
}
Ok(())
}

/// modifies the PATH and optionally DIRENV_DIFF env var if it exists
Expand Down
2 changes: 1 addition & 1 deletion src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ impl Run {

ts.install_arg_versions(config, &InstallOptions::new())?;
ts.notify_if_versions_missing();
let mut env = ts.env_with_path(config);
let mut env = ts.env_with_path(config)?;
if let Some(root) = &config.project_root {
env.insert("MISE_PROJECT_ROOT".into(), root.display().to_string());
}
Expand Down
8 changes: 4 additions & 4 deletions src/cli/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ impl Set {
let config = Config::try_get()?;
if self.remove.is_none() && self.env_vars.is_none() {
let rows = config
.env
.env_with_sources()?
.iter()
.map(|(key, value)| Row {
.map(|(key, (value, source))| Row {
key: key.clone(),
value: value.clone(),
source: display_path(&config.env_sources.get(key).unwrap().clone()),
source: display_path(source),
})
.collect::<Vec<_>>();
let mut table = tabled::Table::new(rows);
Expand All @@ -75,7 +75,7 @@ impl Set {
if let Some(env_vars) = self.env_vars {
if env_vars.len() == 1 && env_vars[0].value.is_none() {
let key = &env_vars[0].key;
match config.env.get(key) {
match config.env()?.get(key) {
Some(value) => miseprintln!("{value}"),
None => bail!("Environment variable {key} not found"),
}
Expand Down
2 changes: 1 addition & 1 deletion src/cli/watch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ impl Watch {
}
info!("$ watchexec {}", args.join(" "));
let mut cmd = cmd::cmd("watchexec", &args);
for (k, v) in ts.env_with_path(&config) {
for (k, v) in ts.env_with_path(&config)? {
cmd = cmd.env(k, v);
}
if let Some(root) = &config.project_root {
Expand Down
46 changes: 25 additions & 21 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,15 @@ mod tracking;

type AliasMap = BTreeMap<ForgeArg, BTreeMap<String, String>>;
type ConfigMap = IndexMap<PathBuf, Box<dyn ConfigFile>>;
type EnvWithSources = IndexMap<String, (String, PathBuf)>;

#[derive(Default)]
pub struct Config {
pub aliases: AliasMap,
pub config_files: ConfigMap,
pub env: BTreeMap<String, String>,
pub env_sources: HashMap<String, PathBuf>,
pub path_dirs: Vec<PathBuf>,
pub project_root: Option<PathBuf>,
env: OnceCell<EnvWithSources>,
all_aliases: OnceCell<AliasMap>,
repo_urls: HashMap<String, String>,
shorthands: OnceCell<HashMap<String, String>>,
Expand Down Expand Up @@ -74,12 +74,10 @@ impl Config {
let config_paths = load_config_paths(&config_filenames);
let config_files = load_all_config_files(&config_paths, &legacy_files)?;

let (env, env_sources) = load_env(&settings, &config_files);
let repo_urls = config_files.values().flat_map(|cf| cf.plugins()).collect();

let config = Self {
env,
env_sources,
env: OnceCell::new(),
path_dirs: load_path_dirs(&config_files),
aliases: load_aliases(&config_files),
all_aliases: OnceCell::new(),
Expand All @@ -95,6 +93,16 @@ impl Config {

Ok(config)
}
pub fn env(&self) -> Result<BTreeMap<String, String>> {
Ok(self
.env_with_sources()?
.iter()
.map(|(k, (v, _))| (k.clone(), v.clone()))
.collect())
}
pub fn env_with_sources(&self) -> Result<&EnvWithSources> {
self.env.get_or_try_init(|| load_env(&self.config_files))
}
pub fn get_shorthands(&self) -> &Shorthands {
self.shorthands
.get_or_init(|| get_shorthands(&Settings::get()))
Expand Down Expand Up @@ -435,22 +443,18 @@ fn parse_config_file(
}
}

fn load_env(
settings: &Settings,
config_files: &ConfigMap,
) -> (BTreeMap<String, String>, HashMap<String, PathBuf>) {
let mut env = BTreeMap::new();
let mut env_sources = HashMap::new();
fn load_env(config_files: &ConfigMap) -> Result<EnvWithSources> {
let mut env = IndexMap::new();
for (source, cf) in config_files.iter().rev() {
env.extend(cf.env());
for k in cf.env().keys() {
env_sources.insert(k.clone(), source.clone());
for (k, v) in cf.env() {
env.insert(k, (v, source.clone()));
}
for k in cf.env_remove() {
// remove values set to "false"
env.remove(&k);
}
}
let settings = Settings::get();
if let Some(env_file) = &settings.env_file {
match dotenvy::from_filename_iter(env_file) {
Ok(iter) => {
Expand All @@ -459,16 +463,14 @@ fn load_env(
warn!("env_file: {err}");
Default::default()
});
env.insert(k.clone(), v);
env_sources.insert(k, env_file.clone());
env.insert(k, (v, env_file.clone()));
}
}
Err(err) => trace!("env_file: {err}"),
}
}
(env, env_sources)
Ok(env)
}

fn load_path_dirs(config_files: &ConfigMap) -> Vec<PathBuf> {
let mut path_dirs = vec![];
for cf in config_files.values().rev() {
Expand Down Expand Up @@ -506,9 +508,11 @@ impl Debug for Config {
&tasks.values().map(|t| t.to_string()).collect_vec(),
);
}
if !self.env.is_empty() {
s.field("Env", &self.env);
// s.field("Env Sources", &self.env_sources);
if let Ok(env) = self.env() {
if !env.is_empty() {
s.field("Env", &env);
// s.field("Env Sources", &self.env_sources);
}
}
if !self.path_dirs.is_empty() {
s.field("Path Dirs", &self.path_dirs);
Expand Down
2 changes: 1 addition & 1 deletion src/forge/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ impl Forge for CargoForge {
.arg("--root")
.arg(ctx.tv.install_path())
.with_pr(ctx.pr.as_ref())
.envs(&config.env)
.envs(config.env()?)
.prepend_path(ctx.ts.list_paths())?
.execute()?;

Expand Down
2 changes: 1 addition & 1 deletion src/forge/go.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ impl Forge for GoForge {
.arg("install")
.arg(&format!("{}@{}", self.name(), ctx.tv.version))
.with_pr(ctx.pr.as_ref())
.envs(&config.env)
.envs(config.env()?)
.env("GOPATH", ctx.tv.cache_path())
.env("GOBIN", ctx.tv.install_path().join("bin"))
.execute()?;
Expand Down
2 changes: 1 addition & 1 deletion src/forge/npm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl Forge for NPMForge {
.arg("--prefix")
.arg(ctx.tv.install_path())
.with_pr(ctx.pr.as_ref())
.envs(&config.env)
.envs(config.env()?)
.prepend_path(ctx.ts.list_paths())?
.execute()?;

Expand Down
6 changes: 3 additions & 3 deletions src/plugins/core/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,7 @@ impl NodePlugin {
.arg("install")
.arg("--global")
.arg(package)
.envs(&config.env)
.envs(config.env()?)
.env("PATH", CorePlugin::path_env_with_tv_path(tv)?)
.execute()?;
}
Expand Down Expand Up @@ -213,7 +213,7 @@ impl NodePlugin {
CmdLineRunner::new(self.node_path(tv))
.with_pr(pr)
.arg("-v")
.envs(&config.env)
.envs(config.env()?)
.execute()
}

Expand All @@ -223,7 +223,7 @@ impl NodePlugin {
.env("PATH", CorePlugin::path_env_with_tv_path(tv)?)
.with_pr(pr)
.arg("-v")
.envs(&config.env)
.envs(config.env()?)
.execute()
}

Expand Down
8 changes: 4 additions & 4 deletions src/plugins/core/python.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl PythonPlugin {
.with_pr(ctx.pr.as_ref())
.arg(ctx.tv.version.as_str())
.arg(&ctx.tv.install_path())
.envs(&config.env);
.envs(config.env()?);
if settings.verbose {
cmd = cmd.arg("--verbose");
}
Expand Down Expand Up @@ -213,7 +213,7 @@ impl PythonPlugin {
.arg("--upgrade")
.arg("-r")
.arg(packages_file)
.envs(&config.env)
.envs(config.env()?)
.execute()
}

Expand Down Expand Up @@ -245,7 +245,7 @@ impl PythonPlugin {
.arg("-m")
.arg("venv")
.arg(&virtualenv)
.envs(&config.env);
.envs(config.env()?);
if let Some(pr) = pr {
cmd = cmd.with_pr(pr);
}
Expand Down Expand Up @@ -288,7 +288,7 @@ impl PythonPlugin {
pr.set_message("python --version".into());
CmdLineRunner::new(self.python_path(tv))
.arg("--version")
.envs(&config.env)
.envs(config.env()?)
.execute()
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/plugins/core/ruby.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ impl RubyPlugin {
let mut cmd = CmdLineRunner::new(gem)
.with_pr(pr)
.arg("install")
.envs(&config.env);
.envs(config.env()?);
match package.split_once(' ') {
Some((name, "--pre")) => cmd = cmd.arg(name).arg("--pre"),
Some((name, version)) => cmd = cmd.arg(name).arg("--version").arg(version),
Expand All @@ -208,7 +208,7 @@ impl RubyPlugin {
CmdLineRunner::new(self.ruby_path(tv))
.with_pr(pr)
.arg("-v")
.envs(&config.env)
.envs(config.env()?)
.execute()
}

Expand All @@ -217,7 +217,7 @@ impl RubyPlugin {
CmdLineRunner::new(self.gem_path(tv))
.with_pr(pr)
.arg("-v")
.envs(&config.env)
.envs(config.env()?)
.env("PATH", CorePlugin::path_env_with_tv_path(tv)?)
.execute()
}
Expand Down Expand Up @@ -260,7 +260,7 @@ impl RubyPlugin {
.args(self.install_args_ruby_build(tv)?)
.stdin_string(self.fetch_patches()?)
};
Ok(cmd.with_pr(pr).envs(&config.env))
Ok(cmd.with_pr(pr).envs(config.env()?))
}
fn install_args_ruby_build(&self, tv: &ToolVersion) -> Result<Vec<String>> {
let mut args = env::MISE_RUBY_BUILD_OPTS.clone()?;
Expand Down
Loading

0 comments on commit a5573cc

Please sign in to comment.