From 1c44d8049a9770c420ab17f0fff29f619fd36286 Mon Sep 17 00:00:00 2001 From: ilewin Date: Thu, 8 Sep 2022 19:22:44 +0200 Subject: [PATCH 1/6] Issue 813. Made watch interval configurable through global setting in Taskfile and through CLI arg. Separated Taskfile param and Arg flag --- cmd/task/task.go | 7 +++ task.go | 1 + task_test.go | 61 +++++++++++++++++++++++++ taskfile/taskfile.go | 6 +++ testdata/watcher_interval/.gitignore | 1 + testdata/watcher_interval/Taskfile.yaml | 16 +++++++ watch.go | 31 ++++++++++++- 7 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 testdata/watcher_interval/.gitignore create mode 100644 testdata/watcher_interval/Taskfile.yaml diff --git a/cmd/task/task.go b/cmd/task/task.go index 5a3893b8..abee37dd 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -73,6 +73,7 @@ func main() { entrypoint string output taskfile.Output color bool + interval string ) pflag.BoolVar(&versionFlag, "version", false, "show Task version") @@ -96,6 +97,7 @@ func main() { pflag.StringVar(&output.Group.End, "output-group-end", "", "message template to print after a task's grouped output") pflag.BoolVarP(&color, "color", "c", true, "colored output. Enabled by default. Set flag to false or use NO_COLOR=1 to disable") pflag.IntVarP(&concurrency, "concurrency", "C", 0, "limit number tasks to run concurrently") + pflag.StringVarP(&interval, "interval", "I", "5s", "interval to watch for changes") pflag.Parse() if versionFlag { @@ -151,6 +153,7 @@ func main() { Parallel: parallel, Color: color, Concurrency: concurrency, + Interval: interval, Stdin: os.Stdin, Stdout: os.Stdout, @@ -206,6 +209,10 @@ func main() { e.InterceptInterruptSignals() } + if e.Interval == "" { + e.Interval = strings.TrimSpace(interval) + } + ctx := context.Background() if status { diff --git a/task.go b/task.go index 02c8e383..e3b3b6d1 100644 --- a/task.go +++ b/task.go @@ -41,6 +41,7 @@ type Executor struct { Parallel bool Color bool Concurrency int + Interval string Stdin io.Reader Stdout io.Writer diff --git a/task_test.go b/task_test.go index d26ea0f8..7ce4e19a 100644 --- a/task_test.go +++ b/task_test.go @@ -10,6 +10,7 @@ import ( "runtime" "strings" "testing" + "time" "github.com/stretchr/testify/assert" @@ -1400,3 +1401,63 @@ func TestEvaluateSymlinksInPaths(t *testing.T) { err = os.RemoveAll(dir + "/.task") assert.NoError(t, err) } + +func TestFileWatcherInterval(t *testing.T) { + const dir = "testdata/watcher_interval" + expectedOutput := strings.TrimSpace(` +task: Started watching for tasks: default +task: [default] echo "Hello, World!" +Hello, World! +task: [default] echo "Hello, World!" +Hello, World! + `) + + var buff bytes.Buffer + e := &task.Executor{ + Dir: dir, + Stdout: &buff, + Stderr: &buff, + Watch: true, + } + + assert.NoError(t, e.Setup()) + buff.Reset() + + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + + err := os.MkdirAll(dir+"/src", 0755) + assert.NoError(t, err) + + err = os.WriteFile(dir+"/src/a", []byte("test"), 0644) + if err != nil { + t.Fatal(err) + } + + go func(ctx context.Context) { + for { + select { + case <-ctx.Done(): + return + default: + err := e.Run(ctx, taskfile.Call{Task: "default"}) + if err != nil { + return + } + } + } + }(ctx) + + time.Sleep(10 * time.Millisecond) + err = os.WriteFile(dir+"/src/a", []byte("test updated"), 0644) + if err != nil { + t.Fatal(err) + } + time.Sleep(70 * time.Millisecond) + cancel() + + assert.Equal(t, expectedOutput, strings.TrimSpace(buff.String())) + buff.Reset() + err = os.RemoveAll(dir + "/.task") + assert.NoError(t, err) +} diff --git a/taskfile/taskfile.go b/taskfile/taskfile.go index ff7b1bbf..90f274ed 100644 --- a/taskfile/taskfile.go +++ b/taskfile/taskfile.go @@ -18,6 +18,7 @@ type Taskfile struct { Silent bool Dotenv []string Run string + Interval string } // UnmarshalYAML implements yaml.Unmarshaler interface @@ -34,10 +35,13 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { Silent bool Dotenv []string Run string + Interval string } + if err := unmarshal(&taskfile); err != nil { return err } + tf.Version = taskfile.Version tf.Expansions = taskfile.Expansions tf.Output = taskfile.Output @@ -49,6 +53,8 @@ func (tf *Taskfile) UnmarshalYAML(unmarshal func(interface{}) error) error { tf.Silent = taskfile.Silent tf.Dotenv = taskfile.Dotenv tf.Run = taskfile.Run + tf.Interval = taskfile.Interval + if tf.Expansions <= 0 { tf.Expansions = 2 } diff --git a/testdata/watcher_interval/.gitignore b/testdata/watcher_interval/.gitignore new file mode 100644 index 00000000..71bcfc6c --- /dev/null +++ b/testdata/watcher_interval/.gitignore @@ -0,0 +1 @@ +src/* diff --git a/testdata/watcher_interval/Taskfile.yaml b/testdata/watcher_interval/Taskfile.yaml new file mode 100644 index 00000000..23c2d069 --- /dev/null +++ b/testdata/watcher_interval/Taskfile.yaml @@ -0,0 +1,16 @@ +# https://taskfile.dev + +version: '3' + +vars: + GREETING: Hello, World! + +interval: "50ms" + +tasks: + default: + sources: + - "src/*" + cmds: + - echo "{{.GREETING}}" + silent: false diff --git a/watch.go b/watch.go index b93e2d30..c75d225c 100644 --- a/watch.go +++ b/watch.go @@ -16,7 +16,7 @@ import ( "github.com/radovskyb/watcher" ) -const watchInterval = 5 * time.Second +const defaultWatchInterval = 5 * time.Second // watchTasks start watching the given tasks func (e *Executor) watchTasks(calls ...taskfile.Call) error { @@ -24,6 +24,7 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { for i, c := range calls { tasks[i] = c.Task } + e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s", strings.Join(tasks, ", ")) ctx, cancel := context.WithCancel(context.Background()) @@ -36,6 +37,26 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { }() } + var watchIntervalString string + + if e.Taskfile.Interval != "" { + watchIntervalString = e.Taskfile.Interval + } else if e.Interval != "" { + watchIntervalString = e.Interval + } + + watchInterval := defaultWatchInterval + + if watchIntervalString != "" { + var err error + watchInterval, err = parsedWatchInterval(watchIntervalString) + if err != nil { + e.Logger.Errf(logger.Red, "%v", err) + } + } + + e.Logger.VerboseOutf(logger.Green, "task: Watching for changes every %v", watchInterval) + w := watcher.New() defer w.Close() w.SetMaxEvents(1) @@ -163,3 +184,11 @@ func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Ca func shouldIgnoreFile(path string) bool { return strings.Contains(path, "/.git") || strings.Contains(path, "/.task") || strings.Contains(path, "/node_modules") } + +func parsedWatchInterval(watchInterval string) (time.Duration, error) { + v, err := time.ParseDuration(watchInterval) + if err != nil { + return 0, fmt.Errorf(`task: Could not parse watch interval "%s": %v`, watchInterval, err) + } + return v, nil +} From c2f20465ab9a52560cdc2c1f9c5c83cba7349ef6 Mon Sep 17 00:00:00 2001 From: ilewin Date: Mon, 19 Sep 2022 20:31:24 +0200 Subject: [PATCH 2/6] Updated test for file watcher interval param Updated Interval Setting Priority --- task_test.go | 9 +++++---- testdata/watcher_interval/Taskfile.yaml | 2 +- watch.go | 10 +++++----- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/task_test.go b/task_test.go index ca3e02d8..1b131f07 100644 --- a/task_test.go +++ b/task_test.go @@ -1503,9 +1503,6 @@ Hello, World! assert.NoError(t, e.Setup()) buff.Reset() - ctx := context.Background() - ctx, cancel := context.WithCancel(ctx) - err := os.MkdirAll(dir+"/src", 0755) assert.NoError(t, err) @@ -1514,6 +1511,9 @@ Hello, World! t.Fatal(err) } + ctx := context.Background() + ctx, cancel := context.WithCancel(ctx) + go func(ctx context.Context) { for { select { @@ -1535,9 +1535,10 @@ Hello, World! } time.Sleep(70 * time.Millisecond) cancel() - assert.Equal(t, expectedOutput, strings.TrimSpace(buff.String())) buff.Reset() err = os.RemoveAll(dir + "/.task") assert.NoError(t, err) + err = os.RemoveAll(dir + "/src") + assert.NoError(t, err) } diff --git a/testdata/watcher_interval/Taskfile.yaml b/testdata/watcher_interval/Taskfile.yaml index 23c2d069..2a1e16b8 100644 --- a/testdata/watcher_interval/Taskfile.yaml +++ b/testdata/watcher_interval/Taskfile.yaml @@ -5,7 +5,7 @@ version: '3' vars: GREETING: Hello, World! -interval: "50ms" +interval: "30ms" tasks: default: diff --git a/watch.go b/watch.go index c75d225c..60ac6aae 100644 --- a/watch.go +++ b/watch.go @@ -39,17 +39,17 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { var watchIntervalString string - if e.Taskfile.Interval != "" { - watchIntervalString = e.Taskfile.Interval - } else if e.Interval != "" { + if e.Interval != "" { watchIntervalString = e.Interval + } else if e.Taskfile.Interval != "" { + watchIntervalString = e.Taskfile.Interval } watchInterval := defaultWatchInterval if watchIntervalString != "" { var err error - watchInterval, err = parsedWatchInterval(watchIntervalString) + watchInterval, err = parseWatchInterval(watchIntervalString) if err != nil { e.Logger.Errf(logger.Red, "%v", err) } @@ -185,7 +185,7 @@ func shouldIgnoreFile(path string) bool { return strings.Contains(path, "/.git") || strings.Contains(path, "/.task") || strings.Contains(path, "/node_modules") } -func parsedWatchInterval(watchInterval string) (time.Duration, error) { +func parseWatchInterval(watchInterval string) (time.Duration, error) { v, err := time.ParseDuration(watchInterval) if err != nil { return 0, fmt.Errorf(`task: Could not parse watch interval "%s": %v`, watchInterval, err) From 6bff658af088243afcfb5e8a7c72475d2c739a5d Mon Sep 17 00:00:00 2001 From: Pete Davison Date: Thu, 13 Oct 2022 14:37:52 +0000 Subject: [PATCH 3/6] chore: ignore fleet configuration directory --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9827564c..35da8d54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ dist/ # editors .idea/ .vscode/ +.fleet/ # exuberant ctags tags From 99014ad38dc036bc1bd3aee8b86ac287d1753e38 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 14 Oct 2022 16:45:04 -0300 Subject: [PATCH 4/6] Small adjustments + CHANGELOG for #865 --- CHANGELOG.md | 3 +++ cmd/task/task.go | 4 ---- docs/docs/api_reference.md | 2 ++ docs/docs/usage.md | 4 ++++ task_test.go | 12 ++++++------ testdata/watcher_interval/Taskfile.yaml | 2 +- watch.go | 3 ++- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fe311987..3ab8562e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +- Added ability to set a different watch interval by setting + `interval: '500ms'` or using the `--interval=500ms` flag + ([#813](https://github.com/go-task/task/issues/813), [#865](https://github.com/go-task/task/pull/865)). - Add colored output to `--list`, `--list-all` and `--summary` flags ([#845](https://github.com/go-task/task/pull/845), [#874](https://github.com/go-task/task/pull/874)). - Fix unexpected behavior where `label:` was being shown instead of the task name on `--list` diff --git a/cmd/task/task.go b/cmd/task/task.go index abee37dd..c7c25dee 100644 --- a/cmd/task/task.go +++ b/cmd/task/task.go @@ -209,10 +209,6 @@ func main() { e.InterceptInterruptSignals() } - if e.Interval == "" { - e.Interval = strings.TrimSpace(interval) - } - ctx := context.Background() if status { diff --git a/docs/docs/api_reference.md b/docs/docs/api_reference.md index e67b721a..5a10c700 100644 --- a/docs/docs/api_reference.md +++ b/docs/docs/api_reference.md @@ -30,6 +30,7 @@ variable | `-f` | `--force` | `bool` | `false` | Forces execution even when the task is up-to-date. | | `-h` | `--help` | `bool` | `false` | Shows Task usage. | | `-i` | `--init` | `bool` | `false` | Creates a new Taskfile.yaml in the current folder. | +| `-I` | `--interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). | | `-l` | `--list` | `bool` | `false` | Lists tasks with description of current Taskfile. | | `-a` | `--list-all` | `bool` | `false` | Lists tasks with or without a description. | | `-o` | `--output` | `string` | Default set in the Taskfile or `intervealed` | Sets output style: [`interleaved`/`group`/`prefixed`]. | @@ -84,6 +85,7 @@ Some environment variables can be overriden to adjust Task behavior. | `method` | `string` | `checksum` | Default method in this Taskfile. Can be overriden in a task by task basis. Available options: `checksum`, `timestamp` and `none`. | | `silent` | `bool` | `false` | Default "silent" options for this Taskfile. If `false`, can be overidden with `true` in a task by task basis. | | `run` | `string` | `always` | Default "run" option for this Taskfile. Available options: `always`, `once` and `when_changed`. | +| `interval` | `string` | `5s` | Sets a different watch interval when using `--watch`, the default being 5 seconds. This string should be a valid [Go Duration](https://pkg.go.dev/time#ParseDuration). | | `vars` | [`map[string]Variable`](#variable) | | Global variables. | | `env` | [`map[string]Variable`](#variable) | | Global environment. | | `dotenv` | `[]string` | | A list of `.env` file paths to be parsed. | diff --git a/docs/docs/usage.md b/docs/docs/usage.md index a36f97e1..10f2eab3 100644 --- a/docs/docs/usage.md +++ b/docs/docs/usage.md @@ -1247,5 +1247,9 @@ With the flags `--watch` or `-w` task will watch for file changes and run the task again. This requires the `sources` attribute to be given, so task knows which files to watch. +The default watch interval is 5 seconds, but it's possible to change it by +either setting `interval: '500ms'` in the root of the Taskfile passing it +as an argument like `--interval=500ms`. + [gotemplate]: https://golang.org/pkg/text/template/ [minify]: https://github.com/tdewolff/minify/tree/master/cmd/minify diff --git a/task_test.go b/task_test.go index b66d8d48..96d5a4bb 100644 --- a/task_test.go +++ b/task_test.go @@ -1503,10 +1503,10 @@ Hello, World! assert.NoError(t, e.Setup()) buff.Reset() - err := os.MkdirAll(dir+"/src", 0755) + err := os.MkdirAll(filepathext.SmartJoin(dir, "src"), 0755) assert.NoError(t, err) - err = os.WriteFile(dir+"/src/a", []byte("test"), 0644) + err = os.WriteFile(filepathext.SmartJoin(dir, "src/a"), []byte("test"), 0644) if err != nil { t.Fatal(err) } @@ -1529,16 +1529,16 @@ Hello, World! }(ctx) time.Sleep(10 * time.Millisecond) - err = os.WriteFile(dir+"/src/a", []byte("test updated"), 0644) + err = os.WriteFile(filepathext.SmartJoin(dir, "src/a"), []byte("test updated"), 0644) if err != nil { t.Fatal(err) } - time.Sleep(70 * time.Millisecond) + time.Sleep(700 * time.Millisecond) cancel() assert.Equal(t, expectedOutput, strings.TrimSpace(buff.String())) buff.Reset() - err = os.RemoveAll(dir + "/.task") + err = os.RemoveAll(filepathext.SmartJoin(dir, ".task")) assert.NoError(t, err) - err = os.RemoveAll(dir + "/src") + err = os.RemoveAll(filepathext.SmartJoin(dir, "src")) assert.NoError(t, err) } diff --git a/testdata/watcher_interval/Taskfile.yaml b/testdata/watcher_interval/Taskfile.yaml index 2a1e16b8..110eff27 100644 --- a/testdata/watcher_interval/Taskfile.yaml +++ b/testdata/watcher_interval/Taskfile.yaml @@ -5,7 +5,7 @@ version: '3' vars: GREETING: Hello, World! -interval: "30ms" +interval: "500ms" tasks: default: diff --git a/watch.go b/watch.go index 60ac6aae..6af7dbc6 100644 --- a/watch.go +++ b/watch.go @@ -51,7 +51,8 @@ func (e *Executor) watchTasks(calls ...taskfile.Call) error { var err error watchInterval, err = parseWatchInterval(watchIntervalString) if err != nil { - e.Logger.Errf(logger.Red, "%v", err) + cancel() + return err } } From 476d9f5e70273482bd0fad975b4265fb32da9f79 Mon Sep 17 00:00:00 2001 From: Artem Yadelskyi Date: Sat, 15 Oct 2022 00:24:11 +0300 Subject: [PATCH 5/6] Improved fish shell completions --- .editorconfig | 2 +- completion/fish/task.fish | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.editorconfig b/.editorconfig index 8ef8a237..d37c349a 100644 --- a/.editorconfig +++ b/.editorconfig @@ -8,6 +8,6 @@ charset = utf-8 trim_trailing_whitespace = true indent_style = tab -[*.{md,yml,yaml,json,toml,htm,html,js,css,svg,sh,bash}] +[*.{md,yml,yaml,json,toml,htm,html,js,css,svg,sh,bash,fish}] indent_style = space indent_size = 2 diff --git a/completion/fish/task.fish b/completion/fish/task.fish index e0d9c051..45a9746d 100644 --- a/completion/fish/task.fish +++ b/completion/fish/task.fish @@ -1,16 +1,24 @@ set GO_TASK_PROGNAME task function __task_get_tasks --description "Prints all available tasks with their description" - set -l output ($GO_TASK_PROGNAME --list-all | sed '1d; s/\* \(.*\):\s*\(.*\)/\1\t\2/' | string split0) + # Read the list of tasks (and potential errors) + $GO_TASK_PROGNAME --list-all 2>&1 | read -lz rawOutput + + # Return on non-zero exit code (for cases when there is no Taskfile found or etc.) + if test $status -ne 0 + return + end + + # Grab names and descriptions (if any) of the tasks + set -l output (echo $rawOutput | sed '1d; s/\* \(.*\):\s*\(.*\)/\1\t\2/' | string split0) if test $output - echo $output + echo $output end end complete -c $GO_TASK_PROGNAME -d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was specified.' -xa "(__task_get_tasks)" - complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)' complete -c $GO_TASK_PROGNAME -s d -l dir -d 'sets directory of execution' complete -c $GO_TASK_PROGNAME -l dry -d 'compiles and prints tasks in the order that they would be run, without executing them' From 172d71435a7a8fc6ea76c1ca32b5926ea1d852d2 Mon Sep 17 00:00:00 2001 From: Andrey Nering Date: Fri, 14 Oct 2022 18:32:12 -0300 Subject: [PATCH 6/6] Add CHANGELOG for #897 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ab8562e..73704455 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- Improvements to Fish shell completion + ([#897](https://github.com/go-task/task/pull/897)). - Added ability to set a different watch interval by setting `interval: '500ms'` or using the `--interval=500ms` flag ([#813](https://github.com/go-task/task/issues/813), [#865](https://github.com/go-task/task/pull/865)).