github.com/clusterize-io/tusk@v0.6.3-0.20211001020217-cfe8a8cd0d4a/runner/command.go (about) 1 package runner 2 3 import ( 4 "os" 5 "os/exec" 6 7 "github.com/clusterize-io/tusk/marshal" 8 "github.com/clusterize-io/tusk/ui" 9 ) 10 11 var defaultInterpreter = []string{"sh", "-c"} 12 13 // execCommand allows overwriting during tests. 14 var execCommand = exec.Command 15 16 // Command is a command passed to the shell. 17 type Command struct { 18 // Exec is the script to execute. 19 Exec string `yaml:"exec"` 20 21 // Print is the text that will be printed when the command is executed. 22 Print string `yaml:"print"` 23 24 // Dir is the directory of the command. 25 Dir string `yaml:"dir"` 26 } 27 28 // UnmarshalYAML allows strings to be interpreted as Do actions. 29 func (c *Command) UnmarshalYAML(unmarshal func(interface{}) error) error { 30 var do string 31 doCandidate := marshal.UnmarshalCandidate{ 32 Unmarshal: func() error { return unmarshal(&do) }, 33 Assign: func() { 34 *c = Command{ 35 Exec: do, 36 Print: do, 37 } 38 }, 39 } 40 41 type commandType Command // Use new type to avoid recursion 42 var commandItem commandType 43 commandCandidate := marshal.UnmarshalCandidate{ 44 Unmarshal: func() error { return unmarshal(&commandItem) }, 45 Assign: func() { 46 *c = Command(commandItem) 47 if c.Print == "" { 48 c.Print = c.Exec 49 } 50 }, 51 } 52 53 return marshal.UnmarshalOneOf(doCandidate, commandCandidate) 54 } 55 56 // newCmd creates an exec.Cmd that uses the interpreter and the script passed. 57 func newCmd(ctx Context, script string) *exec.Cmd { 58 interpreter := defaultInterpreter 59 if len(ctx.Interpreter) > 0 { 60 interpreter = ctx.Interpreter 61 } 62 63 path := interpreter[0] 64 args := []string{script} 65 if len(interpreter) > 1 { 66 args = append(interpreter[1:], args...) 67 } 68 69 return execCommand(path, args...) 70 } 71 72 // execCommand executes a shell command. 73 func (c *Command) exec(ctx Context) error { 74 cmd := newCmd(ctx, c.Exec) 75 76 cmd.Dir = c.Dir 77 cmd.Stdin = os.Stdin 78 if ctx.Logger.Verbosity > ui.VerbosityLevelSilent { 79 cmd.Stdout = os.Stdout 80 cmd.Stderr = os.Stderr 81 } 82 83 return cmd.Run() 84 } 85 86 // CommandList is a list of commands with custom yaml unamrshaling. 87 type CommandList []Command 88 89 // UnmarshalYAML allows single items to be used as lists. 90 func (cl *CommandList) UnmarshalYAML(unmarshal func(interface{}) error) error { 91 var commandSlice []Command 92 sliceCandidate := marshal.UnmarshalCandidate{ 93 Unmarshal: func() error { return unmarshal(&commandSlice) }, 94 Assign: func() { *cl = commandSlice }, 95 } 96 97 var commandItem Command 98 itemCandidate := marshal.UnmarshalCandidate{ 99 Unmarshal: func() error { return unmarshal(&commandItem) }, 100 Assign: func() { *cl = CommandList{commandItem} }, 101 } 102 103 return marshal.UnmarshalOneOf(sliceCandidate, itemCandidate) 104 }