From afcd333591bd1df01d997d756cef666100e3895f Mon Sep 17 00:00:00 2001 From: nxtcoder17 Date: Thu, 19 Dec 2024 11:52:03 +0530 Subject: [PATCH] fix: fixes watch behaviour, now watches all the commands in a task instead of, just the last command --- go.mod | 9 +- go.sum | 20 +--- parser/parse-task.go | 1 + runner/run-task.go | 223 +++++++++++++++++++++++++++++------------- types/parsed-types.go | 2 +- types/types.go | 4 +- 6 files changed, 162 insertions(+), 97 deletions(-) diff --git a/go.mod b/go.mod index 62e791d..23fb584 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/charmbracelet/lipgloss v1.0.0 github.com/joho/godotenv v1.5.1 github.com/muesli/termenv v0.15.2 - github.com/nxtcoder17/fwatcher v1.0.3 + github.com/nxtcoder17/fwatcher v1.0.4-0.20241218102704-76d04c526fb0 github.com/phuslu/log v1.0.112 github.com/urfave/cli/v3 v3.0.0-beta1 golang.org/x/sync v0.10.0 @@ -16,20 +16,13 @@ require ( require ( github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect - github.com/charmbracelet/log v0.4.0 // indirect github.com/charmbracelet/x/ansi v0.6.0 // indirect - github.com/cpuguy83/go-md2man/v2 v2.0.5 // indirect github.com/dlclark/regexp2 v1.11.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect github.com/rivo/uniseg v0.4.7 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/urfave/cli/v2 v2.27.5 // indirect - github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect - golang.org/x/exp v0.0.0-20241210194714-1829a127f884 // indirect golang.org/x/sys v0.28.0 // indirect ) diff --git a/go.sum b/go.sum index 04a3f2c..17f993a 100644 --- a/go.sum +++ b/go.sum @@ -8,20 +8,14 @@ github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiE github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= -github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= -github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= github.com/charmbracelet/x/ansi v0.6.0 h1:qOznutrb93gx9oMiGf7caF7bqqubh6YIM0SWKyA08pA= github.com/charmbracelet/x/ansi v0.6.0/go.mod h1:KBUFw1la39nl0dLl10l5ORDAqGXaeurTQmwyyVKse/Q= -github.com/cpuguy83/go-md2man/v2 v2.0.5 h1:ZtcqGrnekaHpVLArFSe4HK5DoKx1T0rq2DwVB0alcyc= -github.com/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo= github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -37,10 +31,8 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/nxtcoder17/fwatcher v1.0.3-0.20241212071621-beb84ec5b061 h1:bNRVjvghGLiCJ9EOTS/qkrSAyKvz4e2S6CMzL8GnxgI= -github.com/nxtcoder17/fwatcher v1.0.3-0.20241212071621-beb84ec5b061/go.mod h1:MNmSwXYOrqp7U1pUxh0GWB5skpjFTWTQXhAA0+sPJcU= -github.com/nxtcoder17/fwatcher v1.0.3 h1:6oCdh9uMzSSeJLyA6iezc/8WBIZPbD9lx50nxC7cMyQ= -github.com/nxtcoder17/fwatcher v1.0.3/go.mod h1:MNmSwXYOrqp7U1pUxh0GWB5skpjFTWTQXhAA0+sPJcU= +github.com/nxtcoder17/fwatcher v1.0.4-0.20241218102704-76d04c526fb0 h1:k09TYswdqCsFz0fBnO3AjGT5MHfvjPRyBtHbBWuiJLM= +github.com/nxtcoder17/fwatcher v1.0.4-0.20241218102704-76d04c526fb0/go.mod h1:MNmSwXYOrqp7U1pUxh0GWB5skpjFTWTQXhAA0+sPJcU= github.com/phuslu/log v1.0.112 h1:vQ0ZFd5O+in/0IQAcjuEl6wRkHiQPw7T0sqwmOjpL0U= github.com/phuslu/log v1.0.112/go.mod h1:F8osGJADo5qLK/0F88djWwdyoZZ9xDJQL1HYRHFEkS0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -48,18 +40,10 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w= -github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ= github.com/urfave/cli/v3 v3.0.0-beta1 h1:6DTaaUarcM0wX7qj5Hcvs+5Dm3dyUTBbEwIWAjcw9Zg= github.com/urfave/cli/v3 v3.0.0-beta1/go.mod h1:FnIeEMYu+ko8zP1F9Ypr3xkZMIDqW3DR92yUtY39q1Y= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4= -github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884 h1:Y/Mj/94zIQQGHVSv1tTtQBDaQaJe62U9bkDZKKyhPCU= -golang.org/x/exp v0.0.0-20241210194714-1829a127f884/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/parser/parse-task.go b/parser/parse-task.go index ba1935a..77383d0 100644 --- a/parser/parse-task.go +++ b/parser/parse-task.go @@ -94,5 +94,6 @@ func ParseTask(ctx context.Context, prf *types.ParsedRunfile, task types.Task) ( Interactive: task.Interactive, Env: taskEnv, Commands: commands, + Watch: task.Watch, }, nil } diff --git a/runner/run-task.go b/runner/run-task.go index 8a2a73c..c30d4b4 100644 --- a/runner/run-task.go +++ b/runner/run-task.go @@ -8,6 +8,7 @@ import ( "os/exec" "path/filepath" "strings" + "sync" "time" "github.com/alecthomas/chroma/v2/quick" @@ -17,6 +18,7 @@ import ( "github.com/nxtcoder17/fwatcher/pkg/watcher" "github.com/nxtcoder17/runfile/errors" fn "github.com/nxtcoder17/runfile/functions" + "github.com/nxtcoder17/runfile/logging" "github.com/nxtcoder17/runfile/parser" "github.com/nxtcoder17/runfile/types" ) @@ -53,45 +55,27 @@ func isTTY() bool { return ((stdout.Mode() & os.ModeCharDevice) == os.ModeCharDevice) && ((stderr.Mode() & os.ModeCharDevice) == os.ModeCharDevice) } -func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { - runfilePath := prf.Metadata.RunfilePath - task := prf.Tasks[args.taskName] - - if task.Metadata.RunfilePath != nil { - runfilePath = *task.Metadata.RunfilePath - } - - trail := append(args.taskTrail, args.taskName) - - logger := ctx.With("task", args.taskName, "runfile", runfilePath) - logger.Debug("running task") - - task, ok := prf.Tasks[args.taskName] - if !ok { - return errors.ErrTaskNotFound - } - - task.Name = args.taskName - - pt, err := parser.ParseTask(ctx, prf, task) - if err != nil { - return errors.WithErr(err) - } - +func runCommand(ctx Context, prf *types.ParsedRunfile, pt *types.ParsedTask, args runTaskArgs) error { for _, command := range pt.Commands { - logger.Debug("running command task", "command.run", command.Run, "parent.task", args.taskName) + ctx.Debug("running command task", "command.run", command.Run, "parent.task", args.taskName) if command.If != nil && !*command.If { - logger.Debug("skipping execution for failed `if`", "command", command.Run) + ctx.Debug("skipping execution for failed `if`", "command", command.Run) continue } if command.Run != "" { - if err := runTask(ctx, prf, runTaskArgs{ - taskTrail: trail, - taskName: command.Run, - envOverrides: pt.Env, - }); err != nil { + rt, ok := prf.Tasks[command.Run] + if !ok { + return fmt.Errorf("invalid run target") + } + + rtp, err := parser.ParseTask(ctx, prf, rt) + if err != nil { + return errors.WithErr(err).KV("env-vars", prf.Env) + } + + if err := runCommand(ctx, prf, rtp, args); err != nil { return errors.WithErr(err).KV("env-vars", prf.Env) } continue @@ -172,8 +156,14 @@ func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { fmt.Printf("%s\n", s.Render(padString(hlCode.String(), args.taskName))) } + logger2 := logging.New(logging.Options{ + Prefix: "[runfile]", + Writer: os.Stderr, + SlogKeyAsPrefix: "task", + }) + ex := executor.NewExecutor(executor.ExecutorArgs{ - Logger: logger, + Logger: logger2, IsInteractive: pt.Interactive, Command: func(c context.Context) *exec.Cmd { return CreateCommand(c, CmdArgs{ @@ -188,47 +178,144 @@ func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { }, }) - if task.Watch.Enable { - watch, err := watcher.NewWatcher(ctx, watcher.WatcherArgs{ - Logger: logger, - WatchDirs: append(task.Watch.Dirs, pt.WorkingDir), - OnlySuffixes: pt.Watch.OnlySuffixes, - IgnoreSuffixes: pt.Watch.IgnoreSuffixes, - ExcludeDirs: pt.Watch.ExcludeDirs, - UseDefaultIgnoreList: true, - }) + // if task.Watch != nil && (task.Watch.Enable == nil || *task.Watch.Enable) { + // watch, err := watcher.NewWatcher(ctx, watcher.WatcherArgs{ + // Logger: logger, + // WatchDirs: append(task.Watch.Dirs, pt.WorkingDir), + // OnlySuffixes: pt.Watch.OnlySuffixes, + // IgnoreSuffixes: pt.Watch.IgnoreSuffixes, + // ExcludeDirs: pt.Watch.ExcludeDirs, + // UseDefaultIgnoreList: true, + // // CooldownDuration: fn.New(1 * time.Second), + // }) + // if err != nil { + // return errors.WithErr(err) + // } + // + // go ex.Exec() + // + // go func() { + // <-ctx.Done() + // logger.Debug("fwatcher is closing ...") + // watch.Close() + // <-time.After(200 * time.Millisecond) + // logger.Info("CLOSING..................") + // os.Exit(0) + // }() + // + // watch.WatchEvents(func(event watcher.Event, fp string) error { + // relPath, err := filepath.Rel(fn.Must(os.Getwd()), fp) + // if err != nil { + // return err + // } + // logger.Info(fmt.Sprintf("[RELOADING] due changes in %s", relPath)) + // ex.Kill() + // select { + // case <-time.After(100 * time.Millisecond): + // go ex.Exec() + // return nil + // case <-ctx.Done(): + // logger.Info("close signal received") + // watch.Close() + // return nil + // } + // }) + // + // return nil + // } + + if err := ex.Exec(); err != nil { + return errors.ErrTaskFailed.Wrap(err).KV("task", args.taskName) + } + } + + return nil +} + +func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { + runfilePath := prf.Metadata.RunfilePath + task := prf.Tasks[args.taskName] + + if task.Metadata.RunfilePath != nil { + runfilePath = *task.Metadata.RunfilePath + } + + args.taskTrail = append(args.taskTrail, args.taskName) + + logger := ctx.With("task", args.taskName, "runfile", runfilePath) + logger.Debug("running task") + + task, ok := prf.Tasks[args.taskName] + if !ok { + return errors.ErrTaskNotFound + } + + task.Name = args.taskName + + pt, err := parser.ParseTask(ctx, prf, task) + if err != nil { + return errors.WithErr(err) + } + + nctx, cf := context.WithCancel(ctx) + defer cf() + + var wg sync.WaitGroup + + wg.Add(1) + go func() { + defer wg.Done() + runCommand(NewContext(nctx, ctx.Logger), prf, pt, args) + }() + + if pt.Watch != nil && (pt.Watch.Enable == nil || *pt.Watch.Enable) { + watch, err := watcher.NewWatcher(ctx, watcher.WatcherArgs{ + Logger: logger, + WatchDirs: append(task.Watch.Dirs, pt.WorkingDir), + OnlySuffixes: pt.Watch.OnlySuffixes, + IgnoreSuffixes: pt.Watch.IgnoreSuffixes, + ExcludeDirs: pt.Watch.ExcludeDirs, + UseDefaultIgnoreList: true, + // CooldownDuration: fn.New(1 * time.Second), + }) + if err != nil { + return errors.WithErr(err) + } + + go func() { + <-ctx.Done() + logger.Debug("fwatcher is closing ...") + watch.Close() + <-time.After(200 * time.Millisecond) + logger.Info("CLOSING..................") + os.Exit(0) + }() + + watch.WatchEvents(func(event watcher.Event, fp string) error { + relPath, err := filepath.Rel(fn.Must(os.Getwd()), fp) if err != nil { - return errors.WithErr(err) + return err } + logger.Info(fmt.Sprintf("[RELOADING] due changes in %s", relPath)) + select { + case <-time.After(100 * time.Millisecond): + cf() - go ex.Exec() - - go func() { - <-ctx.Done() - logger.Info("fwatcher is closing ...") - <-time.After(200 * time.Millisecond) - os.Exit(0) - }() - - watch.WatchEvents(func(event watcher.Event, fp string) error { - relPath, err := filepath.Rel(fn.Must(os.Getwd()), fp) - if err != nil { - return err - } - logger.Info(fmt.Sprintf("[RELOADING] due changes in %s", relPath)) - ex.Kill() - <-time.After(100 * time.Millisecond) - go ex.Exec() - return nil - }) + nctx, cf = context.WithCancel(ctx) + go runCommand(NewContext(nctx, ctx.Logger), prf, pt, args) - return nil - } + return nil + case <-ctx.Done(): + logger.Info("close signal received") + watch.Close() + return nil + } + }) - if err := ex.Exec(); err != nil { - return errors.ErrTaskFailed.Wrap(err).KV("task", args.taskName) - } + return nil } + wg.Wait() + return nil } diff --git a/types/parsed-types.go b/types/parsed-types.go index 4e97ada..faf85ff 100644 --- a/types/parsed-types.go +++ b/types/parsed-types.go @@ -12,7 +12,7 @@ type ParsedRunfile struct { type ParsedTask struct { Shell []string `json:"shell"` WorkingDir string `json:"workingDir"` - Watch TaskWatch `json:"watch"` + Watch *TaskWatch `json:"watch,omitempty"` Env map[string]string `json:"environ"` Interactive bool `json:"interactive,omitempty"` Commands []ParsedCommandJson `json:"commands"` diff --git a/types/types.go b/types/types.go index d091ccc..951cb85 100644 --- a/types/types.go +++ b/types/types.go @@ -36,7 +36,7 @@ type TaskMetadata struct { } type TaskWatch struct { - Enable bool `json:"enable"` + Enable *bool `json:"enable,omitempty"` Dirs []string `json:"dirs"` OnlySuffixes []string `json:"onlySuffixes"` IgnoreSuffixes []string `json:"ignoreSuffixes"` @@ -66,7 +66,7 @@ type Task struct { Env EnvVar `json:"env,omitempty"` - Watch TaskWatch `json:"watch"` + Watch *TaskWatch `json:"watch"` Requires []*Requires `json:"requires,omitempty"`