From 5a0508fba974bcce0a5865000d3716e18b88d17e Mon Sep 17 00:00:00 2001 From: jdx <216188+jdx@users.noreply.github.com> Date: Thu, 28 Nov 2024 12:46:10 -0600 Subject: [PATCH] feat!: added MISE_OVERRIDE_CONFIG_FILENAMES config (#3266) Before this change, MISE_DEFAULT_CONFIG_FILENAME and MISE_DEFAULT_TOOL_VERSIONS_FILENAME accomplished 2 things where it should only have been doing one. These commands both ignored "mise.toml"/".tool-versions" (but only that one filename) if set and also changed the default location for commands like `mise use` to write to. This made it so you could not set MISE_DEFAULT_CONFIG_FILENAME=.mise.local.toml if you always want to use that file without also ignoring any "mise.toml" files. This change adds 2 new env vars, MISE_OVERRIDE_CONFIG_FILENAMES and MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES which can be arrays of filenames. If these are set, mise will not load any local filenames (globals are still loaded, use MISE_CONFIG_FILE to change that) and instead mise will use those filenames. For the common pattern of just wanting to ignore .tool-versions you can set MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES=none Fixes #1901 BREAKING CHANGE: This causes mise to start reading mise.toml if MISE_DEFAULT_CONFIG_FILENAME was set previously which it did not before. This change also changes the behavior slightly for writing new config files. Before it was not consistent across commands like `mise settings -l` and `mise use` but now it will use the following order if no file exists and it needs to create one: - if MISE_DEFAULT_CONFIG_FILENAME is set, create that - if MISE_OVERRIDE_CONFIG_FILENAMES has anything, use the first. - if MISE_ENV is set, create "mise.$MISE_ENV.toml" - else, create "mise.toml" If you have not been using any of these env vars nothing should change. --- docs/.vitepress/config.ts | 5 ++- docs/cli/use.md | 7 ++++ docs/configuration.md | 30 +++++--------- .../environments.md} | 25 ++++++------ mise.usage.kdl | 10 ++++- schema/mise.json | 30 ++++++++++++++ settings.toml | 39 +++++++++++++++++++ src/cli/use.rs | 10 ++++- src/config/config_file/mod.rs | 7 ++++ src/config/mod.rs | 20 ++++++---- src/config/settings.rs | 10 ++++- src/env.rs | 29 ++++++++++++-- 12 files changed, 171 insertions(+), 51 deletions(-) rename docs/{profiles.md => configuration/environments.md} (53%) diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 9e4b47df9b..c868ed2c18 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -40,6 +40,10 @@ export default defineConfig({ items: [ { text: "mise.toml", link: "/configuration" }, { text: "Settings", link: "/configuration/settings" }, + { + text: "Configuration Environments", + link: "/configuration/environments", + }, ], }, { @@ -92,7 +96,6 @@ export default defineConfig({ text: "Environments", items: [ { text: "Environment variables", link: "/environments/" }, - { text: "Profiles", link: "/profiles" }, { text: "direnv", link: "/direnv" }, ], }, diff --git a/docs/cli/use.md b/docs/cli/use.md index eb7d47a1af..29273e9c5b 100644 --- a/docs/cli/use.md +++ b/docs/cli/use.md @@ -9,6 +9,13 @@ Installs a tool and adds the version it to mise.toml. This will install the tool version if it is not already installed. By default, this will use a `mise.toml` file in the current directory. +In the following order: + +- If `MISE_DEFAULT_CONFIG_FILENAME` is set, it will use that instead. +- If `MISE_OVERRIDE_CONFIG_FILENAMES` is set, it will the first from that list. +- If `MISE_ENV` is set, it will use a `mise..toml` instead. +- Otherwise just "mise.toml" + Use the `--global` flag to use the global config file instead. ## Arguments diff --git a/docs/configuration.md b/docs/configuration.md index 8e4e9f3eaf..6a7fdab853 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -13,7 +13,7 @@ Notes: - Paths which start with `mise` can be dotfiles, e.g.: `mise.toml` or `.mise/config.toml`. -- This list doesn't include [Profiles](/profiles) which allow for environment-specific config files like `mise.development.toml`—set with `MISE_PROFILE=development`. +- This list doesn't include [Configuration Environments](/configuration/environments) which allow for environment-specific config files like `mise.development.toml`—set with `MISE_ENV=development`. - See [`LOCAL_CONFIG_FILENAMES` in `src/config/mod.rs`](https://github.com/jdx/mise/blob/main/src/config/mod.rs) for the actual code for these paths and their precedence. Some legacy paths are not listed here for brevity. These files recurse upwards, so if you have a `~/src/work/myproj/mise.toml` file, what is defined @@ -77,7 +77,7 @@ Then when inside of `~/src/myproj/backend`, `node` will be `18`, `python` will b will be `3.1`. You can check the active versions with `mise ls --current`. You can also have environment specific config files like `.mise.production.toml`, see -[Profiles](/profiles) for more details. +[Configuration Environments](/configuration/environments) for more details. ### `[tools]` - Dev tools @@ -291,6 +291,14 @@ See [Tasks](/tasks/) for the full list of configuration options. ## Environment variables +::: tip +Normally environment variables in mise are used to set [settings](/configuration/settings) so most +environment variables are in that doc. The following are environment variables that are not settings. + +A setting in mise is generally something that can be configured either as an environment variable +or set in a config file. +::: + mise can also be configured via environment variables. The following options are available: ### `MISE_DATA_DIR` @@ -328,24 +336,6 @@ Default: `$MISE_CONFIG_DIR/config.toml` (Usually ~/.config/mise/config.toml) This is the path to the config file. -### `MISE_DEFAULT_TOOL_VERSIONS_FILENAME` - -Set to something other than ".tool-versions" to have mise look for `.tool-versions` files but with -a different name. - -### `MISE_DEFAULT_CONFIG_FILENAME` - -Set to something other than `.mise.toml` to have mise look for `.mise.toml` config files with a -different name. - -### `MISE_PROFILE` - -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 -[Profiles](/profiles) for more on how -to use this feature. - ### `MISE_ENV_FILE` Set to a filename to read from env from a dotenv file. e.g.: `MISE_ENV_FILE=.env`. diff --git a/docs/profiles.md b/docs/configuration/environments.md similarity index 53% rename from docs/profiles.md rename to docs/configuration/environments.md index 4503689cf3..3ed10f458c 100644 --- a/docs/profiles.md +++ b/docs/configuration/environments.md @@ -1,29 +1,28 @@ -# Profiles +# Config Environments It's possible to have separate `mise.toml` files in the same directory for different -environments like `development` and `production`. To enable, either set the `-P,--profile` option or `MISE_PROFILE` environment -variable to an environment like `development` or `production`. mise will then look for a `mise.{MISE_PROFILE}.toml` file +environments like `development` and `production`. To enable, either set the `-E,--env` option or `MISE_ENV` environment +variable to an environment like `development` or `production`. mise will then look for a `mise.{MISE_ENV}.toml` file in the current directory, parent directories and the `MISE_CONFIG_DIR` directory. -mise will also look for "local" files like `mise.local.toml` and `mise.{MISE_PROFILE}.local.toml` +mise will also look for "local" files like `mise.local.toml` and `mise.{MISE_ENV}.local.toml` in the current directory and parent directories. These are intended to not be committed to version control. (Add `mise.local.toml` and `mise.*.local.toml` to your `.gitignore` file.) The priority of these files goes in this order (top overrides bottom): -- `mise.{MISE_PROFILE}.local.toml` +- `mise.{MISE_ENV}.local.toml` - `mise.local.toml` -- `mise.{MISE_PROFILE}.toml` +- `mise.{MISE_ENV}.toml` - `mise.toml` -You can also use paths like `mise/config.{MISE_PROFILE}.toml` or `.config/mise.{MISE_PROFILE}.toml` Those rules -follow the order in [Configuration](./configuration.md). +If `MISE_OVERRIDE_CONFIG_FILENAMES` is set, that will be used instead of all of this. + +You can also use paths like `mise/config.{MISE_ENV}.toml` or `.config/mise.{MISE_ENV}.toml` Those rules +follow the order in [Configuration](/configuration). Use `mise config` to see which files are being used. -::: warning -Note that currently modifying `MISE_DEFAULT_CONFIG_FILENAME` to something other than `mise.toml` -will not work with this feature. For now, it will disable it entirely. This may change in the -future. -::: +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. diff --git a/mise.usage.kdl b/mise.usage.kdl index e738bbb89f..3d7dc03ec3 100644 --- a/mise.usage.kdl +++ b/mise.usage.kdl @@ -1485,12 +1485,18 @@ See https://usage.jdx.dev for more information on this specification." } cmd "use" help="Installs a tool and adds the version it to mise.toml." { alias "u" - long_help r"Installs a tool and adds the version it to mise.toml. + long_help r#"Installs a tool and adds the version it to mise.toml. This will install the tool version if it is not already installed. By default, this will use a `mise.toml` file in the current directory. -Use the `--global` flag to use the global config file instead." +In the following order: + - If `MISE_DEFAULT_CONFIG_FILENAME` is set, it will use that instead. + - If `MISE_OVERRIDE_CONFIG_FILENAMES` is set, it will the first from that list. + - If `MISE_ENV` is set, it will use a `mise..toml` instead. + - Otherwise just "mise.toml" + +Use the `--global` flag to use the global config file instead."# after_long_help r"Examples: # set the current version of node to 20.x in mise.toml of current directory diff --git a/schema/mise.json b/schema/mise.json index 0bedd0e48c..a7d1f79244 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -187,10 +187,24 @@ "description": "Use color in mise terminal output", "type": "boolean" }, + "config_file": { + "description": "Path to the global mise config file. Default is `~/.config/mise/config.toml`. This must be an env var.", + "type": "string" + }, "debug": { "description": "Sets log level to debug", "type": "boolean" }, + "default_config_filename": { + "default": "mise.toml", + "description": "The default config filename read. `mise use` and other commands that create new config files will use this value. This must be an env var.", + "type": "string" + }, + "default_tool_versions_filename": { + "default": ".tool-versions", + "description": "The default .tool-versions filename read. `mise use` and other commands that write to new config files will use this if asdf_compat=1. This must be an env var.", + "type": "string" + }, "disable_backends": { "default": [], "description": "Backends to disable such as `asdf` or `pipx`", @@ -377,6 +391,22 @@ } } }, + "override_config_filenames": { + "default": [], + "description": "If set, mise will ignore default config files like `mise.toml` and use these filenames instead. This must be an env var.", + "type": "array", + "items": { + "type": "string" + } + }, + "override_tool_versions_filename": { + "default": [], + "description": "If set, mise will ignore .tool-versions files and use these filename instead. Can be set to `none` to disable .tool-versions. This must be an env var.", + "type": "array", + "items": { + "type": "string" + } + }, "paranoid": { "description": "Enables extra-secure behavior.", "type": "boolean" diff --git a/settings.toml b/settings.toml index 4643d8126c..d16899c7ae 100644 --- a/settings.toml +++ b/settings.toml @@ -160,12 +160,30 @@ type = "Bool" default = true description = "Use color in mise terminal output" +[config_file] +env = "MISE_CONFIG_FILE" +type = "Path" +optional = true +description = "Path to the global mise config file. Default is `~/.config/mise/config.toml`. This must be an env var." + [debug] env = "MISE_DEBUG" type = "Bool" hide = true description = "Sets log level to debug" +[default_config_filename] +env = "MISE_DEFAULT_CONFIG_FILENAME" +type = "String" +default = "mise.toml" +description = "The default config filename read. `mise use` and other commands that create new config files will use this value. This must be an env var." + +[default_tool_versions_filename] +env = "MISE_DEFAULT_TOOL_VERSIONS_FILENAME" +type = "String" +default = ".tool-versions" +description = "The default .tool-versions filename read. `mise use` and other commands that write to new config files will use this if asdf_compat=1. This must be an env var." + [disable_backends] env = "MISE_DISABLE_BACKENDS" type = "ListString" @@ -212,6 +230,13 @@ env = "MISE_ENV" type = "String" 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. +""" [env_file] env = "MISE_ENV_FILE" @@ -469,6 +494,20 @@ mise use -g bun ``` """ +[override_config_filenames] +env = "MISE_OVERRIDE_CONFIG_FILENAMES" +type = "ListString" +default = [] +description = "If set, mise will ignore default config files like `mise.toml` and use these filenames instead. This must be an env var." +parse_env = "list_by_colon" + +[override_tool_versions_filename] +env = "MISE_OVERRIDE_TOOL_VERSIONS_FILENAME" +type = "ListString" +default = [] +description = "If set, mise will ignore .tool-versions files and use these filename instead. Can be set to `none` to disable .tool-versions. This must be an env var." +parse_env = "list_by_colon" + [paranoid] env = "MISE_PARANOID" type = "Bool" diff --git a/src/cli/use.rs b/src/cli/use.rs index 6cce6a3094..29cee1941b 100644 --- a/src/cli/use.rs +++ b/src/cli/use.rs @@ -7,7 +7,7 @@ use path_absolutize::Absolutize; use crate::cli::args::{BackendArg, ToolArg}; use crate::config::config_file::ConfigFile; -use crate::config::{config_file, is_global_config, Config, LOCAL_CONFIG_FILENAMES, SETTINGS}; +use crate::config::{config_file, is_global_config, Config, DEFAULT_CONFIG_FILENAMES, SETTINGS}; use crate::env::{ MISE_DEFAULT_CONFIG_FILENAME, MISE_DEFAULT_TOOL_VERSIONS_FILENAME, MISE_GLOBAL_CONFIG_FILE, }; @@ -23,6 +23,12 @@ use crate::{config, env, file}; /// This will install the tool version if it is not already installed. /// By default, this will use a `mise.toml` file in the current directory. /// +/// In the following order: +/// - If `MISE_DEFAULT_CONFIG_FILENAME` is set, it will use that instead. +/// - If `MISE_OVERRIDE_CONFIG_FILENAMES` is set, it will the first from that list. +/// - If `MISE_ENV` is set, it will use a `mise..toml` instead. +/// - Otherwise just "mise.toml" +/// /// Use the `--global` flag to use the global config file instead. #[derive(Debug, clap::Args)] #[clap(verbatim_doc_comment, visible_alias = "u", after_long_help = AFTER_LONG_HELP)] @@ -244,7 +250,7 @@ fn config_file_from_dir(p: &Path) -> PathBuf { if !p.is_dir() { return p.to_path_buf(); } - let filenames = LOCAL_CONFIG_FILENAMES + let filenames = DEFAULT_CONFIG_FILENAMES .iter() .map(|f| f.to_string()) .collect::>(); diff --git a/src/config/config_file/mod.rs b/src/config/config_file/mod.rs index 95b6d58e48..1d8d2e3c27 100644 --- a/src/config/config_file/mod.rs +++ b/src/config/config_file/mod.rs @@ -416,7 +416,14 @@ fn trust_file_hash(path: &Path) -> eyre::Result { fn detect_config_file_type(path: &Path) -> Option { match path.file_name().unwrap().to_str().unwrap() { f if f.ends_with(".toml") => Some(ConfigFileType::MiseToml), + f if env::MISE_OVERRIDE_CONFIG_FILENAMES.contains(f) => Some(ConfigFileType::MiseToml), f if env::MISE_DEFAULT_CONFIG_FILENAME.as_str() == f => Some(ConfigFileType::MiseToml), + f if env::MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES + .as_ref() + .is_some_and(|o| o.contains(f)) => + { + Some(ConfigFileType::ToolVersions) + } f if env::MISE_DEFAULT_TOOL_VERSIONS_FILENAME.as_str() == f => { Some(ConfigFileType::ToolVersions) } diff --git a/src/config/mod.rs b/src/config/mod.rs index abe7ec2571..7cbddf6bd2 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use std::sync::{Arc, OnceLock, RwLock}; use eyre::{ensure, eyre, Context, Result}; -use indexmap::IndexMap; +use indexmap::{IndexMap, IndexSet}; use itertools::Itertools; use once_cell::sync::{Lazy, OnceCell}; use rayon::prelude::*; @@ -619,9 +619,10 @@ fn load_idiomatic_files() -> BTreeMap> { idiomatic_filenames } -pub static LOCAL_CONFIG_FILENAMES: Lazy> = Lazy::new(|| { - if *env::MISE_DEFAULT_CONFIG_FILENAME == "mise.toml" { - vec![ +static LOCAL_CONFIG_FILENAMES: Lazy> = Lazy::new(|| { + if env::MISE_OVERRIDE_CONFIG_FILENAMES.is_empty() { + [ + ".tool-versions", &*env::MISE_DEFAULT_TOOL_VERSIONS_FILENAME, // .tool-versions ".config/mise/config.toml", ".config/mise.toml", @@ -629,6 +630,7 @@ pub static LOCAL_CONFIG_FILENAMES: Lazy> = Lazy::new(|| { "mise/config.toml", ".mise/config.toml", ".rtx.toml", + "mise.toml", &*env::MISE_DEFAULT_CONFIG_FILENAME, // mise.toml ".mise.toml", ".config/mise/config.local.toml", @@ -638,11 +640,13 @@ pub static LOCAL_CONFIG_FILENAMES: Lazy> = Lazy::new(|| { "mise.local.toml", ".mise.local.toml", ] + .into_iter() + .collect() } else { - vec![ - &*env::MISE_DEFAULT_TOOL_VERSIONS_FILENAME, - &*env::MISE_DEFAULT_CONFIG_FILENAME, - ] + env::MISE_OVERRIDE_CONFIG_FILENAMES + .iter() + .map(|s| s.as_str()) + .collect() } }); pub static DEFAULT_CONFIG_FILENAMES: Lazy> = Lazy::new(|| { diff --git a/src/config/settings.rs b/src/config/settings.rs index 36e61acbee..506aebab59 100644 --- a/src/config/settings.rs +++ b/src/config/settings.rs @@ -266,6 +266,9 @@ impl Settings { .to_string_lossy(); // if the file doesn't exist or is actually a .tool-versions config if !global_config.exists() + || env::MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES + .as_ref() + .is_some_and(|f| f.contains(&filename.to_string())) || filename == *env::MISE_DEFAULT_TOOL_VERSIONS_FILENAME || filename == ".tool-versions" { @@ -295,8 +298,11 @@ impl Settings { .iter() .filter(|p| { let filename = p.file_name().unwrap_or_default().to_string_lossy(); - filename != *env::MISE_DEFAULT_TOOL_VERSIONS_FILENAME - && filename != ".tool-versions" + env::MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES + .as_ref() + .is_some_and(|f| f.contains(&filename.to_string())) + || filename != *env::MISE_DEFAULT_TOOL_VERSIONS_FILENAME + && filename != ".tool-versions" }) .map(Self::parse_settings_file) .chain(once(Self::config_settings())) diff --git a/src/env.rs b/src/env.rs index 773df0d63f..3437441431 100644 --- a/src/env.rs +++ b/src/env.rs @@ -2,6 +2,7 @@ use crate::cli::args::{ENV_ARG, PROFILE_ARG}; use crate::env_diff::{EnvDiff, EnvDiffOperation, EnvDiffPatches}; use crate::file::replace_path; use crate::hook_env::{deserialize_watches, HookEnvWatches}; +use indexmap::IndexSet; use itertools::Itertools; use log::LevelFilter; use once_cell::sync::Lazy; @@ -81,10 +82,32 @@ pub static MISE_SHIMS_DIR: Lazy = Lazy::new(|| var_path("MISE_SHIMS_DIR").unwrap_or_else(|| MISE_DATA_DIR.join("shims"))); pub static MISE_DEFAULT_TOOL_VERSIONS_FILENAME: Lazy = Lazy::new(|| { - var("MISE_DEFAULT_TOOL_VERSIONS_FILENAME").unwrap_or_else(|_| ".tool-versions".into()) + var("MISE_DEFAULT_TOOL_VERSIONS_FILENAME") + .ok() + .or(MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES + .as_ref() + .and_then(|v| v.first().cloned())) + .or(var("MISE_DEFAULT_TOOL_VERSIONS_FILENAME").ok()) + .unwrap_or_else(|| ".tool-versions".into()) +}); +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"))) + .unwrap_or_else(|| "mise.toml".into()) }); -pub static MISE_DEFAULT_CONFIG_FILENAME: Lazy = - Lazy::new(|| var("MISE_DEFAULT_CONFIG_FILENAME").unwrap_or_else(|_| "mise.toml".into())); +pub static MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES: Lazy>> = + Lazy::new(|| match var("MISE_OVERRIDE_TOOL_VERSIONS_FILENAMES") { + Ok(v) if v == "none" => Some([].into()), + Ok(v) => Some(v.split(':').map(|s| s.to_string()).collect()), + Err(_) => Default::default(), + }); +pub static MISE_OVERRIDE_CONFIG_FILENAMES: Lazy> = + Lazy::new(|| match var("MISE_OVERRIDE_CONFIG_FILENAMES") { + 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_SETTINGS_FILE: Lazy = Lazy::new(|| { var_path("MISE_SETTINGS_FILE").unwrap_or_else(|| MISE_CONFIG_DIR.join("settings.toml"))