diff --git a/docs/tasks/task-configuration.md b/docs/tasks/task-configuration.md index fd483e25ae..866242bb2b 100644 --- a/docs/tasks/task-configuration.md +++ b/docs/tasks/task-configuration.md @@ -160,6 +160,20 @@ hide = true run = "echo my internal task" ``` +### `confirm` + +- **Type**: `string` + +A message to show before running the task. This is useful for tasks that are destructive or take a long +time to run. The user will be prompted to confirm before the task is run. + +```toml +[tasks.release] +confirm = "Are you sure you want to cut a release?" +description = 'Cut a new release' +file = 'scripts/release.sh' +``` + ### `raw` - **Type**: `bool` diff --git a/docs/tasks/toml-tasks.md b/docs/tasks/toml-tasks.md index af2c795ca7..ca969d3732 100644 --- a/docs/tasks/toml-tasks.md +++ b/docs/tasks/toml-tasks.md @@ -39,6 +39,7 @@ description = 'Run CI tasks' depends = ['build', 'lint', 'test'] [tasks.release] +confirm = 'Are you sure you want to cut a new release?' description = 'Cut a new release' file = 'scripts/release.sh' # execute an external script ``` @@ -183,6 +184,17 @@ outputs = ['target/debug/mycli'] You can use `sources` alone if with [`mise watch`](/cli/watch.html) to run the task when the sources change. +### Confirmation + +A message to show before running the task. The user will be prompted to confirm before the task is run. + +```toml +[tasks.release] +confirm = 'Are you sure you want to cut a new release?' +description = 'Cut a new release' +file = 'scripts/release.sh' +``` + ## Specifying a shell or an interpreter {#shell-shebang} Tasks are executed with `set -e` (`set -o erropt`) if the shell is `sh`, `bash`, or `zsh`. This means that the script diff --git a/schema/mise-task.json b/schema/mise-task.json index 819413872c..ea010bdab6 100644 --- a/schema/mise-task.json +++ b/schema/mise-task.json @@ -37,6 +37,10 @@ } ] }, + "confirm": { + "description": "confirmation message before running this task", + "type": "string" + }, "depends": { "oneOf": [ { diff --git a/schema/mise.json b/schema/mise.json index a1b1260747..77b9ff8ab6 100644 --- a/schema/mise.json +++ b/schema/mise.json @@ -1013,6 +1013,10 @@ } ] }, + "confirm": { + "description": "confirmation message before running this task", + "type": "string" + }, "depends": { "oneOf": [ { diff --git a/src/cli/run.rs b/src/cli/run.rs index d1acd44427..aa8f70f9d9 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -430,6 +430,11 @@ impl Run { } return Ok(()); } + if let Some(message) = &task.confirm { + if !ui::confirm(message).unwrap_or(true) { + return Err(eyre!("task cancelled")); + } + } let config = Config::get(); let mut tools = self.tool.clone(); diff --git a/src/task/mod.rs b/src/task/mod.rs index 7335a4994c..d3de118d93 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -55,6 +55,8 @@ pub struct Task { pub cf: Option>, #[serde(skip)] pub config_root: Option, + #[serde(default)] + pub confirm: Option, #[serde(default, deserialize_with = "deserialize_arr")] pub depends: Vec, #[serde(default, deserialize_with = "deserialize_arr")] @@ -177,6 +179,7 @@ impl Task { .or(p.parse_str("alias").map(|s| vec![s])) .or(p.parse_str("aliases").map(|s| vec![s])) .unwrap_or_default(); + task.confirm = p.parse_str("confirm"); task.description = p.parse_str("description").unwrap_or_default(); task.sources = p.parse_array("sources").unwrap_or_default(); task.outputs = info.get("outputs").map(|to| to.into()).unwrap_or_default(); @@ -577,6 +580,7 @@ impl Default for Task { config_source: PathBuf::new(), cf: None, config_root: None, + confirm: None, depends: vec![], depends_post: vec![], wait_for: vec![],