diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5bdfa9e44..acde6710e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,6 @@ repos: - id: end-of-file-fixer - id: check-yaml - id: check-toml - - id: check-added-large-files - repo: https://github.com/shellcheck-py/shellcheck-py rev: v0.10.0.1 hooks: diff --git a/docs/tasks/toml-tasks.md b/docs/tasks/toml-tasks.md index 6a016b8e1..a841b99fb 100644 --- a/docs/tasks/toml-tasks.md +++ b/docs/tasks/toml-tasks.md @@ -18,7 +18,7 @@ alias = 'b' # `mise run b` [tasks.test] description = 'Run automated tests' -run = [ # multiple commands are run in series +run = [# multiple commands are run in series 'cargo test', './scripts/test-e2e.sh', ] @@ -26,7 +26,7 @@ dir = "{{cwd}}" # run in user's cwd, default is the project's base directory [tasks.lint] description = 'Lint with clippy' -env = {RUST_BACKTRACE = '1'} # env vars for the script +env = { RUST_BACKTRACE = '1' } # env vars for the script # specify a shell command to run the script with (default is 'sh -c') shell = 'bash -c' # you can specify a multiline script instead of individual commands @@ -44,6 +44,21 @@ description = 'Cut a new release' file = 'scripts/release.sh' # execute an external script ``` +## Vars + +Vars are variables that can be shared between tasks like environment variables but they are not +passed +as environment variables to the scripts. They are defined in the `vars` section of the `mise.toml` +file. + +```toml +[vars] +e2e_args = '--headless' + +[tasks.test] +run = './scripts/test-e2e.sh {{vars.e2e_args}}' +``` + ## Arguments By default, arguments are passed to the last script in the `run` array. So if a task was defined as: @@ -53,7 +68,8 @@ By default, arguments are passed to the last script in the `run` array. So if a run = ['cargo test', './scripts/test-e2e.sh'] ``` -Then running `mise run test foo bar` will pass `foo bar` to `./scripts/test-e2e.sh` but not to `cargo test`. +Then running `mise run test foo bar` will pass `foo bar` to `./scripts/test-e2e.sh` but not to +`cargo test`. You can also define arguments using templates: @@ -65,8 +81,10 @@ run = [ ] ``` -Then running `mise run test foo bar` will pass `foo bar` to `cargo test`. `mise run test --e2e-args baz` will pass `baz` to `./scripts/test-e2e.sh`. -If any arguments are defined with templates then mise will not pass the arguments to the last script in the `run` array. +Then running `mise run test foo bar` will pass `foo bar` to `cargo test`. +`mise run test --e2e-args baz` will pass `baz` to `./scripts/test-e2e.sh`. +If any arguments are defined with templates then mise will not pass the arguments to the last script +in the `run` array. :::tip Using templates to define arguments will make them work with completion and help messages. @@ -74,7 +92,8 @@ Using templates to define arguments will make them work with completion and help ### Positional Arguments -These are defined in scripts with `{{arg()}}`. They are used for positional arguments where the order matters. +These are defined in scripts with `{{arg()}}`. They are used for positional +arguments where the order matters. Example: @@ -85,14 +104,16 @@ run = 'cargo test {{arg(name="file")}}' # runs: cargo test my-test-file ``` -- `i`: The index of the argument. This can be used to specify the order of arguments. Defaults to the order they're defined in the scripts. +- `i`: The index of the argument. This can be used to specify the order of arguments. Defaults to + the order they're defined in the scripts. - `name`: The name of the argument. This is used for help/error messages. - `var`: If `true`, multiple arguments can be passed. - `default`: The default value if the argument is not provided. ### Options -These are defined in scripts with `{{option()}}`. They are used for named arguments where the order doesn't matter. +These are defined in scripts with `{{option()}}`. They are used for named +arguments where the order doesn't matter. Example: @@ -109,7 +130,8 @@ run = 'cargo test {{option(name="file")}}' ### Flags -Flags are like options except they don't take values. They are defined in scripts with `{{flag()}}`. +Flags are like options except they don't take values. They are defined in scripts with +`{{flag()}}`. Example: diff --git a/e2e/tasks/test_task_vars b/e2e/tasks/test_task_vars new file mode 100644 index 000000000..945a5d071 --- /dev/null +++ b/e2e/tasks/test_task_vars @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +cat <mise.toml +tasks.a.run = "echo foo: {{vars.foo}}" +vars.foo = "bar" +EOF + +assert "mise run a" "foo: bar" diff --git a/schema/mise.json b/schema/mise.json index d2b7d589d..bd9abfe9f 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -745,6 +745,14 @@ } ] }, + "vars": { + "description": "variables to set", + "type": "object", + "additionalProperties": { + "description": "value of variable", + "type": "string" + } + }, "task_config": { "description": "configration for task execution/management", "type": "object", diff --git a/schema/mise.json.hbs b/schema/mise.json.hbs index b08b79540..05dbb31bf 100644 --- a/schema/mise.json.hbs +++ b/schema/mise.json.hbs @@ -251,6 +251,14 @@ } ] }, + "vars": { + "description": "variables to set", + "type": "object", + "additionalProperties": { + "description": "value of variable", + "type": "string" + } + }, "task_config": { "description": "configration for task execution/management", "type": "object", diff --git a/src/config/config_file/mise_toml.rs b/src/config/config_file/mise_toml.rs index 452e8b0e9..a29a9a9f1 100644 --- a/src/config/config_file/mise_toml.rs +++ b/src/config/config_file/mise_toml.rs @@ -53,6 +53,8 @@ pub struct MiseToml { #[serde(default)] tasks: Tasks, #[serde(default)] + vars: IndexMap, + #[serde(default)] settings: SettingsPartial, } @@ -420,6 +422,10 @@ impl ConfigFile for MiseToml { fn task_config(&self) -> &TaskConfig { &self.task_config } + + fn vars(&self) -> eyre::Result<&IndexMap> { + Ok(&self.vars) + } } /// Returns a [`toml_edit::Key`] from the given `key`. @@ -482,6 +488,7 @@ impl Clone for MiseToml { tasks: self.tasks.clone(), task_config: self.task_config.clone(), settings: self.settings.clone(), + vars: self.vars.clone(), } } } diff --git a/src/config/config_file/mod.rs b/src/config/config_file/mod.rs index 1248f17e9..d7a91c5ac 100644 --- a/src/config/config_file/mod.rs +++ b/src/config/config_file/mod.rs @@ -7,6 +7,7 @@ use std::sync::{Mutex, Once}; use eyre::eyre; use eyre::Result; +use indexmap::IndexMap; use legacy_version::LegacyVersionFile; use once_cell::sync::Lazy; use serde_derive::Deserialize; @@ -88,6 +89,10 @@ pub trait ConfigFile: Debug + Send + Sync { static DEFAULT_TASK_CONFIG: Lazy = Lazy::new(TaskConfig::default); &DEFAULT_TASK_CONFIG } + fn vars(&self) -> Result<&IndexMap> { + static DEFAULT_VARS: Lazy> = Lazy::new(IndexMap::new); + Ok(&DEFAULT_VARS) + } } impl dyn ConfigFile { diff --git a/src/config/mod.rs b/src/config/mod.rs index 03916bc87..7da081946 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -44,6 +44,7 @@ pub struct Config { pub project_root: Option, pub all_aliases: AliasMap, pub repo_urls: HashMap, + pub vars: IndexMap, aliases: AliasMap, env: OnceCell, env_with_sources: OnceCell, @@ -97,6 +98,7 @@ impl Config { aliases: load_aliases(&config_files)?, project_root: get_project_root(&config_files), repo_urls: load_plugins(&config_files)?, + vars: load_vars(&config_files)?, config_files, ..Default::default() }; @@ -806,6 +808,17 @@ fn load_plugins(config_files: &ConfigMap) -> Result> { Ok(plugins) } +fn load_vars(config_files: &ConfigMap) -> Result> { + let mut vars = IndexMap::new(); + for config_file in config_files.values() { + for (k, v) in config_file.vars()?.clone() { + vars.insert(k, v); + } + } + trace!("load_vars: {}", vars.len()); + Ok(vars) +} + impl Debug for Config { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { let config_files = self diff --git a/src/task/task_script_parser.rs b/src/task/task_script_parser.rs index 8e5043ee2..dab8cdc77 100644 --- a/src/task/task_script_parser.rs +++ b/src/task/task_script_parser.rs @@ -1,5 +1,7 @@ +use crate::config::CONFIG; use crate::tera::{get_tera, BASE_CONTEXT}; use eyre::Result; +use indexmap::IndexMap; use itertools::Itertools; use std::collections::HashMap; use std::path::PathBuf; @@ -259,6 +261,12 @@ impl TaskScriptParser { }); let mut ctx = BASE_CONTEXT.clone(); ctx.insert("config_root", config_root); + let mut vars = IndexMap::new(); + ctx.insert("vars", &vars); + for (k, v) in &CONFIG.vars { + vars.insert(k.clone(), tera.render_str(v, &ctx).unwrap()); + ctx.insert("vars", &vars); + } let scripts = scripts .iter() .map(|s| tera.render_str(s, &ctx).unwrap())