From cc37bf03eeffa2d17e54658cf67a9c3c22c41117 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:49:50 +0000 Subject: [PATCH 1/2] feat: multiple MISE_ENV environments --- docs/configuration/environments.md | 2 ++ e2e/config/test_config_env | 11 +++++++++++ schema/mise.json | 6 +++++- settings.toml | 8 ++++++-- src/cli/mod.rs | 4 ++-- src/cli/use.rs | 3 ++- src/config/mod.rs | 4 ++-- src/config/settings.rs | 7 +++++-- src/env.rs | 10 +++++++--- 9 files changed, 42 insertions(+), 13 deletions(-) create mode 100644 e2e/config/test_config_env diff --git a/docs/configuration/environments.md b/docs/configuration/environments.md index 3ed10f458c..039b8e9834 100644 --- a/docs/configuration/environments.md +++ b/docs/configuration/environments.md @@ -26,3 +26,5 @@ Use `mise config` to see which files are being used. The rules around which file is written are different because we ultimately need to choose one. See the docs for [`mise use`](/cli/use.html) for more information. + +Multiple environments can be specified, e.g. `MISE_ENV=ci,test` with the last one taking precedence. diff --git a/e2e/config/test_config_env b/e2e/config/test_config_env new file mode 100644 index 0000000000..a495326156 --- /dev/null +++ b/e2e/config/test_config_env @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +echo "tools.dummy = '1'" >mise.toml +echo "tools.dummy = '2'" >mise.test.toml +echo "tools.dummy = '3'" >mise.ci.toml + +assert "mise ls dummy" "dummy 1.1.0 (missing) ~/workdir/mise.toml 1" +MISE_ENV=test assert "mise ls dummy" "dummy 2.0.0 (missing) ~/workdir/mise.test.toml 2" +MISE_ENV=ci assert "mise ls dummy" "dummy 3 (missing) ~/workdir/mise.ci.toml 3" +MISE_ENV=ci,test assert "mise ls dummy" "dummy 2.0.0 (missing) ~/workdir/mise.test.toml 2" +MISE_ENV=test,ci assert "mise ls dummy" "dummy 3 (missing) ~/workdir/mise.ci.toml 3" diff --git a/schema/mise.json b/schema/mise.json index e379de2480..01104004e0 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -235,8 +235,12 @@ } }, "env": { + "default": [], "description": "Env to use for mise..toml files.", - "type": "string" + "type": "array", + "items": { + "type": "string" + } }, "env_file": { "description": "Path to a file containing environment variables.", diff --git a/settings.toml b/settings.toml index 7709a8b0ab..d6f1f3d7ef 100644 --- a/settings.toml +++ b/settings.toml @@ -222,15 +222,19 @@ description = "Tools defined in mise.toml that should be ignored" [env] env = "MISE_ENV" -type = "String" +type = "ListString" +default = [] +parse_env = "list_by_comma" description = "Env to use for mise..toml files." -optional = true docs = """ Enables profile-specific config files such as `.mise.development.toml`. Use this for different env vars or different tool versions in development/staging/production environments. See [Configuration Environments](/configuration/environments) for more on how to use this feature. + +Multiple envs can be set by separating them with a comma, e.g. `MISE_ENV=ci,test`. +They will be read in order, with the last one taking precedence. """ [env_file] diff --git a/src/cli/mod.rs b/src/cli/mod.rs index a1df93f90c..98f40671aa 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -96,7 +96,7 @@ pub struct Cli { pub debug: bool, /// Set the environment for loading `mise..toml` #[clap(short = 'E', long, global = true)] - pub env: Option, + pub env: Option>, /// Force the operation #[clap(long, short, hide = true)] pub force: bool, @@ -112,7 +112,7 @@ pub struct Cli { pub prefix: bool, /// Set the profile (environment) #[clap(short = 'P', long, global = true, hide = true)] - pub profile: Option, + pub profile: Option>, #[clap(long, short, hide = true)] pub shell: Option, /// Tool(s) to run in addition to what is in mise.toml files diff --git a/src/cli/use.rs b/src/cli/use.rs index 6ea13e29fc..0a6dc18c83 100644 --- a/src/cli/use.rs +++ b/src/cli/use.rs @@ -193,7 +193,8 @@ impl Use { fn get_config_file(&self) -> Result> { let cwd = env::current_dir()?; - let path = if let Some(env) = &*env::MISE_ENV { + let path = if !env::MISE_ENV.is_empty() { + let env = env::MISE_ENV.last().unwrap(); config_file_from_dir(&cwd.join(format!("mise.{env}.toml"))) } else if self.global || env::in_home_dir() { MISE_GLOBAL_CONFIG_FILE.clone() diff --git a/src/config/mod.rs b/src/config/mod.rs index fbeab9faef..cc8c9cc4a6 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -702,7 +702,7 @@ pub static DEFAULT_CONFIG_FILENAMES: Lazy> = Lazy::new(|| { .iter() .map(|f| f.to_string()) .collect_vec(); - if let Some(env) = &*env::MISE_ENV { + for env in &*env::MISE_ENV { filenames.push(format!(".config/mise/config.{env}.toml")); filenames.push(format!(".config/mise.{env}.toml")); filenames.push(format!("mise/config.{env}.toml")); @@ -815,7 +815,7 @@ pub fn global_config_files() -> IndexSet { config_files.insert(f); } } - if let Some(env) = &*env::MISE_ENV { + for env in &*env::MISE_ENV { let global_profile_files = vec![ dirs::CONFIG.join(format!("config.{env}.toml")), dirs::CONFIG.join(format!("config.{env}.local.toml")), diff --git a/src/config/settings.rs b/src/config/settings.rs index 199f2f6b8b..f0be6dc11c 100644 --- a/src/config/settings.rs +++ b/src/config/settings.rs @@ -233,8 +233,11 @@ impl Settings { if let Some(cd) = &cli.cd { s.cd = Some(cd.clone()); } - if let Some(env) = cli.env.as_ref().or(cli.profile.as_ref()) { - s.env = Some(env.clone()); + if cli.profile.is_some() { + s.env = cli.profile.clone(); + } + if cli.env.is_some() { + s.env = cli.env.clone(); } if cli.yes { s.yes = Some(true); diff --git a/src/env.rs b/src/env.rs index 7a53df9cae..807ce37392 100644 --- a/src/env.rs +++ b/src/env.rs @@ -94,7 +94,7 @@ pub static MISE_DEFAULT_CONFIG_FILENAME: Lazy = Lazy::new(|| { var("MISE_DEFAULT_CONFIG_FILENAME") .ok() .or(MISE_OVERRIDE_CONFIG_FILENAMES.first().cloned()) - .or(MISE_ENV.as_ref().map(|env| format!("mise.{env}.toml"))) + .or(MISE_ENV.last().map(|env| format!("mise.{env}.toml"))) .unwrap_or_else(|| "mise.toml".into()) }); pub static MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES: Lazy>> = @@ -108,7 +108,7 @@ pub static MISE_OVERRIDE_CONFIG_FILENAMES: Lazy> = Ok(v) => v.split(':').map(|s| s.to_string()).collect(), Err(_) => Default::default(), }); -pub static MISE_ENV: Lazy> = Lazy::new(|| environment(&ARGS.read().unwrap())); +pub static MISE_ENV: Lazy> = Lazy::new(|| environment(&ARGS.read().unwrap())); pub static MISE_GLOBAL_CONFIG_FILE: Lazy = Lazy::new(|| { var_path("MISE_GLOBAL_CONFIG_FILE") .or_else(|| var_path("MISE_CONFIG_FILE")) @@ -410,7 +410,7 @@ fn prefer_stale(args: &[String]) -> bool { .contains(&c.as_str()) } -fn environment(args: &[String]) -> Option { +fn environment(args: &[String]) -> Vec { let arg_defs = HashSet::from([ format!("--{}", PROFILE_ARG.get_long().unwrap_or_default()), format!("-{}", PROFILE_ARG.get_short().unwrap_or_default()), @@ -429,6 +429,10 @@ fn environment(args: &[String]) -> Option { .or_else(|| var("MISE_ENV").ok()) .or_else(|| var("MISE_PROFILE").ok()) .or_else(|| var("MISE_ENVIRONMENT").ok()) + .unwrap_or_default() + .split(',') + .map(String::from) + .collect() } fn log_file_level() -> Option { From 7ad74d5a1d4a47c79923406aed75c9e9c220adc9 Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Thu, 5 Dec 2024 16:51:04 +0000 Subject: [PATCH 2/2] feat: multiple MISE_ENV environments --- docs/cli/index.md | 2 +- mise.usage.kdl | 4 ++-- src/cli/mod.rs | 2 +- src/env.rs | 1 + xtasks/fig/src/mise.ts | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/cli/index.md b/docs/cli/index.md index d942d165db..be1cb4954e 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -18,7 +18,7 @@ Shorthand for `mise task run `. Change directory before running command -### `-E --env ` +### `-E --env... ` Set the environment for loading `mise..toml` diff --git a/mise.usage.kdl b/mise.usage.kdl index b58d575d06..ea256a307a 100644 --- a/mise.usage.kdl +++ b/mise.usage.kdl @@ -17,7 +17,7 @@ flag "-C --cd" help="Change directory before running command" global=true { } flag "-n --dry-run" help="Dry run, don't actually do anything" hide=true global=true flag "--debug" help="Sets log level to debug" hide=true global=true -flag "-E --env" help="Set the environment for loading `mise..toml`" global=true { +flag "-E --env" help="Set the environment for loading `mise..toml`" var=true global=true { arg "" } flag "-f --force" help="Force the operation" hide=true @@ -31,7 +31,7 @@ flag "--log-level" hide=true global=true { } } flag "-p --prefix" hide=true -flag "-P --profile" help="Set the profile (environment)" hide=true global=true { +flag "-P --profile" help="Set the profile (environment)" var=true hide=true global=true { arg "" } flag "-s --shell" hide=true { diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 98f40671aa..dca73523ed 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -111,7 +111,7 @@ pub struct Cli { #[clap(long, short, hide = true, overrides_with = "interleave")] pub prefix: bool, /// Set the profile (environment) - #[clap(short = 'P', long, global = true, hide = true)] + #[clap(short = 'P', long, global = true, hide = true, conflicts_with = "env")] pub profile: Option>, #[clap(long, short, hide = true)] pub shell: Option, diff --git a/src/env.rs b/src/env.rs index 807ce37392..9ec17928ff 100644 --- a/src/env.rs +++ b/src/env.rs @@ -431,6 +431,7 @@ fn environment(args: &[String]) -> Vec { .or_else(|| var("MISE_ENVIRONMENT").ok()) .unwrap_or_default() .split(',') + .filter(|s| !s.is_empty()) .map(String::from) .collect() } diff --git a/xtasks/fig/src/mise.ts b/xtasks/fig/src/mise.ts index cfa8c6103c..8befe4ad44 100644 --- a/xtasks/fig/src/mise.ts +++ b/xtasks/fig/src/mise.ts @@ -3145,7 +3145,7 @@ const completionSpec: Fig.Spec = { "--env" ], "description": "Set the environment for loading `mise..toml`", - "isRepeatable": false, + "isRepeatable": true, "args": { "name": "env", "isOptional": false,