diff --git a/completions/_rtx b/completions/_rtx index be6324a37..c13bd2acc 100644 --- a/completions/_rtx +++ b/completions/_rtx @@ -53,6 +53,7 @@ _rtx() { (up|upgrade) __rtx_upgrade_cmd && ret=0 ;; (u|use) __rtx_use_cmd && ret=0 ;; (v|version) __rtx_version_cmd && ret=0 ;; + (w|watch) __rtx_watch_cmd && ret=0 ;; (where) __rtx_where_cmd && ret=0 ;; (which) __rtx_which_cmd && ret=0 ;; esac @@ -530,7 +531,7 @@ __rtx_reshim_cmd() { (( $+functions[__rtx_run_cmd] )) || __rtx_run_cmd() { _arguments -s -S \ - ':task:__rtx_tasks' \ + '::task:__rtx_tasks' \ '*::args:' \ '(-C --cd)'{-C,--cd}'=[Change to this directory before executing the command]:cd:_directories' \ '(-n --dry-run)'{-n,--dry-run}'[Don'\''t actually run the task(s), just print them in order of execution]' \ @@ -687,6 +688,7 @@ return ret __rtx_task_edit_cmd() { _arguments -s -S \ ':task:__rtx_tasks' \ + '(-p --path)'{-p,--path}'[Display the path to the task instead of editing it]' \ '(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \ '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ '(-y --yes)'{-y,--yes}'[Answer yes to all prompts]' @@ -703,7 +705,7 @@ __rtx_task_ls_cmd() { (( $+functions[__rtx_task_run_cmd] )) || __rtx_task_run_cmd() { _arguments -s -S \ - ':task:__rtx_tasks' \ + '::task:__rtx_tasks' \ '*::args:' \ '(-C --cd)'{-C,--cd}'=[Change to this directory before executing the command]:cd:_directories' \ '(-n --dry-run)'{-n,--dry-run}'[Don'\''t actually run the task(s), just print them in order of execution]' \ @@ -773,6 +775,16 @@ __rtx_version_cmd() { '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ '(-y --yes)'{-y,--yes}'[Answer yes to all prompts]' } +(( $+functions[__rtx_watch_cmd] )) || +__rtx_watch_cmd() { + _arguments -s -S \ + '*'{-t,--task}'=[Task to run]:task:__rtx_tasks' \ + '*::args:' \ + '*'{-g,--glob}'=[Files to watch]:glob:' \ + '(-q --quiet)'{-q,--quiet}'[Suppress non-error messages]' \ + '*'{-v,--verbose}'[Show extra output (use -vv for even more)]' \ + '(-y --yes)'{-y,--yes}'[Answer yes to all prompts]' +} (( $+functions[__rtx_where_cmd] )) || __rtx_where_cmd() { _arguments -s -S \ @@ -829,6 +841,7 @@ __rtx_cmds() { {up,upgrade}':Upgrades outdated tool versions' {u,use}':Change the active version of a tool locally or globally.' 'version:Show rtx version' + {w,watch}':\[experimental\] Run a task watching for changes' 'where:Display the installation path for a runtime' 'which:Shows the path that a bin name points to' ) @@ -900,7 +913,7 @@ __rtx_sync_cmds() { __rtx_task_cmds() { local commands; commands=( 'edit:\[experimental\] Edit a task with \$EDITOR' - 'ls:\[experimental\] List config files currently in use' + 'ls:\[experimental\] List available tasks to execute' {r,run}':\[experimental\] Run a task' ) _describe -t commands 'command' commands "$@" diff --git a/completions/rtx.bash b/completions/rtx.bash index f01ab7ad6..fceb30e7e 100644 --- a/completions/rtx.bash +++ b/completions/rtx.bash @@ -180,6 +180,12 @@ _rtx() { rtx,version) cmd="rtx__version" ;; + rtx,w) + cmd="rtx__watch" + ;; + rtx,watch) + cmd="rtx__watch" + ;; rtx,where) cmd="rtx__where" ;; @@ -426,6 +432,9 @@ _rtx() { rtx__help,version) cmd="rtx__help__version" ;; + rtx__help,watch) + cmd="rtx__help__watch" + ;; rtx__help,where) cmd="rtx__help__where" ;; @@ -682,7 +691,7 @@ _rtx() { case "${cmd}" in rtx) - opts="-q -v -y -h -V --debug --log-level --trace --quiet --verbose --yes --help --version activate alias asdf bin-paths cache completion config current deactivate direnv doctor env env-vars exec global hook-env implode install latest link local ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version where which render-completion render-help render-mangen help" + opts="-q -v -y -h -V --debug --log-level --trace --quiet --verbose --yes --help --version activate alias asdf bin-paths cache completion config current deactivate direnv doctor env env-vars exec global hook-env implode install latest link local ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version watch where which render-completion render-help render-mangen help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -1478,7 +1487,7 @@ _rtx() { return 0 ;; rtx__help) - opts="activate alias asdf bin-paths cache completion config current deactivate direnv doctor env env-vars exec global hook-env implode install latest link local ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version where which render-completion render-help render-mangen help" + opts="activate alias asdf bin-paths cache completion config current deactivate direnv doctor env env-vars exec global hook-env implode install latest link local ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version watch where which render-completion render-help render-mangen help" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -2429,6 +2438,20 @@ _rtx() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + rtx__help__watch) + opts="" + if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; rtx__help__where) opts="" if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then @@ -2996,7 +3019,7 @@ _rtx() { return 0 ;; rtx__run) - opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --debug --log-level --trace --quiet --verbose --yes --help [ARGS]..." + opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --debug --log-level --trace --quiet --verbose --yes --help [TASK] [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3384,7 +3407,7 @@ _rtx() { return 0 ;; rtx__task__edit) - opts="-q -v -y -h --debug --log-level --trace --quiet --verbose --yes --help " + opts="-p -q -v -y -h --path --debug --log-level --trace --quiet --verbose --yes --help " if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3490,7 +3513,7 @@ _rtx() { return 0 ;; rtx__task__run) - opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --debug --log-level --trace --quiet --verbose --yes --help [ARGS]..." + opts="-C -n -f -p -i -t -j -r -q -v -y -h --cd --dry-run --force --prefix --interleave --tool --jobs --raw --debug --log-level --trace --quiet --verbose --yes --help [TASK] [ARGS]..." if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 @@ -3657,6 +3680,40 @@ _rtx() { COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) return 0 ;; + rtx__watch) + opts="-t -g -q -v -y -h --task --glob --debug --log-level --trace --quiet --verbose --yes --help [ARGS]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --task) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -t) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --glob) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -g) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --log-level) + COMPREPLY=($(compgen -W "error warn info debug trace" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; rtx__where) opts="-q -v -y -h --debug --log-level --trace --quiet --verbose --yes --help [ASDF_VERSION]" if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then diff --git a/completions/rtx.fish b/completions/rtx.fish index 79665743b..a13b2d04c 100644 --- a/completions/rtx.fish +++ b/completions/rtx.fish @@ -4,7 +4,7 @@ set -l fssf "__fish_seen_subcommand_from" complete -kxc rtx -s q -l quiet -d 'Suppress non-error messages' complete -kxc rtx -s v -l verbose -d 'Show extra output (use -vv for even more)' complete -kxc rtx -s y -l yes -d 'Answer yes to all prompts' -set -l others activate alias bin-paths cache completion config current deactivate direnv doctor env env-vars exec implode install latest link ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version where which +set -l others activate alias bin-paths cache completion config current deactivate direnv doctor env env-vars exec implode install latest link ls ls-remote outdated plugins prune reshim run self-update settings shell sync task trust uninstall upgrade use version watch where which complete -xc rtx -n "not $fssf $others" -a activate -d 'Initializes rtx in the current shell session' complete -xc rtx -n "not $fssf $others" -a alias -d 'Manage aliases' complete -xc rtx -n "not $fssf $others" -a bin-paths -d 'List all the active runtime bin paths' @@ -39,6 +39,7 @@ complete -xc rtx -n "not $fssf $others" -a uninstall -d 'Removes runtime version complete -xc rtx -n "not $fssf $others" -a upgrade -d 'Upgrades outdated tool versions' complete -xc rtx -n "not $fssf $others" -a use -d 'Change the active version of a tool locally or globally.' complete -xc rtx -n "not $fssf $others" -a version -d 'Show rtx version' +complete -xc rtx -n "not $fssf $others" -a watch -d '[experimental] Run a task watching for changes' complete -xc rtx -n "not $fssf $others" -a where -d 'Display the installation path for a runtime' complete -xc rtx -n "not $fssf $others" -a which -d 'Shows the path that a bin name points to' @@ -220,7 +221,7 @@ complete -kxc rtx -n "$fssf prune" -a "(__rtx_plugins)" -d 'Prune only versions # reshim # run -complete -kxc rtx -n "$fssf run" -d 'Arguments to pass to the task' +complete -kxc rtx -n "$fssf run" -d 'Arguments to pass to the task. Use ":::" to separate tasks' complete -kxc rtx -n "$fssf run" -s C -l cd -a "(__fish_complete_directories)" -d 'Change to this directory before executing the command' complete -kxc rtx -n "$fssf run" -s n -l dry-run -d 'Don'\''t actually run the task(s), just print them in order of execution' complete -kxc rtx -n "$fssf run" -s f -l force -d 'Force the task to run even if outputs are up to date' @@ -228,7 +229,7 @@ complete -kxc rtx -n "$fssf run" -s i -l interleave -d 'Print directly to stdout complete -kxc rtx -n "$fssf run" -s j -l jobs -d 'Number of tasks to run in parallel' complete -kxc rtx -n "$fssf run" -s p -l prefix -d 'Print stdout/stderr by line, prefixed with the task'\''s label' complete -kxc rtx -n "$fssf run" -s r -l raw -d 'Read/write directly to stdin/stdout/stderr instead of by line' -complete -kxc rtx -n "$fssf run" -a "(__rtx_tasks)" -d 'Task to run Can specify multiple tasks by separating with `:::` e.g.: rtx run task1 arg1 arg2 ::: task2 arg1 arg2' +complete -kxc rtx -n "$fssf run" -a "(__rtx_tasks)" -d 'Task to run' complete -kxc rtx -n "$fssf run" -s t -l tool -a "(__rtx_tool_versions)" -d 'Tool(s) to also add e.g.: node@20 python@3.10' # self-update @@ -282,10 +283,11 @@ complete -kxc rtx -n "$fssf task" -l hidden -d 'Show hidden tasks' complete -kxc rtx -n "$fssf task" -l no-header -d 'Do not print table header' set -l others edit ls run complete -xc rtx -n "$fssf task; and not $fssf $others" -a edit -d '[experimental] Edit a task with $EDITOR' -complete -xc rtx -n "$fssf task; and not $fssf $others" -a ls -d '[experimental] List config files currently in use' +complete -xc rtx -n "$fssf task; and not $fssf $others" -a ls -d '[experimental] List available tasks to execute' complete -xc rtx -n "$fssf task; and not $fssf $others" -a run -d '[experimental] Run a task' # task edit +complete -kxc rtx -n "$fssf task; and $fssf edit" -s p -l path -d 'Display the path to the task instead of editing it' complete -kxc rtx -n "$fssf task; and $fssf edit" -a "(__rtx_tasks)" -d 'Task to edit' # task ls @@ -293,7 +295,7 @@ complete -kxc rtx -n "$fssf task; and $fssf ls" -l hidden -d 'Show hidden tasks' complete -kxc rtx -n "$fssf task; and $fssf ls" -l no-header -d 'Do not print table header' # task run -complete -kxc rtx -n "$fssf task; and $fssf run" -d 'Arguments to pass to the task' +complete -kxc rtx -n "$fssf task; and $fssf run" -d 'Arguments to pass to the task. Use ":::" to separate tasks' complete -kxc rtx -n "$fssf task; and $fssf run" -s C -l cd -a "(__fish_complete_directories)" -d 'Change to this directory before executing the command' complete -kxc rtx -n "$fssf task; and $fssf run" -s n -l dry-run -d 'Don'\''t actually run the task(s), just print them in order of execution' complete -kxc rtx -n "$fssf task; and $fssf run" -s f -l force -d 'Force the task to run even if outputs are up to date' @@ -301,7 +303,7 @@ complete -kxc rtx -n "$fssf task; and $fssf run" -s i -l interleave -d 'Print di complete -kxc rtx -n "$fssf task; and $fssf run" -s j -l jobs -d 'Number of tasks to run in parallel' complete -kxc rtx -n "$fssf task; and $fssf run" -s p -l prefix -d 'Print stdout/stderr by line, prefixed with the task'\''s label' complete -kxc rtx -n "$fssf task; and $fssf run" -s r -l raw -d 'Read/write directly to stdin/stdout/stderr instead of by line' -complete -kxc rtx -n "$fssf task; and $fssf run" -a "(__rtx_tasks)" -d 'Task to run Can specify multiple tasks by separating with `:::` e.g.: rtx run task1 arg1 arg2 ::: task2 arg1 arg2' +complete -kxc rtx -n "$fssf task; and $fssf run" -a "(__rtx_tasks)" -d 'Task to run' complete -kxc rtx -n "$fssf task; and $fssf run" -s t -l tool -a "(__rtx_tool_versions)" -d 'Tool(s) to also add e.g.: node@20 python@3.10' @@ -336,6 +338,11 @@ complete -kxc rtx -n "$fssf use" -a "(__rtx_tool_versions)" -d 'Tool(s) to add t # version +# watch +complete -kxc rtx -n "$fssf watch" -d 'Extra arguments' +complete -kxc rtx -n "$fssf watch" -s g -l glob -d 'Files to watch' +complete -kxc rtx -n "$fssf watch" -s t -l task -a "(__rtx_tasks)" -d 'Task to run' + # where complete -kxc rtx -n "$fssf where" -a "(__rtx_tool_versions)" -d 'Tool(s) to look up' diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 211d3e3df..b86181736 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -50,6 +50,7 @@ mod uninstall; mod upgrade; mod r#use; pub mod version; +mod watch; mod r#where; mod r#which; @@ -95,6 +96,7 @@ pub enum Commands { Upgrade(upgrade::Upgrade), Use(r#use::Use), Version(version::Version), + Watch(watch::Watch), Where(r#where::Where), Which(which::Which), @@ -149,6 +151,7 @@ impl Commands { Self::Upgrade(cmd) => cmd.run(), Self::Use(cmd) => cmd.run(), Self::Version(cmd) => cmd.run(), + Self::Watch(cmd) => cmd.run(), Self::Where(cmd) => cmd.run(), Self::Which(cmd) => cmd.run(), diff --git a/src/cli/run.rs b/src/cli/run.rs index bfeeabd07..4380312c2 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -31,15 +31,39 @@ use crate::ui::style; use crate::{env, file, ui}; /// [experimental] Run a task +/// +/// This command will run a task, or multiple tasks in parallel. +/// Tasks may have dependencies on other tasks or on source files. +/// If source is configured on a task, it will only run if the source +/// files have changed. +/// +/// Tasks can be defined in .rtx.toml or as standalone scripts. +/// In .rtx.toml, tasks take this form: +/// +/// [tasks.build] +/// run = "npm run build" +/// sources = ["src/**/*.ts"] +/// outputs = ["dist/**/*.js"] +/// +/// Alternatively, tasks can be defined as standalone scripts. +/// These must be located in the `.rtx/tasks` directory. +/// The name of the script will be the name of the task. +/// +/// $ cat .rtx/tasks/build<, @@ -156,9 +180,6 @@ impl Run { } fn run_task(&self, config: &Config, env: &BTreeMap, task: &Task) -> Result<()> { - if self.dry_run { - return Ok(()); - } let prefix = style::estyle(task.prefix()).fg(get_color()).to_string(); if !self.force && self.sources_are_fresh(config, task) { info_unprefix_trunc!("{prefix} sources up-to-date, skipping"); @@ -250,6 +271,9 @@ impl Run { if let Some(cd) = &self.cd.as_ref().or(task.dir.as_ref()) { cmd = cmd.current_dir(cd); } + if self.dry_run { + return Ok(()); + } if let Err(err) = cmd.execute() { if let Some(ScriptFailed(_, Some(status))) = err.downcast_ref::() { if let Some(code) = status.code() { @@ -370,8 +394,22 @@ impl Run { static AFTER_LONG_HELP: &str = color_print::cstr!( r#"Examples: + $ rtx run lint + Runs the "lint" task. This needs to either be defined in .rtx.toml + or as a standalone script. See the project README for more information. + + $ rtx run build --force + Forces the "build" task to run even if its sources are up-to-date. + + $ rtx run test --raw + Runs "test" with stdin/stdout/stderr all connected to the current terminal. + This forces `--jobs=1` to prevent interleaving of output. + + $ rtx run lint ::: test ::: check + Runs the "lint", "test", and "check" tasks in parallel. + $ rtx task cmd1 arg1 arg2 ::: cmd2 arg1 arg2 - TODO + Execute multiple tasks each with their own arguments. "# ); diff --git a/src/cli/task/edit.rs b/src/cli/task/edit.rs index 94d82269c..d13f4b8e2 100644 --- a/src/cli/task/edit.rs +++ b/src/cli/task/edit.rs @@ -5,12 +5,18 @@ use crate::task::Task; use crate::{env, file}; /// [experimental] Edit a task with $EDITOR +/// +/// The task will be created as a standalone script if it does not already exist. #[derive(Debug, clap::Args)] -#[clap(verbatim_doc_comment)] +#[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)] pub struct TaskEdit { /// Task to edit #[clap()] task: String, + + /// Display the path to the task instead of editing it + #[clap(long, short, verbatim_doc_comment)] + path: bool, } impl TaskEdit { @@ -37,8 +43,19 @@ impl TaskEdit { file::create(file)?; file::make_executable(file)?; } - cmd!(&*env::EDITOR, &file).run()?; + if self.path { + rtxprintln!("{}", file.display()); + } else { + cmd!(&*env::EDITOR, &file).run()?; + } Ok(()) } } + +static AFTER_LONG_HELP: &str = color_print::cstr!( + r#"Examples: + $ rtx task edit build + $ rtx task edit test +"# +); diff --git a/src/cli/task/ls.rs b/src/cli/task/ls.rs index fa2df8728..a62b37491 100644 --- a/src/cli/task/ls.rs +++ b/src/cli/task/ls.rs @@ -9,7 +9,13 @@ use crate::config::{Config, Settings}; use crate::file::display_path; use crate::ui::{style, table}; -/// [experimental] List config files currently in use +/// [experimental] List available tasks to execute +/// These may be included from the config file or from the project's .rtx/tasks directory +/// rtx will merge all tasks from all parent directories into this list. +/// +/// So if you have global tasks in ~/.config/rtx/tasks/* and project-specific tasks in +/// ~/myproject/.rtx/tasks/*, then they'll both be available but the project-specific +/// tasks will override the global ones if they have the same name. #[derive(Debug, clap::Args)] #[clap(verbatim_doc_comment, after_long_help = AFTER_LONG_HELP)] pub struct TaskLs { diff --git a/src/cli/watch.rs b/src/cli/watch.rs new file mode 100644 index 000000000..f10aee472 --- /dev/null +++ b/src/cli/watch.rs @@ -0,0 +1,138 @@ +use std::process::exit; + +use console::style; + +use eyre::Result; + +use crate::config::{Config, Settings}; + +use crate::cmd; +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, + + /// Extra arguments + #[clap(allow_hyphen_values = true)] + args: Vec, + + /// Files to watch + /// Defaults to sources from the task(s) + #[clap(short, long, verbatim_doc_comment)] + glob: Vec, + // /// Change to this directory before executing the command + // #[clap(short = 'C', long, value_hint = ValueHint::DirPath, long)] + // pub cd: Option, + // + // /// 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 `RTX_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 `RTX_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", value_parser = ToolArgParser)] + // pub tool: Vec, + // + // /// Number of tasks to run in parallel + // /// [default: 4] + // /// Configure with `jobs` config or `RTX_JOBS` env var + // #[clap(long, short, env = "RTX_JOBS", verbatim_doc_comment)] + // pub jobs: Option, + // + // /// Read/write directly to stdin/stdout/stderr instead of by line + // /// Configure with `raw` config or `RTX_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") { + if !ts.versions.contains_key("watchexec") { + eprintln!("{}: {}", style("Error").red().bold(), err); + eprintln!("{}: Install watchexec with:", style("Hint").bold()); + eprintln!(" rtx use -g watchexec@latest"); + exit(1); + } + } + let tasks = self + .task + .iter() + .map(|t| { + config + .tasks() + .get(t) + .cloned() + .ok_or_else(|| eyre!("Task not found: {t}")) + }) + .collect::>>()?; + let mut args = vec![]; + let globs = if self.glob.is_empty() { + tasks + .iter() + .flat_map(|t| t.sources.clone()) + .collect::>() + } else { + self.glob.clone() + }; + if !globs.is_empty() { + args.push("-f".to_string()); + args.extend(itertools::intersperse(globs, "-f".to_string()).collect::>()); + } + args.extend(self.args.clone()); + args.extend(["--".to_string(), "rtx".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#"Examples: + $ rtx watch -t build + 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. + + $ rtx watch -t build --glob src/**/*.rs + Runs the "build" task but specify the files to watch with a glob pattern. + This overrides the "sources" from the task definition. + + $ rtx run -t build --clear + Extra arguments are passed to watchexec. See `watchexec --help` for details. +"# +); diff --git a/src/config/settings.rs b/src/config/settings.rs index 708aa8919..352fa6e68 100644 --- a/src/config/settings.rs +++ b/src/config/settings.rs @@ -83,7 +83,6 @@ impl Settings { } let mut settings = Self::default_builder().load()?; if settings.raw { - settings.verbose = true; settings.jobs = 1; } if settings.debug {