Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow _.file in vars #3593

Merged
merged 1 commit into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/tasks/task-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -361,3 +361,12 @@ e2e_args = '--headless'
[tasks.test]
run = './scripts/test-e2e.sh {{vars.e2e_args}}'
```

Like `[env]`, vars can also be read in as a file:

```toml
[vars]
_.file = ".env"
```

[Secrets](/environments/secrets) are also supported as vars.
14 changes: 13 additions & 1 deletion e2e/tasks/test_task_vars
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,17 @@ cat <<EOF >mise.toml
tasks.a.run = "echo foo: {{vars.foo}}"
vars.foo = "bar"
EOF

assert "mise run a" "foo: bar"

cat <<EOF >mise.toml
tasks.a.run = "echo foo: {{vars.foo}}"
vars.foo = "{{cwd}}"
EOF
assert "mise run a" "foo: $(pwd)"

echo '{ "SECRET": "123" }' >.env.json
cat <<EOF >mise.toml
tasks.a.run = "echo foo: {{vars.SECRET}}"
vars._.file = ".env.json"
EOF
assert "mise run a" "foo: 123"
7 changes: 4 additions & 3 deletions src/cli/test_tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::cli::args::ToolArg;
use crate::config::Config;
use crate::file::display_path;
use crate::registry::REGISTRY;
use crate::tera::{get_tera, BASE_CONTEXT};
use crate::tera::get_tera;
use crate::toolset::{InstallOptions, ToolsetBuilder};
use crate::ui::time;
use crate::{dirs, env, file};
Expand Down Expand Up @@ -129,7 +129,8 @@ impl TestTool {
return Ok(());
};
let backend = tv.backend()?;
let env = ts.env_with_path(&Config::get())?;
let config = Config::get();
let env = ts.env_with_path(&config)?;
let mut which_parts = cmd.split_whitespace().collect::<Vec<_>>();
let cmd = which_parts.remove(0);
let mut which_cmd = backend.which(&tv, cmd)?.unwrap_or(PathBuf::from(cmd));
Expand Down Expand Up @@ -170,7 +171,7 @@ impl TestTool {
}
None => return Err(eyre!("command failed: terminated by signal")),
}
let mut ctx = BASE_CONTEXT.clone();
let mut ctx = config.tera_ctx.clone();
ctx.insert("version", &tv.version);
let mut tera = get_tera(dirs::CWD.as_ref().map(|d| d.as_path()));
let expected = tera.render_str(expected, &ctx)?;
Expand Down
10 changes: 5 additions & 5 deletions src/config/config_file/mise_toml.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ pub struct MiseToml {
#[serde(default)]
watch_files: Vec<WatchFile>,
#[serde(default)]
vars: IndexMap<String, String>,
vars: EnvList,
#[serde(default)]
settings: SettingsPartial,
}
Expand Down Expand Up @@ -293,6 +293,10 @@ impl ConfigFile for MiseToml {
Ok(all)
}

fn vars_entries(&self) -> eyre::Result<Vec<EnvDirective>> {
Ok(self.vars.0.clone())
}

fn tasks(&self) -> Vec<&Task> {
self.tasks.0.values().collect()
}
Expand Down Expand Up @@ -486,10 +490,6 @@ impl ConfigFile for MiseToml {
.flatten()
.collect())
}

fn vars(&self) -> eyre::Result<&IndexMap<String, String>> {
Ok(&self.vars)
}
}

/// Returns a [`toml_edit::Key`] from the given `key`.
Expand Down
20 changes: 5 additions & 15 deletions src/config/config_file/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::sync::{Mutex, Once};

use eyre::{eyre, Result};
use idiomatic_version::IdiomaticVersionFile;
use indexmap::IndexMap;
use once_cell::sync::Lazy;
use path_absolutize::Absolutize;
use serde_derive::Deserialize;
Expand All @@ -25,7 +24,6 @@ use crate::hash::hash_to_str;
use crate::hooks::Hook;
use crate::redactions::Redactions;
use crate::task::Task;
use crate::tera::{get_tera, BASE_CONTEXT};
use crate::toolset::{ToolRequest, ToolRequestSet, ToolSource, ToolVersionList, Toolset};
use crate::ui::{prompt, style};
use crate::watch_files::WatchFile;
Expand Down Expand Up @@ -70,10 +68,13 @@ pub trait ConfigFile: Debug + Send + Sync {
fn config_root(&self) -> PathBuf {
config_root(self.get_path())
}
fn plugins(&self) -> eyre::Result<HashMap<String, String>> {
fn plugins(&self) -> Result<HashMap<String, String>> {
Ok(Default::default())
}
fn env_entries(&self) -> eyre::Result<Vec<EnvDirective>> {
fn env_entries(&self) -> Result<Vec<EnvDirective>> {
Ok(Default::default())
}
fn vars_entries(&self) -> Result<Vec<EnvDirective>> {
Ok(Default::default())
}
fn tasks(&self) -> Vec<&Task> {
Expand All @@ -93,21 +94,10 @@ pub trait ConfigFile: Debug + Send + Sync {
Ok(Default::default())
}

fn tera(&self) -> (tera::Tera, tera::Context) {
let tera = get_tera(Some(&self.config_root()));
let mut ctx = BASE_CONTEXT.clone();
ctx.insert("config_root", &self.config_root());
(tera, ctx)
}

fn task_config(&self) -> &TaskConfig {
static DEFAULT_TASK_CONFIG: Lazy<TaskConfig> = Lazy::new(TaskConfig::default);
&DEFAULT_TASK_CONFIG
}
fn vars(&self) -> Result<&IndexMap<String, String>> {
static DEFAULT_VARS: Lazy<IndexMap<String, String>> = Lazy::new(IndexMap::new);
Ok(&DEFAULT_VARS)
}

fn redactions(&self) -> &Redactions {
static DEFAULT_REDACTIONS: Lazy<Redactions> = Lazy::new(Redactions::default);
Expand Down
10 changes: 7 additions & 3 deletions src/config/env_directive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use crate::config::config_file::{config_root, trust_check};
use crate::dirs;
use crate::env_diff::EnvMap;
use crate::file::display_path;
use crate::tera::{get_tera, BASE_CONTEXT};
use crate::tera::get_tera;
use eyre::{eyre, Context};
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer};
Expand Down Expand Up @@ -141,6 +141,7 @@ impl Display for EnvDirective {
}
}

#[derive(Default)]
pub struct EnvResults {
pub env: IndexMap<String, (String, PathBuf)>,
pub env_remove: BTreeSet<String>,
Expand All @@ -150,8 +151,11 @@ pub struct EnvResults {
}

impl EnvResults {
pub fn resolve(initial: &EnvMap, input: Vec<(EnvDirective, PathBuf)>) -> eyre::Result<Self> {
let mut ctx = BASE_CONTEXT.clone();
pub fn resolve(
mut ctx: tera::Context,
initial: &EnvMap,
input: Vec<(EnvDirective, PathBuf)>,
) -> eyre::Result<Self> {
// trace!("resolve: input: {:#?}", &input);
let mut env = initial
.iter()
Expand Down
5 changes: 3 additions & 2 deletions src/config/env_directive/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,21 @@ impl EnvResults {
#[cfg(test)]
#[cfg(unix)]
mod tests {
use super::*;
use crate::config::env_directive::EnvDirective;
use crate::env_diff::EnvMap;
use crate::tera::BASE_CONTEXT;
use crate::test::replace_path;
use insta::assert_debug_snapshot;
use test_log::test;

use super::*;

#[test]
fn test_env_path() {
let mut env = EnvMap::new();
env.insert("A".to_string(), "1".to_string());
env.insert("B".to_string(), "2".to_string());
let results = EnvResults::resolve(
BASE_CONTEXT.clone(),
&env,
vec![
(
Expand Down
5 changes: 3 additions & 2 deletions src/config/env_directive/venv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,17 +146,18 @@ impl EnvResults {
#[cfg(test)]
#[cfg(unix)]
mod tests {
use super::*;
use crate::config::env_directive::EnvDirective;
use crate::tera::BASE_CONTEXT;
use crate::test::replace_path;
use insta::assert_debug_snapshot;
use test_log::test;

use super::*;

#[test]
fn test_venv_path() {
let env = EnvMap::new();
let results = EnvResults::resolve(
BASE_CONTEXT.clone(),
&env,
vec![
(
Expand Down
74 changes: 59 additions & 15 deletions src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::hook_env::WatchFilePattern;
use crate::hooks::Hook;
use crate::plugins::PluginType;
use crate::redactions::Redactions;
use crate::tera::{get_tera, BASE_CONTEXT};
use crate::watch_files::WatchFile;
use crate::wildcard::Wildcard;
pub use settings::SETTINGS;
Expand All @@ -46,13 +47,13 @@ type AliasMap = IndexMap<String, Alias>;
type ConfigMap = IndexMap<PathBuf, Box<dyn ConfigFile>>;
pub type EnvWithSources = IndexMap<String, (String, PathBuf)>;

#[derive(Default)]
pub struct Config {
pub config_files: ConfigMap,
pub project_root: Option<PathBuf>,
pub all_aliases: AliasMap,
pub repo_urls: HashMap<String, String>,
pub vars: IndexMap<String, String>,
pub tera_ctx: tera::Context,
aliases: AliasMap,
env: OnceCell<EnvResults>,
env_with_sources: OnceCell<EnvWithSources>,
Expand Down Expand Up @@ -105,13 +106,31 @@ impl Config {
let config_files = load_all_config_files(&config_paths, &idiomatic_files)?;
time!("load config_files");

let mut tera_ctx = BASE_CONTEXT.clone();
let vars_results = load_vars(tera_ctx.clone(), &config_files)?;
let vars = vars_results
.env
.iter()
.map(|(k, (v, _))| (k.clone(), v.clone()))
.collect();
tera_ctx.insert("vars", &vars);

let mut config = Self {
aliases: load_aliases(&config_files)?,
project_root: get_project_root(&config_files),
repo_urls: load_plugins(&config_files)?,
vars: load_vars(&config_files)?,
tera_ctx,
vars,
config_files,
..Default::default()
env: OnceCell::new(),
env_with_sources: OnceCell::new(),
redactions: OnceCell::new(),
shorthands: OnceLock::new(),
hooks: OnceCell::new(),
tasks: OnceCell::new(),
tool_request_set: OnceCell::new(),
toolset: OnceCell::new(),
all_aliases: Default::default(),
};
time!("load build");

Expand Down Expand Up @@ -185,7 +204,7 @@ impl Config {
Ok(env)
})
}
pub fn env_results(&self) -> eyre::Result<&EnvResults> {
pub fn env_results(&self) -> Result<&EnvResults> {
self.env.get_or_try_init(|| self.load_env())
}
pub fn path_dirs(&self) -> eyre::Result<&Vec<PathBuf>> {
Expand Down Expand Up @@ -548,7 +567,7 @@ impl Config {
Ok(())
}

fn load_env(&self) -> eyre::Result<EnvResults> {
fn load_env(&self) -> Result<EnvResults> {
time!("load_env start");
let entries = self
.config_files
Expand All @@ -563,7 +582,7 @@ impl Config {
.flatten()
.collect();
// trace!("load_env: entries: {:#?}", entries);
let env_results = EnvResults::resolve(&env::PRISTINE_ENV, entries)?;
let env_results = EnvResults::resolve(self.tera_ctx.clone(), &env::PRISTINE_ENV, entries)?;
time!("load_env done");
if log::log_enabled!(log::Level::Trace) {
trace!("{env_results:#?}");
Expand Down Expand Up @@ -629,14 +648,21 @@ impl Config {
.collect())
}

fn tera(&self, config_root: &Path) -> (tera::Tera, tera::Context) {
let tera = get_tera(Some(config_root));
let mut ctx = self.tera_ctx.clone();
ctx.insert("config_root", &config_root);
(tera, ctx)
}

pub fn redactions(&self) -> Result<&IndexSet<String>> {
self.redactions.get_or_try_init(|| {
let mut redactions = Redactions::default();
for cf in self.config_files.values() {
let r = cf.redactions();
if !r.is_empty() {
let mut r = r.clone();
let (tera, ctx) = cf.tera();
let (tera, ctx) = self.tera(&cf.config_root());
r.render(&mut tera.clone(), &ctx)?;
redactions.merge(r);
}
Expand Down Expand Up @@ -1041,15 +1067,27 @@ fn load_plugins(config_files: &ConfigMap) -> Result<HashMap<String, String>> {
Ok(plugins)
}

fn load_vars(config_files: &ConfigMap) -> Result<IndexMap<String, String>> {
let mut vars = IndexMap::new();
for config_file in config_files.values() {
for (k, v) in config_file.vars()?.clone() {
vars.insert(k, v);
}
fn load_vars(ctx: tera::Context, config_files: &ConfigMap) -> Result<EnvResults> {
time!("load_vars start");
let entries = config_files
.iter()
.rev()
.map(|(source, cf)| {
cf.vars_entries()
.map(|ee| ee.into_iter().map(|e| (e, source.clone())))
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.flatten()
.collect();
let vars_results = EnvResults::resolve(ctx, &env::PRISTINE_ENV, entries)?;
time!("load_vars done");
if log::log_enabled!(log::Level::Trace) {
trace!("{vars_results:#?}");
} else {
debug!("{vars_results:?}");
}
trace!("load_vars: {}", vars.len());
Ok(vars)
Ok(vars_results)
}

impl Debug for Config {
Expand Down Expand Up @@ -1077,6 +1115,12 @@ impl Debug for Config {
if !env_results.env_files.is_empty() {
s.field("Path Dirs", &env_results.env_paths);
}
if !env_results.env_scripts.is_empty() {
s.field("Scripts", &env_results.env_scripts);
}
if !env_results.env_files.is_empty() {
s.field("Files", &env_results.env_files);
}
}
if !self.aliases.is_empty() {
s.field("Aliases", &self.aliases);
Expand Down
Loading
Loading