github.com/hazelops/ize@v1.1.12-0.20230915191306-97d7c0e48f11/pkg/term/runner.go (about) 1 package term 2 3 import ( 4 "bufio" 5 "bytes" 6 "fmt" 7 "io" 8 "os" 9 "os/exec" 10 "syscall" 11 ) 12 13 type Runner struct { 14 stdout io.Writer 15 stderr io.Writer 16 dir string 17 stdin io.Reader 18 } 19 20 type RunnerOption func(*Runner) 21 22 func WithStdout(stdout io.Writer) RunnerOption { 23 return func(r *Runner) { 24 r.stdout = stdout 25 } 26 } 27 28 func WithStderr(stderr io.Writer) RunnerOption { 29 return func(r *Runner) { 30 r.stderr = stderr 31 } 32 } 33 34 func WithStdin(stdin io.Reader) RunnerOption { 35 return func(r *Runner) { 36 r.stdin = stdin 37 } 38 } 39 40 func WithDir(path string) RunnerOption { 41 return func(r *Runner) { 42 r.dir = path 43 } 44 } 45 46 func New(opts ...RunnerOption) *Runner { 47 r := &Runner{ 48 stderr: os.Stderr, 49 stdout: os.Stdout, 50 dir: ".", 51 } 52 53 for _, opt := range opts { 54 opt(r) 55 } 56 57 return r 58 } 59 60 type Option func(cmd *exec.Cmd) 61 62 func (r Runner) Run(cmd *exec.Cmd) (stdout, stderr string, exitCode int, err error) { 63 if r.stdin != nil { 64 cmd.Stdin = os.Stdin 65 } 66 67 outReader, err := cmd.StdoutPipe() 68 if err != nil { 69 return 70 } 71 errReader, err := cmd.StderrPipe() 72 if err != nil { 73 return 74 } 75 76 var bufOut, bufErr bytes.Buffer 77 outReader2 := io.TeeReader(outReader, &bufOut) 78 errReader2 := io.TeeReader(errReader, &bufErr) 79 80 if err = cmd.Start(); err != nil { 81 return 82 } 83 84 go r.printOutputWithHeader("", outReader2) 85 go r.printOutputWithHeader("", errReader2) 86 87 err = cmd.Wait() 88 89 stdout = bufOut.String() 90 stderr = bufErr.String() 91 92 if err != nil { 93 if err2, ok := err.(*exec.ExitError); ok { 94 if s, ok := err2.Sys().(syscall.WaitStatus); ok { 95 err = nil 96 exitCode = s.ExitStatus() 97 } 98 } 99 } 100 return 101 } 102 103 func (r Runner) printOutputWithHeader(header string, reader io.Reader) { 104 scanner := bufio.NewScanner(reader) 105 for scanner.Scan() { 106 if r.stdout != nil { 107 fmt.Fprintf(r.stdout, "%s%s\n", header, scanner.Text()) 108 } else { 109 fmt.Printf("%s%s\n", header, scanner.Text()) 110 } 111 } 112 }