1
0
mirror of https://github.com/go-task/task.git synced 2024-12-04 10:24:45 +02:00
task/watch.go

182 lines
4.0 KiB
Go
Raw Normal View History

2017-04-01 21:04:52 +02:00
package task
import (
"context"
"fmt"
"os"
"os/signal"
"path/filepath"
2017-04-01 21:04:52 +02:00
"strings"
"syscall"
2017-04-01 21:04:52 +02:00
"time"
"github.com/radovskyb/watcher"
2023-04-15 22:22:25 +02:00
"github.com/go-task/task/v3/errors"
"github.com/go-task/task/v3/internal/fingerprint"
"github.com/go-task/task/v3/internal/logger"
"github.com/go-task/task/v3/taskfile"
2017-04-01 21:04:52 +02:00
)
const defaultWatchInterval = 5 * time.Second
// watchTasks start watching the given tasks
func (e *Executor) watchTasks(calls ...taskfile.Call) error {
tasks := make([]string, len(calls))
for i, c := range calls {
tasks[i] = c.Task
}
e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s\n", strings.Join(tasks, ", "))
2017-04-01 21:04:52 +02:00
ctx, cancel := context.WithCancel(context.Background())
for _, c := range calls {
c := c
go func() {
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
e.Logger.Errf(logger.Red, "%v\n", err)
}
}()
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
}
e.Logger.VerboseOutf(logger.Green, "task: Watching for changes every %v\n", watchInterval)
w := watcher.New()
defer w.Close()
w.SetMaxEvents(1)
2017-04-01 21:04:52 +02:00
closeOnInterrupt(w)
2017-04-01 21:04:52 +02:00
go func() {
for {
select {
case event := <-w.Event:
e.Logger.VerboseErrf(logger.Magenta, "task: received watch event: %v\n", event)
cancel()
ctx, cancel = context.WithCancel(context.Background())
e.Compiler.ResetCache()
for _, c := range calls {
c := c
go func() {
if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
e.Logger.Errf(logger.Red, "%v\n", err)
}
}()
}
case err := <-w.Error:
switch err {
case watcher.ErrWatchedFileDeleted:
default:
e.Logger.Errf(logger.Red, "%v\n", err)
}
case <-w.Closed:
cancel()
return
2017-04-01 21:04:52 +02:00
}
}
}()
go func() {
// re-register every 5 seconds because we can have new files, but this process is expensive to run
for {
if err := e.registerWatchedFiles(w, calls...); err != nil {
e.Logger.Errf(logger.Red, "%v\n", err)
2017-04-01 21:04:52 +02:00
}
time.Sleep(watchInterval)
2017-04-01 21:04:52 +02:00
}
}()
return w.Start(watchInterval)
2017-04-01 21:04:52 +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
}
return err == context.Canceled || err == context.DeadlineExceeded
}
func closeOnInterrupt(w *watcher.Watcher) {
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt, syscall.SIGTERM)
go func() {
<-ch
w.Close()
}()
}
func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Call) error {
watchedFiles := w.WatchedFiles()
2017-04-01 21:04:52 +02:00
var registerTaskFiles func(taskfile.Call) error
registerTaskFiles = func(c taskfile.Call) error {
task, err := e.CompiledTask(c)
if err != nil {
return err
2017-07-02 20:30:50 +02:00
}
for _, d := range task.Deps {
if err := registerTaskFiles(taskfile.Call{Task: d.Task, Vars: d.Vars}); err != nil {
return err
}
2017-04-01 21:04:52 +02:00
}
for _, c := range task.Cmds {
if c.Task != "" {
if err := registerTaskFiles(taskfile.Call{Task: c.Task, Vars: c.Vars}); err != nil {
return err
}
}
}
2017-04-01 21:04:52 +02:00
for _, s := range task.Sources {
files, err := fingerprint.Glob(task.Dir, s)
2017-04-01 21:04:52 +02:00
if err != nil {
return fmt.Errorf("task: %s: %w", s, err)
2017-04-01 21:04:52 +02:00
}
for _, f := range files {
absFile, err := filepath.Abs(f)
if err != nil {
return err
}
if shouldIgnoreFile(absFile) {
continue
}
if _, ok := watchedFiles[absFile]; ok {
continue
}
if err := w.Add(absFile); err != nil {
2017-04-01 21:04:52 +02:00
return err
}
e.Logger.VerboseOutf(logger.Green, "task: watching new file: %v\n", absFile)
2017-04-01 21:04:52 +02:00
}
}
return nil
}
for _, c := range calls {
if err := registerTaskFiles(c); err != nil {
return err
}
2017-04-01 21:04:52 +02:00
}
return nil
}
func shouldIgnoreFile(path string) bool {
2023-03-31 02:25:08 +02:00
return strings.Contains(path, "/.git") || strings.Contains(path, "/.hg") ||
strings.Contains(path, "/.task") || strings.Contains(path, "/node_modules")
}