2017-04-01 21:04:52 +02:00
|
|
|
package task
|
|
|
|
|
|
|
|
import (
|
2017-04-13 01:53:41 +02:00
|
|
|
"context"
|
2021-04-18 12:52:55 +02:00
|
|
|
"fmt"
|
2018-09-17 02:59:00 +02:00
|
|
|
"os"
|
|
|
|
"os/signal"
|
2021-01-04 02:08:16 +02:00
|
|
|
"path/filepath"
|
2017-04-01 21:04:52 +02:00
|
|
|
"strings"
|
2018-09-17 02:59:00 +02:00
|
|
|
"syscall"
|
2017-04-01 21:04:52 +02:00
|
|
|
"time"
|
|
|
|
|
2022-12-19 03:06:09 +02:00
|
|
|
"github.com/radovskyb/watcher"
|
|
|
|
|
2023-04-15 22:22:25 +02:00
|
|
|
"github.com/go-task/task/v3/errors"
|
2023-03-10 20:27:30 +02:00
|
|
|
"github.com/go-task/task/v3/internal/fingerprint"
|
2020-08-16 20:48:19 +02:00
|
|
|
"github.com/go-task/task/v3/internal/logger"
|
2023-12-29 22:32:03 +02:00
|
|
|
"github.com/go-task/task/v3/taskfile/ast"
|
2017-04-01 21:04:52 +02:00
|
|
|
)
|
|
|
|
|
2022-09-08 19:22:44 +02:00
|
|
|
const defaultWatchInterval = 5 * time.Second
|
2017-08-05 18:35:10 +02:00
|
|
|
|
2017-06-04 21:02:04 +02:00
|
|
|
// watchTasks start watching the given tasks
|
2023-12-29 22:32:03 +02:00
|
|
|
func (e *Executor) watchTasks(calls ...ast.Call) error {
|
2017-09-07 18:57:06 +02:00
|
|
|
tasks := make([]string, len(calls))
|
|
|
|
for i, c := range calls {
|
|
|
|
tasks[i] = c.Task
|
|
|
|
}
|
2022-09-08 19:22:44 +02:00
|
|
|
|
2023-08-04 20:36:05 +02:00
|
|
|
e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s\n", strings.Join(tasks, ", "))
|
2017-04-01 21:04:52 +02:00
|
|
|
|
2017-08-04 17:48:15 +02:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
2017-09-07 18:57:06 +02:00
|
|
|
for _, c := range calls {
|
|
|
|
c := c
|
2017-08-04 17:48:15 +02:00
|
|
|
go func() {
|
2017-09-07 18:57:06 +02:00
|
|
|
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.Errf(logger.Red, "%v\n", err)
|
2017-08-04 17:48:15 +02:00
|
|
|
}
|
|
|
|
}()
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
|
|
|
|
2022-12-31 18:48:49 +02:00
|
|
|
var watchInterval time.Duration
|
|
|
|
switch {
|
|
|
|
case e.Interval != 0:
|
|
|
|
watchInterval = e.Interval
|
|
|
|
case e.Taskfile.Interval != 0:
|
|
|
|
watchInterval = e.Taskfile.Interval
|
|
|
|
default:
|
|
|
|
watchInterval = defaultWatchInterval
|
2022-09-08 19:22:44 +02:00
|
|
|
}
|
|
|
|
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.VerboseOutf(logger.Green, "task: Watching for changes every %v\n", watchInterval)
|
2022-09-08 19:22:44 +02:00
|
|
|
|
2017-08-05 16:50:39 +02:00
|
|
|
w := watcher.New()
|
|
|
|
defer w.Close()
|
|
|
|
w.SetMaxEvents(1)
|
2017-04-01 21:04:52 +02:00
|
|
|
|
2018-09-17 02:59:00 +02:00
|
|
|
closeOnInterrupt(w)
|
|
|
|
|
2017-04-01 21:04:52 +02:00
|
|
|
go func() {
|
|
|
|
for {
|
2017-08-05 16:50:39 +02:00
|
|
|
select {
|
|
|
|
case event := <-w.Event:
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.VerboseErrf(logger.Magenta, "task: received watch event: %v\n", event)
|
2017-08-05 16:50:39 +02:00
|
|
|
|
|
|
|
cancel()
|
|
|
|
ctx, cancel = context.WithCancel(context.Background())
|
2021-01-05 16:19:34 +02:00
|
|
|
|
|
|
|
e.Compiler.ResetCache()
|
|
|
|
|
2017-09-07 18:57:06 +02:00
|
|
|
for _, c := range calls {
|
|
|
|
c := c
|
2017-08-05 16:50:39 +02:00
|
|
|
go func() {
|
2017-09-07 18:57:06 +02:00
|
|
|
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.Errf(logger.Red, "%v\n", err)
|
2017-08-05 16:50:39 +02:00
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
case err := <-w.Error:
|
|
|
|
switch err {
|
|
|
|
case watcher.ErrWatchedFileDeleted:
|
|
|
|
default:
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.Errf(logger.Red, "%v\n", err)
|
2017-08-05 16:50:39 +02:00
|
|
|
}
|
|
|
|
case <-w.Closed:
|
2018-09-17 02:59:00 +02:00
|
|
|
cancel()
|
2017-08-05 16:50:39 +02:00
|
|
|
return
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2017-08-05 16:50:39 +02:00
|
|
|
go func() {
|
2021-01-05 15:48:04 +02:00
|
|
|
// re-register every 5 seconds because we can have new files, but this process is expensive to run
|
2017-08-05 16:50:39 +02:00
|
|
|
for {
|
2017-09-30 20:19:58 +02:00
|
|
|
if err := e.registerWatchedFiles(w, calls...); err != nil {
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.Errf(logger.Red, "%v\n", err)
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
2021-01-05 15:48:04 +02:00
|
|
|
time.Sleep(watchInterval)
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
2017-08-05 16:50:39 +02:00
|
|
|
}()
|
|
|
|
|
2021-01-05 15:48:04 +02:00
|
|
|
return w.Start(watchInterval)
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
|
|
|
|
2018-09-17 02:59:00 +02:00
|
|
|
func isContextError(err error) bool {
|
2023-04-15 22:22:25 +02:00
|
|
|
if taskRunErr, ok := err.(*errors.TaskRunError); ok {
|
|
|
|
err = taskRunErr.Err
|
2020-04-29 04:56:02 +02:00
|
|
|
}
|
|
|
|
|
2018-09-17 02:59:00 +02:00
|
|
|
return err == context.Canceled || err == context.DeadlineExceeded
|
|
|
|
}
|
|
|
|
|
|
|
|
func closeOnInterrupt(w *watcher.Watcher) {
|
|
|
|
ch := make(chan os.Signal, 1)
|
2022-05-15 02:00:15 +02:00
|
|
|
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
|
2018-09-17 02:59:00 +02:00
|
|
|
go func() {
|
|
|
|
<-ch
|
|
|
|
w.Close()
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
|
2023-12-29 22:32:03 +02:00
|
|
|
func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...ast.Call) error {
|
2021-01-05 15:48:04 +02:00
|
|
|
watchedFiles := w.WatchedFiles()
|
2017-04-01 21:04:52 +02:00
|
|
|
|
2023-12-29 22:32:03 +02:00
|
|
|
var registerTaskFiles func(ast.Call) error
|
|
|
|
registerTaskFiles = func(c ast.Call) error {
|
2017-09-30 20:19:58 +02:00
|
|
|
task, err := e.CompiledTask(c)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-07-02 20:30:50 +02:00
|
|
|
}
|
2017-08-05 16:50:39 +02:00
|
|
|
|
|
|
|
for _, d := range task.Deps {
|
2023-12-29 22:32:03 +02:00
|
|
|
if err := registerTaskFiles(ast.Call{Task: d.Task, Vars: d.Vars}); err != nil {
|
2017-08-05 16:50:39 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
2017-09-30 20:19:58 +02:00
|
|
|
for _, c := range task.Cmds {
|
|
|
|
if c.Task != "" {
|
2023-12-29 22:32:03 +02:00
|
|
|
if err := registerTaskFiles(ast.Call{Task: c.Task, Vars: c.Vars}); err != nil {
|
2017-09-30 20:19:58 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-08-05 16:50:39 +02:00
|
|
|
|
2023-11-30 03:38:12 +02:00
|
|
|
globs, err := fingerprint.Globs(task.Dir, task.Sources)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, s := range globs {
|
2023-03-10 20:27:30 +02:00
|
|
|
files, err := fingerprint.Glob(task.Dir, s)
|
2017-04-01 21:04:52 +02:00
|
|
|
if err != nil {
|
2021-04-18 12:52:55 +02:00
|
|
|
return fmt.Errorf("task: %s: %w", s, err)
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
|
|
|
for _, f := range files {
|
2021-01-04 02:08:16 +02:00
|
|
|
absFile, err := filepath.Abs(f)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2021-01-05 15:48:04 +02:00
|
|
|
if shouldIgnoreFile(absFile) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if _, ok := watchedFiles[absFile]; ok {
|
2017-08-05 16:50:39 +02:00
|
|
|
continue
|
|
|
|
}
|
2021-01-04 02:08:16 +02:00
|
|
|
if err := w.Add(absFile); err != nil {
|
2017-04-01 21:04:52 +02:00
|
|
|
return err
|
|
|
|
}
|
2023-04-27 02:20:06 +02:00
|
|
|
e.Logger.VerboseOutf(logger.Green, "task: watching new file: %v\n", absFile)
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
|
|
|
}
|
2017-08-05 16:50:39 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-30 20:19:58 +02:00
|
|
|
for _, c := range calls {
|
|
|
|
if err := registerTaskFiles(c); err != nil {
|
2017-08-05 16:50:39 +02:00
|
|
|
return err
|
|
|
|
}
|
2017-04-01 21:04:52 +02:00
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2021-01-05 15:48:04 +02:00
|
|
|
|
|
|
|
func shouldIgnoreFile(path string) bool {
|
2023-10-07 23:38:14 +02:00
|
|
|
ignorePaths := []string{
|
|
|
|
"/.task",
|
|
|
|
"/.git",
|
|
|
|
"/.hg",
|
2023-10-07 23:41:35 +02:00
|
|
|
"/node_modules",
|
2023-10-07 23:38:14 +02:00
|
|
|
}
|
|
|
|
for _, p := range ignorePaths {
|
|
|
|
if strings.Contains(path, fmt.Sprintf("%s/", p)) || strings.HasSuffix(path, p) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
2021-01-05 15:48:04 +02:00
|
|
|
}
|