diff --git a/.mise.toml b/.mise.toml
index 1798e6262..7802a18bb 100644
--- a/.mise.toml
+++ b/.mise.toml
@@ -22,6 +22,7 @@ jq = "latest"
"npm:prettier" = "3"
direnv = "latest"
actionlint = "latest"
+ripgrep = "latest"
"pipx:toml-sort" = "latest"
#python = { version = "latest", virtualenv = "{{env.HOME}}/.cache/venv" }
#ruby = "3.1"
diff --git a/.mise/tasks/lint/ripgrep b/.mise/tasks/lint/ripgrep
new file mode 100755
index 000000000..6ebb254fb
--- /dev/null
+++ b/.mise/tasks/lint/ripgrep
@@ -0,0 +1,10 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+found=0
+rg "dbg!" src && found=1
+
+if [[ $found == 1 ]]; then
+ echo "dbg! macro found"
+ exit 1
+fi
diff --git a/docs/configuration.md b/docs/configuration.md
index de3d586ef..a6779a0d9 100644
--- a/docs/configuration.md
+++ b/docs/configuration.md
@@ -247,6 +247,10 @@ version files since they're version files not specific to asdf/mise and can be u
See [Settings](/settings) for the full list of settings.
+## Tasks
+
+See [Tasks](/tasks/) for the full list of configuration options.
+
## Environment variables
mise can also be configured via environment variables. The following options are available:
diff --git a/docs/tasks/file-tasks.md b/docs/tasks/file-tasks.md
index 4ff4e7ee7..8f72ff099 100644
--- a/docs/tasks/file-tasks.md
+++ b/docs/tasks/file-tasks.md
@@ -67,7 +67,7 @@ test:integration .../.mise/tasks/test/integration
test:units .../.mise/tasks/test/units
```
-### Argument parsing with usage
+## Argument parsing with usage
[usage](https://usage.jdx.dev) spec can be used within these files to provide argument parsing, autocompletion,
documentation when running mise and can be exported to markdown. Essentially this turns tasks into
diff --git a/docs/tasks/index.md b/docs/tasks/index.md
index 265980fd3..6309716f6 100644
--- a/docs/tasks/index.md
+++ b/docs/tasks/index.md
@@ -1,8 +1,8 @@
# Tasks
-You can define tasks in `.mise.toml` files or as standalone shell scripts. These are useful for things like
+You can define tasks in `mise.toml` files or as standalone shell scripts. These are useful for things like
running linters, tests, builders, servers, and other tasks that are specific to a project. Of course,
-tasks launched with mise will include the mise environment—your tools and env vars defined in `.mise.toml`.
+tasks launched with mise will include the mise environment—your tools and env vars defined in `mise.toml`.
Here's my favorite features about mise's task runner:
@@ -19,4 +19,30 @@ Please give feedback early since while it's experimental it's much easier to cha
## Task Environment Variables
-- `root` - the root of the project, defaults to the directory of the `.mise.toml` file
+- `root` - the root of the project, defaults to the directory of the `mise.toml` file
+
+## Task Configuration
+
+You can configure how tasks are used in mise with the `[task_config]` section of `mise.toml`:
+
+```toml
+[task_config]
+
+# add toml files containing toml tasks, or file tasks to include when looking for tasks
+includes = [
+ "tasks.toml", # a task toml file
+ "mytasks" # a directory containing file tasks
+]
+```
+
+If using a toml file for tasks, the file should be the same format as the `[tasks]` section of `mise.toml`
+but without the `[task]` prefix:
+
+```toml
+task1 = "echo task1"
+task2 = "echo task2"
+task3 = "echo task3"
+
+[task4]
+run = "echo task4"
+```
diff --git a/docs/tasks/toml-tasks.md b/docs/tasks/toml-tasks.md
index d12c5a67e..171bfbd85 100644
--- a/docs/tasks/toml-tasks.md
+++ b/docs/tasks/toml-tasks.md
@@ -1,6 +1,6 @@
# TOML-based Tasks
-Tasks can also be defined in `.mise.toml` files in different ways. This is a more "traditional" method of defining tasks:
+Tasks can be defined in `mise.toml` files in different ways:
```toml
[tasks.cleancache]
@@ -64,6 +64,11 @@ 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.
+
+:::tip
+Using templates to define arguments will make them work with completion and help messages.
+:::
### Positional Arguments
diff --git a/e2e/tasks/test_task_ls b/e2e/tasks/test_task_ls
new file mode 100644
index 000000000..674c8be9a
--- /dev/null
+++ b/e2e/tasks/test_task_ls
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+
+cat <mise.toml
+[task_config]
+includes = [
+ "tasks.toml",
+ "mytasks",
+]
+[tasks.lint]
+run = 'echo "linting!"'
+EOF
+
+cat <tasks.toml
+[test]
+run = 'echo "testing!"'
+[test-with-args]
+run = 'echo "{{arg()}} {{flag(name="force")}} {{option(name="user")}}"'
+EOF
+
+mkdir -p .mise/tasks
+cat <<'EOF' >.mise/tasks/do-not-show
+#!/usr/bin/env bash
+EOF
+chmod +x .mise/tasks/do-not-show
+
+mkdir -p mytasks
+cat <<'EOF' >mytasks/filetask2
+#!/usr/bin/env bash
+EOF
+chmod +x mytasks/filetask2
+
+assert "mise task ls" "filetask2 ~/workdir/mytasks/filetask2
+lint ~/workdir/mise.toml
+test ~/workdir/tasks.toml
+test-with-args ~/workdir/tasks.toml"
diff --git a/e2e/cli/test_run b/e2e/tasks/test_task_run
similarity index 100%
rename from e2e/cli/test_run
rename to e2e/tasks/test_task_run
diff --git a/schema/mise.json b/schema/mise.json
index 735bec1a0..1bab0cb7b 100644
--- a/schema/mise.json
+++ b/schema/mise.json
@@ -563,6 +563,21 @@
}
]
},
+ "task_config": {
+ "description": "configration for task execution/management",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "includes": {
+ "description": "files/directories to include searching for tasks",
+ "items": {
+ "description": "file/directory root to include in task execution",
+ "type": "string"
+ },
+ "type": "array"
+ }
+ }
+ },
"tool": {
"oneOf": [
{
diff --git a/schema/mise.json.hbs b/schema/mise.json.hbs
index 907b8552d..0e0d22e1f 100644
--- a/schema/mise.json.hbs
+++ b/schema/mise.json.hbs
@@ -233,6 +233,21 @@
}
]
},
+ "task_config": {
+ "description": "configration for task execution/management",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "includes": {
+ "description": "files/directories to include searching for tasks",
+ "items": {
+ "description": "file/directory root to include in task execution",
+ "type": "string"
+ },
+ "type": "array"
+ }
+ }
+ },
"tool": {
"oneOf": [
{
diff --git a/src/cli/tasks/ls.rs b/src/cli/tasks/ls.rs
index 4e292a0c6..a0e310778 100644
--- a/src/cli/tasks/ls.rs
+++ b/src/cli/tasks/ls.rs
@@ -6,6 +6,7 @@ use tabled::Tabled;
use crate::config::{Config, Settings};
use crate::file::display_path;
use crate::task::Task;
+use crate::ui::info::trim_line_end_whitespace;
use crate::ui::{style, table};
/// [experimental] List available tasks to execute
@@ -91,7 +92,8 @@ impl TasksLs {
if !self.extended {
table::disable_columns(&mut table, vec![1]);
}
- miseprintln!("{table}");
+ let table = format!("{table}");
+ miseprintln!("{}", trim_line_end_whitespace(&table));
Ok(())
}
@@ -191,12 +193,12 @@ mod tests {
#[test]
fn test_task_ls() {
reset();
- assert_cli_snapshot!("t", "--no-headers", @r###"
- configtask ~/config/config.toml
- filetask This is a test build script ~/cwd/.mise/tasks/filetask
- lint ~/config/config.toml
+ assert_cli_snapshot!("t", "--no-headers", @r#"
+ configtask ~/config/config.toml
+ filetask This is a test build script ~/cwd/.mise/tasks/filetask
+ lint ~/config/config.toml
test ~/config/config.toml
- "###);
+ "#);
}
#[test]
diff --git a/src/cli/toml/mod.rs b/src/cli/toml/mod.rs
index 2e29766e2..6ce44cd0d 100644
--- a/src/cli/toml/mod.rs
+++ b/src/cli/toml/mod.rs
@@ -1,6 +1,7 @@
use crate::config::{load_config_paths, DEFAULT_CONFIG_FILENAMES};
+use crate::file;
use clap::Subcommand;
-use eyre::Result;
+use eyre::{bail, Result};
use once_cell::sync::Lazy;
use std::path::PathBuf;
@@ -47,8 +48,15 @@ impl Commands {
impl Toml {
pub fn run(self) -> Result<()> {
- let cmd = self.command.unwrap();
+ if let Some(cmd) = self.command {
+ return cmd.run();
+ }
+ if let Some(toml_config) = top_toml_config() {
+ miseprintln!("{}", file::read_to_string(&toml_config)?);
+ } else {
+ bail!("No mise.toml file found");
+ }
- cmd.run()
+ Ok(())
}
}
diff --git a/src/config/mod.rs b/src/config/mod.rs
index b4ebd17dc..412db261f 100644
--- a/src/config/mod.rs
+++ b/src/config/mod.rs
@@ -244,18 +244,40 @@ impl Config {
let includes = configs
.iter()
.find_map(|cf| cf.task_config().includes.clone())
- .unwrap_or_else(default_task_includes);
+ .unwrap_or_else(default_task_includes)
+ .into_iter()
+ .map(|p| if p.is_absolute() { p } else { dir.join(p) })
+ .collect_vec();
+ let extra_tasks = includes
+ .iter()
+ .filter(|p| {
+ p.is_file() && p.extension().unwrap_or_default().to_string_lossy() == "toml"
+ })
+ .map(|p| self.load_task_file(p))
+ .flatten_ok()
+ .collect::>>()?;
let file_tasks = includes.into_iter().flat_map(|p| {
- let p = match p.is_absolute() {
- true => p,
- false => dir.join(p),
- };
self.load_tasks_includes(&p).unwrap_or_else(|err| {
warn!("loading tasks in {}: {err}", display_path(&p));
vec![]
})
});
- Ok(file_tasks.into_iter().chain(config_tasks).collect())
+ Ok(file_tasks
+ .into_iter()
+ .chain(config_tasks)
+ .chain(extra_tasks)
+ .collect())
+ }
+
+ fn load_task_file(&self, path: &Path) -> Result> {
+ let raw = file::read_to_string(path)?;
+ let mut tasks = toml::from_str::>(&raw)
+ .map_err(|e| eyre!("Error parsing task file: {} {e}", display_path(path)))?;
+ for (name, task) in &mut tasks {
+ task.name = name.clone();
+ task.config_source = path.to_path_buf();
+ }
+ Ok(tasks.into_values().collect())
}
fn load_global_tasks(&self) -> Result> {
@@ -313,14 +335,14 @@ impl Config {
if !root.is_dir() {
return Ok(vec![]);
}
- let files: Vec = WalkDir::new(root)
+ WalkDir::new(root)
.follow_links(true)
.into_iter()
- .filter_entry(|e| !e.file_name().to_string_lossy().starts_with('.'))
+ // skip hidden directories (if the root is hidden that's ok)
+ .filter_entry(|e| e.path() == root || !e.file_name().to_string_lossy().starts_with('.'))
.filter_ok(|e| e.file_type().is_file())
.map_ok(|e| e.path().to_path_buf())
- .try_collect()?;
- files
+ .try_collect::<_, Vec, _>()?
.into_par_iter()
.filter(|p| file::is_executable(p))
.map(|path| Task::from_path(&path))
diff --git a/src/ui/info.rs b/src/ui/info.rs
index c20d7666a..9cf5c4a08 100644
--- a/src/ui/info.rs
+++ b/src/ui/info.rs
@@ -23,6 +23,6 @@ pub fn indent_by(s: S, ind: &'static str) -> String {
out
}
-fn trim_line_end_whitespace(s: &str) -> String {
+pub fn trim_line_end_whitespace(s: &str) -> String {
s.lines().map(str::trim_end).collect::>().join("\n")
}
diff --git a/tasks b/tasks
new file mode 120000
index 000000000..1d02a36bd
--- /dev/null
+++ b/tasks
@@ -0,0 +1 @@
+.mise/tasks
\ No newline at end of file