1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
use std::process::exit;
use console::style;
use eyre::Result;
use crate::cli::args::ForgeArg;
use crate::cmd;
use crate::config::{Config, Settings};
use crate::toolset::ToolsetBuilder;
/// [experimental] Run a task watching for changes
#[derive(Debug, clap::Args)]
#[clap(visible_alias = "w", verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)]
pub struct Watch {
/// Task to run
#[clap(short, long, verbatim_doc_comment, default_value = "default")]
task: Vec<String>,
/// Extra arguments
#[clap(allow_hyphen_values = true)]
args: Vec<String>,
/// Files to watch
/// Defaults to sources from the task(s)
#[clap(short, long, verbatim_doc_comment)]
glob: Vec<String>,
// /// Change to this directory before executing the command
// #[clap(short = 'C', long, value_hint = ValueHint::DirPath, long)]
// pub cd: Option<PathBuf>,
//
// /// Don't actually run the task(s), just print them in order of execution
// #[clap(long, short = 'n', verbatim_doc_comment)]
// pub dry_run: bool,
//
// /// Force the task to run even if outputs are up to date
// #[clap(long, short, verbatim_doc_comment)]
// pub force: bool,
//
// /// Print stdout/stderr by line, prefixed with the task's label
// /// Defaults to true if --jobs > 1
// /// Configure with `task_output` config or `MISE_TASK_OUTPUT` env var
// #[clap(long, short, verbatim_doc_comment, overrides_with = "interleave")]
// pub prefix: bool,
//
// /// Print directly to stdout/stderr instead of by line
// /// Defaults to true if --jobs == 1
// /// Configure with `task_output` config or `MISE_TASK_OUTPUT` env var
// #[clap(long, short, verbatim_doc_comment, overrides_with = "prefix")]
// pub interleave: bool,
//
// /// Tool(s) to also add
// /// e.g.: node@20 python@3.10
// #[clap(short, long, value_name = "TOOL@VERSION")]
// pub tool: Vec<ToolArg>,
//
// /// Number of tasks to run in parallel
// /// [default: 4]
// /// Configure with `jobs` config or `MISE_JOBS` env var
// #[clap(long, short, env = "MISE_JOBS", verbatim_doc_comment)]
// pub jobs: Option<usize>,
//
// /// Read/write directly to stdin/stdout/stderr instead of by line
// /// Configure with `raw` config or `MISE_RAW` env var
// #[clap(long, short, verbatim_doc_comment)]
// pub raw: bool,
}
impl Watch {
pub fn run(self) -> Result<()> {
let config = Config::try_get()?;
let settings = Settings::try_get()?;
let ts = ToolsetBuilder::new().build(&config)?;
settings.ensure_experimental()?;
if let Err(err) = which::which("watchexec") {
let watchexec: ForgeArg = "watchexec".parse()?;
if !ts.versions.contains_key(&watchexec) {
eprintln!("{}: {}", style("Error").red().bold(), err);
eprintln!("{}: Install watchexec with:", style("Hint").bold());
eprintln!(" mise use -g watchexec@latest");
exit(1);
}
}
let tasks = self
.task
.iter()
.map(|t| {
config
.tasks_with_aliases()
.get(t)
.cloned()
.ok_or_else(|| eyre!("Task not found: {t}"))
})
.collect::<Result<Vec<_>>>()?;
let mut args = vec![];
let globs = if self.glob.is_empty() {
tasks
.iter()
.flat_map(|t| t.sources.clone())
.collect::<Vec<_>>()
} else {
self.glob.clone()
};
if !globs.is_empty() {
args.push("-f".to_string());
args.extend(itertools::intersperse(globs, "-f".to_string()).collect::<Vec<_>>());
}
args.extend(self.args.clone());
args.extend(["--".to_string(), "mise".to_string(), "run".to_string()]);
for arg in itertools::intersperse(tasks.iter().map(|t| t.name.as_str()), ":::") {
args.push(arg.to_string());
}
info!("$ watchexec {}", args.join(" "));
let mut cmd = cmd::cmd("watchexec", &args);
for (k, v) in ts.env_with_path(&config) {
cmd = cmd.env(k, v);
}
if let Some(root) = &config.project_root {
cmd = cmd.dir(root);
}
cmd.run()?;
Ok(())
}
}
static AFTER_LONG_HELP: &str = color_print::cstr!(
r#"<bold><underline>Examples:</underline></bold>
$ <bold>mise watch -t build</bold>
Runs the "build" task. Will re-run the task when any of its sources change.
Uses "sources" from the task definition to determine which files to watch.
$ <bold>mise watch -t build --glob src/**/*.rs</bold>
Runs the "build" task but specify the files to watch with a glob pattern.
This overrides the "sources" from the task definition.
$ <bold>mise run -t build --clear</bold>
Extra arguments are passed to watchexec. See `watchexec --help` for details.
"#
);