diff --git a/examples/run1/Runfile b/examples/run1/Runfile index b1b4bad..458f200 100644 --- a/examples/run1/Runfile +++ b/examples/run1/Runfile @@ -4,6 +4,8 @@ tasks: echo: cmd: - echo "hello from run1" + - if: gt 1 0 + cmd: echo "hello after condition check" node:shell: interactive: true diff --git a/pkg/runfile/run.go b/pkg/runfile/run.go index 0191c81..95a9965 100644 --- a/pkg/runfile/run.go +++ b/pkg/runfile/run.go @@ -69,7 +69,7 @@ func processOutput(writer io.Writer, reader io.Reader, prefix *string) { if err != nil { // logger.Info("stdout", "msg", string(msg[:n]), "err", err) if errors.Is(err, io.EOF) { - os.Stdout.Write(msg[:n]) + writer.Write(msg[:n]) return } } @@ -80,7 +80,7 @@ func processOutput(writer io.Writer, reader io.Reader, prefix *string) { if prevByte == '\n' && prefix != nil { // os.Stdout.WriteString(fmt.Sprintf("HERE... msg: '%s'", msg[:n])) - os.Stdout.WriteString(*prefix) + writer.Write([]byte(*prefix)) } writer.Write(msg[:n]) @@ -124,6 +124,12 @@ func runTask(ctx Context, rf *Runfile, args runTaskArgs) *Error { logger.Debug("debugging env", "pt.environ", pt.Env, "overrides", args.envOverrides) for _, command := range pt.Commands { logger.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) + continue + } + if command.Run != "" { if err := runTask(ctx, rf, runTaskArgs{ taskTrail: trail, @@ -131,9 +137,6 @@ func runTask(ctx Context, rf *Runfile, args runTaskArgs) *Error { envOverrides: pt.Env, }); err != nil { return err - // return NewError("", "").WithTask(fmt.Sprintf("%s/%s", err.TaskName, command.Run)).WithRunfile(rf.attrs.RunfilePath).WithErr(err.WithMetadata()) - // e := formatErr(err).WithTask(fmt.Sprintf("%s/%s", err.TaskName, command.Run)) - // return e } continue } diff --git a/pkg/runfile/task-parser.go b/pkg/runfile/task-parser.go index 550e465..5b2f1b6 100644 --- a/pkg/runfile/task-parser.go +++ b/pkg/runfile/task-parser.go @@ -13,11 +13,31 @@ import ( ) type ParsedTask struct { - Shell []string `json:"shell"` - WorkingDir string `json:"workingDir"` - Env map[string]string `json:"environ"` - Interactive bool `json:"interactive,omitempty"` - Commands []CommandJson `json:"commands"` + Shell []string `json:"shell"` + WorkingDir string `json:"workingDir"` + Env map[string]string `json:"environ"` + Interactive bool `json:"interactive,omitempty"` + Commands []ParsedCommandJson `json:"commands"` +} + +func evalGoTemplateCondition(tpl string) (bool, *Error) { + t := template.New("requirement") + t = t.Funcs(sprig.FuncMap()) + templateExpr := fmt.Sprintf(`{{ %s }}`, tpl) + t, err := t.Parse(templateExpr) + if err != nil { + return false, TaskRequirementIncorrect.WithErr(err).WithMetadata("requirement", tpl) + } + b := new(bytes.Buffer) + if err := t.ExecuteTemplate(b, "requirement", map[string]string{}); err != nil { + return false, TaskRequirementIncorrect.WithErr(err).WithMetadata("requirement", tpl) + } + + if b.String() != "true" { + return false, TaskRequirementFailed.WithErr(fmt.Errorf("template must have evaluated to true")).WithMetadata("requirement", tpl) + } + + return true, nil } func ParseTask(ctx Context, rf *Runfile, task Task) (*ParsedTask, *Error) { @@ -71,22 +91,9 @@ func ParseTask(ctx Context, rf *Runfile, task Task) (*ParsedTask, *Error) { } if requirement.GoTmpl != nil { - t := template.New("requirement") - t = t.Funcs(sprig.FuncMap()) - templateExpr := fmt.Sprintf(`{{ %s }}`, *requirement.GoTmpl) - t, err := t.Parse(templateExpr) - if err != nil { - return nil, TaskRequirementIncorrect.WithErr(err).WithMetadata("requirement", *requirement.GoTmpl) - } - b := new(bytes.Buffer) - if err := t.ExecuteTemplate(b, "requirement", map[string]string{}); err != nil { - return nil, TaskRequirementIncorrect.WithErr(err).WithMetadata("requirement", *requirement.GoTmpl) - } - - if b.String() != "true" { - return nil, TaskRequirementFailed.WithErr(fmt.Errorf("template must have evaluated to true")).WithMetadata("requirement", *requirement.GoTmpl) + if _, err := evalGoTemplateCondition(*requirement.GoTmpl); err != nil { + return nil, err } - continue } } @@ -127,7 +134,7 @@ func ParseTask(ctx Context, rf *Runfile, task Task) (*ParsedTask, *Error) { return nil, err } - commands := make([]CommandJson, 0, len(task.Commands)) + commands := make([]ParsedCommandJson, 0, len(task.Commands)) for i := range task.Commands { c2, err := parseCommand(rf, task.Commands[i]) if err != nil { @@ -169,11 +176,11 @@ func resolveDotEnvFiles(pwd string, dotEnvFiles ...string) ([]string, *Error) { return paths, nil } -func parseCommand(rf *Runfile, command any) (*CommandJson, *Error) { +func parseCommand(rf *Runfile, command any) (*ParsedCommandJson, *Error) { switch c := command.(type) { case string: { - return &CommandJson{Command: c}, nil + return &ParsedCommandJson{Command: c}, nil } case map[string]any: { @@ -187,15 +194,27 @@ func parseCommand(rf *Runfile, command any) (*CommandJson, *Error) { return nil, CommandInvalid.WithErr(err).WithMetadata("command", command) } - if cj.Run == "" { - return nil, CommandInvalid.WithErr(fmt.Errorf("key: 'run', must be specified when setting command in json format")).WithMetadata("command", command) + if cj.Run == "" && cj.Command == "" { + return nil, CommandInvalid.WithErr(fmt.Errorf("key: 'run'/'cmd', must be specified when setting command in json format")).WithMetadata("command", cj) } - if _, ok := rf.Tasks[cj.Run]; !ok { - return nil, CommandInvalid.WithErr(fmt.Errorf("run target, not found")).WithMetadata("command", command, "run-target", cj.Run) + var pcj ParsedCommandJson + pcj.Run = cj.Run + pcj.Command = cj.Command + + if cj.If != nil { + ok, _ := evalGoTemplateCondition(*cj.If) + // if err != nil { + // return nil, err + // } + pcj.If = &ok } - return &cj, nil + // if _, ok := rf.Tasks[cj.Run]; !ok { + // return nil, CommandInvalid.WithErr(fmt.Errorf("run target, not found")).WithMetadata("command", command, "run-target", cj.Run) + // } + + return &pcj, nil } default: { diff --git a/pkg/runfile/task.go b/pkg/runfile/task.go index cce3133..70c7659 100644 --- a/pkg/runfile/task.go +++ b/pkg/runfile/task.go @@ -46,11 +46,26 @@ type Task struct { // List of commands to be executed in given shell (default: sh) // can take multiple forms // - simple string - // - a json object with key `run`, signifying other tasks to run + // - a json object with key + // `run`, signifying other tasks to run + // `if`, condition when to run this server Commands []any `json:"cmd"` } type CommandJson struct { - Command string + Command string `json:"cmd"` Run string `json:"run"` + Env string `json:"env"` + + // If is a go template expression, which must evaluate to true, for task to run + If *string `json:"if,omitempty"` +} + +type ParsedCommandJson struct { + Command string `json:"cmd"` + Run string `json:"run"` + Env string `json:"env"` + + // If is a go template expression, which must evaluate to true, for task to run + If *bool `json:"if"` }