package exec import ( "bytes" "context" "golang.org/x/sync/errgroup" "os/exec" "time" ) type RunOptions struct { Timeout time.Duration } type CommandRunner interface { Run(ctx context.Context, cmd string, args []string, opts RunOptions) (string, string, error) } type DefaultRunner struct{} func (r *DefaultRunner) Run(ctx context.Context, cmd string, args []string, opts RunOptions) (string, string, error) { var stdout, stderr bytes.Buffer execCtx := ctx if opts.Timeout > 0 { var cancel context.CancelFunc execCtx, cancel = context.WithTimeout(ctx, opts.Timeout) defer cancel() } eg, cctx := errgroup.WithContext(execCtx) eg.Go(func() error { command := exec.CommandContext(cctx, cmd, args...) command.Stdout = &stdout command.Stderr = &stderr return command.Run() }) if err := eg.Wait(); err != nil { return stdout.String(), stderr.String(), err } return stdout.String(), stderr.String(), nil }