From 1c44d8049a9770c420ab17f0fff29f619fd36286 Mon Sep 17 00:00:00 2001 From: ilewin Date: Thu, 8 Sep 2022 19:22:44 +0200 Subject: [PATCH 1/2] 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/2] 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)