diff --git a/runner/run-task.go b/runner/run-task.go index c30d4b4..a7b970d 100644 --- a/runner/run-task.go +++ b/runner/run-task.go @@ -55,177 +55,193 @@ func isTTY() bool { return ((stdout.Mode() & os.ModeCharDevice) == os.ModeCharDevice) && ((stderr.Mode() & os.ModeCharDevice) == os.ModeCharDevice) } -func runCommand(ctx Context, prf *types.ParsedRunfile, pt *types.ParsedTask, args runTaskArgs) error { - for _, command := range pt.Commands { - ctx.Debug("running command task", "command.run", command.Run, "parent.task", args.taskName) +func runCommand(ctx Context, prf *types.ParsedRunfile, pt *types.ParsedTask, args runTaskArgs, command types.ParsedCommandJson) error { + ctx.Debug("running command task", "command.run", command.Run, "parent.task", args.taskName) + var wg sync.WaitGroup - if command.If != nil && !*command.If { - ctx.Debug("skipping execution for failed `if`", "command", command.Run) - continue + if command.If != nil && !*command.If { + ctx.Debug("skipping execution for failed `if`", "command", command.Run) + return nil + } + + if command.Run != "" { + rt, ok := prf.Tasks[command.Run] + if !ok { + return fmt.Errorf("invalid run target") } - if command.Run != "" { - 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) + } - rtp, err := parser.ParseTask(ctx, prf, rt) - if err != nil { - return errors.WithErr(err).KV("env-vars", prf.Env) - } + if err := runTaskCommands(ctx, prf, rtp, args); err != nil { + return errors.WithErr(err).KV("env-vars", prf.Env) + } + return nil + } - if err := runCommand(ctx, prf, rtp, args); err != nil { - return errors.WithErr(err).KV("env-vars", prf.Env) - } - continue + // stdoutR, stdoutW := io.Pipe() + // stderrR, stderrW := io.Pipe() + + // wg := sync.WaitGroup{} + + // [snippet source](https://rderik.com/blog/identify-if-output-goes-to-the-terminal-or-is-being-redirected-in-golang/) + // stdout, _ := os.Stdout.Stat() + // stderr, _ := os.Stderr.Stat() + // isTTY := ((stdout.Mode() & os.ModeCharDevice) == os.ModeCharDevice) && ((stderr.Mode() & os.ModeCharDevice) == os.ModeCharDevice) + // + // if isTTY { + // go func() { + // defer wg.Done() + // logPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s]", strings.Join(trail, "/")))) + // processOutput(os.Stdout, stdoutR, &logPrefix) + // + // stderrPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s/stderr]", strings.Join(trail, "/")))) + // processOutput(os.Stderr, stderrR, &stderrPrefix) + // }() + // } else { + // wg.Add(1) + // go func() { + // defer wg.Done() + // logPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s]", strings.Join(trail, "/")))) + // processOutput(os.Stdout, stdoutR, &logPrefix) + // // if pt.Interactive { + // // processOutput(os.Stdout, stdoutR, &logPrefix) + // // return + // // } + // // processOutputLineByLine(os.Stdout, stdoutR, &logPrefix) + // }() + // + // wg.Add(1) + // go func() { + // defer wg.Done() + // logPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s/stderr]", strings.Join(trail, "/")))) + // processOutput(os.Stderr, stderrR, &logPrefix) + // // if pt.Interactive { + // // processOutput(os.Stderr, stderrR, &logPrefix) + // // return + // // } + // // processOutputLineByLine(os.Stderr, stderrR, &logPrefix) + // }() + // } + + if isTTY() { + borderColor := "#4388cc" + if !isDarkTheme() { + borderColor = "#3d5485" } + s := lipgloss.NewStyle().BorderForeground(lipgloss.Color(borderColor)).PaddingLeft(1).PaddingRight(1).Border(lipgloss.RoundedBorder(), true, true, true, true) + // labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(borderColor)).Blink(true) - // stdoutR, stdoutW := io.Pipe() - // stderrR, stderrW := io.Pipe() - - // wg := sync.WaitGroup{} - - // [snippet source](https://rderik.com/blog/identify-if-output-goes-to-the-terminal-or-is-being-redirected-in-golang/) - // stdout, _ := os.Stdout.Stat() - // stderr, _ := os.Stderr.Stat() - // isTTY := ((stdout.Mode() & os.ModeCharDevice) == os.ModeCharDevice) && ((stderr.Mode() & os.ModeCharDevice) == os.ModeCharDevice) - // - // if isTTY { - // go func() { - // defer wg.Done() - // logPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s]", strings.Join(trail, "/")))) - // processOutput(os.Stdout, stdoutR, &logPrefix) - // - // stderrPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s/stderr]", strings.Join(trail, "/")))) - // processOutput(os.Stderr, stderrR, &stderrPrefix) - // }() - // } else { - // wg.Add(1) - // go func() { - // defer wg.Done() - // logPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s]", strings.Join(trail, "/")))) - // processOutput(os.Stdout, stdoutR, &logPrefix) - // // if pt.Interactive { - // // processOutput(os.Stdout, stdoutR, &logPrefix) - // // return - // // } - // // processOutputLineByLine(os.Stdout, stdoutR, &logPrefix) - // }() - // - // wg.Add(1) - // go func() { - // defer wg.Done() - // logPrefix := fmt.Sprintf("%s ", ctx.theme.TaskPrefixStyle.Render(fmt.Sprintf("[%s/stderr]", strings.Join(trail, "/")))) - // processOutput(os.Stderr, stderrR, &logPrefix) - // // if pt.Interactive { - // // processOutput(os.Stderr, stderrR, &logPrefix) - // // return - // // } - // // processOutputLineByLine(os.Stderr, stderrR, &logPrefix) - // }() - // } - - if isTTY() { - borderColor := "#4388cc" - if !isDarkTheme() { - borderColor = "#3d5485" - } - s := lipgloss.NewStyle().BorderForeground(lipgloss.Color(borderColor)).PaddingLeft(1).PaddingRight(1).Border(lipgloss.RoundedBorder(), true, true, true, true) - // labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(borderColor)).Blink(true) + if args.DebugEnv { + fmt.Printf("%s\n", s.Render(padString(fmt.Sprintf("%+v", prf.Env), "DEBUG: env"))) + } - if args.DebugEnv { - fmt.Printf("%s\n", s.Render(padString(fmt.Sprintf("%+v", prf.Env), "DEBUG: env"))) - } + hlCode := new(bytes.Buffer) + // choose colorschemes from `https://swapoff.org/chroma/playground/` + colorscheme := "catppuccin-macchiato" + if !isDarkTheme() { + colorscheme = "monokailight" + } + // quick.Highlight(hlCode, strings.TrimSpace(command.Command), "bash", "terminal16m", colorscheme) - hlCode := new(bytes.Buffer) - // choose colorschemes from `https://swapoff.org/chroma/playground/` - colorscheme := "catppuccin-macchiato" - if !isDarkTheme() { - colorscheme = "monokailight" - } - // quick.Highlight(hlCode, strings.TrimSpace(command.Command), "bash", "terminal16m", colorscheme) + cmdStr := strings.TrimSpace(command.Command) - cmdStr := strings.TrimSpace(command.Command) + quick.Highlight(hlCode, cmdStr, "bash", "terminal16m", colorscheme) + // cst := styles.Get("gruvbox") + // fmt.Println("cst: ", cst.Name, styles.Fallback.Name, styles.Names()) - quick.Highlight(hlCode, cmdStr, "bash", "terminal16m", colorscheme) - // cst := styles.Get("gruvbox") - // fmt.Println("cst: ", cst.Name, styles.Fallback.Name, styles.Names()) + // fmt.Printf("%s\n", s.Render(args.taskName+" | "+hlCode.String())) + fmt.Printf("%s\n", s.Render(padString(hlCode.String(), args.taskName))) + } - // fmt.Printf("%s\n", s.Render(args.taskName+" | "+hlCode.String())) - 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: logger2, + IsInteractive: pt.Interactive, + Command: func(c context.Context) *exec.Cmd { + return CreateCommand(c, CmdArgs{ + Shell: pt.Shell, + Env: fn.ToEnviron(pt.Env), + Cmd: command.Command, + WorkingDir: pt.WorkingDir, + interactive: pt.Interactive, + Stdout: os.Stdout, + Stderr: os.Stderr, + }) + }, + }) - logger2 := logging.New(logging.Options{ - Prefix: "[runfile]", - Writer: os.Stderr, - SlogKeyAsPrefix: "task", - }) + wg.Add(1) + go func() { + defer wg.Done() + <-ctx.Done() + ex.Kill() + }() - ex := executor.NewExecutor(executor.ExecutorArgs{ - Logger: logger2, - IsInteractive: pt.Interactive, - Command: func(c context.Context) *exec.Cmd { - return CreateCommand(c, CmdArgs{ - Shell: pt.Shell, - Env: fn.ToEnviron(pt.Env), - Cmd: command.Command, - WorkingDir: pt.WorkingDir, - interactive: pt.Interactive, - Stdout: os.Stdout, - Stderr: os.Stderr, - }) - }, - }) + // 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) + } - // 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 runTaskCommands(ctx Context, prf *types.ParsedRunfile, pt *types.ParsedTask, args runTaskArgs) error { + for _, command := range pt.Commands { + if err := runCommand(ctx, prf, pt, args, command); err != nil { + return err } } @@ -265,7 +281,7 @@ func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { wg.Add(1) go func() { defer wg.Done() - runCommand(NewContext(nctx, ctx.Logger), prf, pt, args) + runTaskCommands(NewContext(nctx, ctx.Logger), prf, pt, args) }() if pt.Watch != nil && (pt.Watch.Enable == nil || *pt.Watch.Enable) { @@ -276,7 +292,7 @@ func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { IgnoreSuffixes: pt.Watch.IgnoreSuffixes, ExcludeDirs: pt.Watch.ExcludeDirs, UseDefaultIgnoreList: true, - // CooldownDuration: fn.New(1 * time.Second), + CooldownDuration: fn.New(1 * time.Second), }) if err != nil { return errors.WithErr(err) @@ -286,9 +302,6 @@ func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { <-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 { @@ -302,7 +315,11 @@ func runTask(ctx Context, prf *types.ParsedRunfile, args runTaskArgs) error { cf() nctx, cf = context.WithCancel(ctx) - go runCommand(NewContext(nctx, ctx.Logger), prf, pt, args) + wg.Add(1) + go func() { + defer wg.Done() + runTaskCommands(NewContext(nctx, ctx.Logger), prf, pt, args) + }() return nil case <-ctx.Done():