package util import ( "io" "os" "os/exec" ) // CommandOpts contains options to attach Stdout/err to a command to run // or set its initial directory type CommandOpts struct { Stdout io.Writer Stderr io.Writer Dir string EnvAppend []string } // CommandRunner executes OS commands with the given parameters and options type CommandRunner interface { RunWithOptions(opts CommandOpts, name string, arg ...string) error Run(name string, arg ...string) error StartWithStdoutPipe(opts CommandOpts, name string, arg ...string) (io.ReadCloser, error) Wait() error } // NewCommandRunner creates a new instance of the default implementation of // CommandRunner func NewCommandRunner() CommandRunner { return &runner{} } type runner struct { cmd *exec.Cmd } // RunWithOptions runs a command with the provided options func (c *runner) RunWithOptions(opts CommandOpts, name string, arg ...string) error { cmd := exec.Command(name, arg...) if opts.Stdout != nil { cmd.Stdout = opts.Stdout } if opts.Stderr != nil { cmd.Stderr = opts.Stderr } if opts.Dir != "" { cmd.Dir = opts.Dir } if len(opts.EnvAppend) > 0 { cmd.Env = os.Environ() cmd.Env = append(cmd.Env, opts.EnvAppend...) } return cmd.Run() } // Run executes a command with default options func (c *runner) Run(name string, arg ...string) error { return c.RunWithOptions(CommandOpts{}, name, arg...) } // StartWithStdoutPipe executes a command returning a ReadCloser connected to // the command's stdout. func (c *runner) StartWithStdoutPipe(opts CommandOpts, name string, arg ...string) (io.ReadCloser, error) { c.cmd = exec.Command(name, arg...) if opts.Stderr != nil { c.cmd.Stderr = opts.Stderr } if opts.Dir != "" { c.cmd.Dir = opts.Dir } if len(opts.EnvAppend) > 0 { c.cmd.Env = os.Environ() c.cmd.Env = append(c.cmd.Env, opts.EnvAppend...) } r, err := c.cmd.StdoutPipe() if err != nil { return nil, err } return r, c.cmd.Start() } // Wait waits for the command to exit. func (c *runner) Wait() error { return c.cmd.Wait() }