diff --git a/config.go b/config.go index 01c3dc7..1a80ee0 100644 --- a/config.go +++ b/config.go @@ -192,7 +192,6 @@ func (f FastlaneRunner) checkForRbenv(workDir string) { f.logger.Donef("$ %s", cmd.PrintableCommandArgs()) if err := cmd.Run(); err != nil { - err := f.wrapCommandError(cmd, err) f.logger.Warnf(err.Error()) } } diff --git a/dependencies.go b/dependencies.go index 79a2cd7..5c7eb58 100644 --- a/dependencies.go +++ b/dependencies.go @@ -33,7 +33,7 @@ func (f FastlaneRunner) InstallDependencies(opts EnsureDependenciesOpts) error { f.logger.Println() if err := cmd.Run(); err != nil { - return f.wrapCommandError(cmd, err) + return err } } @@ -51,7 +51,7 @@ func (f FastlaneRunner) InstallDependencies(opts EnsureDependenciesOpts) error { f.logger.Println() if err := cmd.Run(); err != nil { - return f.wrapCommandError(cmd, err) + return err } } else if opts.UpdateFastlane { f.logger.Println() @@ -66,7 +66,7 @@ func (f FastlaneRunner) InstallDependencies(opts EnsureDependenciesOpts) error { f.logger.Donef("$ %s", cmd.PrintableCommandArgs()) if err := cmd.Run(); err != nil { - return f.wrapCommandError(cmd, err) + return err } } } else { @@ -94,7 +94,7 @@ func (f FastlaneRunner) InstallDependencies(opts EnsureDependenciesOpts) error { f.logger.Donef("$ %s", cmd.PrintableCommandArgs()) if err := cmd.Run(); err != nil { - return f.wrapCommandError(cmd, err) + return err } return nil diff --git a/go.mod b/go.mod index 6148caf..26d69d7 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,7 @@ require ( github.com/bitrise-io/go-steputils v1.0.2 github.com/bitrise-io/go-steputils/v2 v2.0.0-alpha.14 github.com/bitrise-io/go-utils v1.0.2 - github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.14 + github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.15 github.com/bitrise-io/go-xcode v1.0.9 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/stretchr/testify v1.8.1 diff --git a/go.sum b/go.sum index cb4f53e..1b8a53e 100644 --- a/go.sum +++ b/go.sum @@ -30,8 +30,8 @@ github.com/bitrise-io/go-utils v1.0.2/go.mod h1:ZY1DI+fEpZuFpO9szgDeICM4QbqoWVt0 github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.6/go.mod h1:mysJdafur1ytXda0TeRsV+GxYCJFDU0QcCmYBgQf2Fc= github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.7/go.mod h1:6i0Gt0JRIbXpsrFDJT1YWghFfdN8qF26/fnpc/6d/88= github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.12/go.mod h1:gZWtM7PLn1VOroa4gN1La/24aRVc0jg5R701jTsPaO8= -github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.14 h1:ojxW7IWBXmF2VsmkO+1s1/zSQR7f9DWqphSgcoQbGjw= -github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.14/go.mod h1:Laih4ji980SQkRgdnMCH0g4u2GZI/5nnbqmYT9UfKFQ= +github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.15 h1:ERQb+OOa+eKMWb+HByyPd5ugz6TeaJPnQ3xHgyaR7hA= +github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.15/go.mod h1:Laih4ji980SQkRgdnMCH0g4u2GZI/5nnbqmYT9UfKFQ= github.com/bitrise-io/go-xcode v1.0.6/go.mod h1:Y0Wu2dXm0MilJ/4D3+gPHaNMlUcP+1DjIPoLPykq7wY= github.com/bitrise-io/go-xcode v1.0.9 h1:+sbqOYidQ+aiFfCTDpf2LdGSQEM5RfbtDsiG27zJG+s= github.com/bitrise-io/go-xcode v1.0.9/go.mod h1:Y0Wu2dXm0MilJ/4D3+gPHaNMlUcP+1DjIPoLPykq7wY= diff --git a/main.go b/main.go index fca7f2e..53e1ec8 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "github.com/bitrise-io/go-steputils/v2/stepconf" "github.com/bitrise-io/go-utils/v2/command" "github.com/bitrise-io/go-utils/v2/env" + "github.com/bitrise-io/go-utils/v2/errorutil" . "github.com/bitrise-io/go-utils/v2/exitcode" "github.com/bitrise-io/go-utils/v2/log" "github.com/bitrise-io/go-utils/v2/pathutil" @@ -25,7 +26,7 @@ func run() ExitCode { config, err := buildStep.ProcessConfig() if err != nil { buildStep.logger.Println() - buildStep.logger.Errorf(formattedError(fmt.Errorf("Failed to process Step inputs: %w", err))) + buildStep.logger.Errorf(errorutil.FormattedError(fmt.Errorf("Failed to process Step inputs: %w", err))) return Failure } @@ -38,14 +39,14 @@ func run() ExitCode { if err := buildStep.InstallDependencies(dependenciesOpts); err != nil { buildStep.logger.Println() - buildStep.logger.Errorf(formattedError(fmt.Errorf("Failed to install Step dependencies: %w", err))) + buildStep.logger.Errorf(errorutil.FormattedError(fmt.Errorf("Failed to install Step dependencies: %w", err))) return Failure } runOpts := createRunOptions(config) if err := buildStep.Run(runOpts); err != nil { buildStep.logger.Println() - logger.Errorf(formattedError(fmt.Errorf("Failed to execute Step main logic: %w", err))) + logger.Errorf(errorutil.FormattedError(fmt.Errorf("Failed to execute Step: %w", err))) return Failure } diff --git a/run.go b/run.go index c26467f..e22365a 100644 --- a/run.go +++ b/run.go @@ -112,7 +112,7 @@ you can find the output of fastlane env in the following log file: %s`, deployPt f.logger.Warnf("Failed to walk directory, error: %s", err) } - return fmt.Errorf("running Fastlane failed: %w", f.wrapCommandError(cmd, fastlaneErr)) + return fmt.Errorf("running Fastlane failed: %w", fastlaneErr) } f.cacheDeps(opts) diff --git a/vendor/github.com/bitrise-io/go-utils/v2/command/command.go b/vendor/github.com/bitrise-io/go-utils/v2/command/command.go index eedc628..4206c2b 100644 --- a/vendor/github.com/bitrise-io/go-utils/v2/command/command.go +++ b/vendor/github.com/bitrise-io/go-utils/v2/command/command.go @@ -1,6 +1,8 @@ package command import ( + "errors" + "fmt" "io" "os/exec" "strconv" @@ -9,13 +11,17 @@ import ( "github.com/bitrise-io/go-utils/v2/env" ) +// ErrorFinder ... +type ErrorFinder func(out string) []string + // Opts ... type Opts struct { - Stdout io.Writer - Stderr io.Writer - Stdin io.Reader - Env []string - Dir string + Stdout io.Writer + Stderr io.Writer + Stdin io.Reader + Env []string + Dir string + ErrorFinder ErrorFinder } // Factory ... @@ -35,7 +41,13 @@ func NewFactory(envRepository env.Repository) Factory { // Create ... func (f factory) Create(name string, args []string, opts *Opts) Command { cmd := exec.Command(name, args...) + var collector *errorCollector + if opts != nil { + if opts.ErrorFinder != nil { + collector = &errorCollector{errorFinder: opts.ErrorFinder} + } + cmd.Stdout = opts.Stdout cmd.Stderr = opts.Stderr cmd.Stdin = opts.Stdin @@ -47,7 +59,10 @@ func (f factory) Create(name string, args []string, opts *Opts) Command { cmd.Env = append(f.envRepository.List(), opts.Env...) cmd.Dir = opts.Dir } - return command{cmd} + return &command{ + cmd: cmd, + errorCollector: collector, + } } // Command ... @@ -62,7 +77,8 @@ type Command interface { } type command struct { - cmd *exec.Cmd + cmd *exec.Cmd + errorCollector *errorCollector } // PrintableCommandArgs ... @@ -71,13 +87,24 @@ func (c command) PrintableCommandArgs() string { } // Run ... -func (c command) Run() error { - return c.cmd.Run() +func (c *command) Run() error { + c.wrapOutputs() + + if err := c.cmd.Run(); err != nil { + return c.wrapError(err) + } + + return nil } // RunAndReturnExitCode ... func (c command) RunAndReturnExitCode() (int, error) { + c.wrapOutputs() err := c.cmd.Run() + if err != nil { + err = c.wrapError(err) + } + exitCode := c.cmd.ProcessState.ExitCode() return exitCode, err } @@ -86,6 +113,13 @@ func (c command) RunAndReturnExitCode() (int, error) { func (c command) RunAndReturnTrimmedOutput() (string, error) { outBytes, err := c.cmd.Output() outStr := string(outBytes) + if err != nil { + if c.errorCollector != nil { + c.errorCollector.collectErrors(outStr) + } + err = c.wrapError(err) + } + return strings.TrimSpace(outStr), err } @@ -93,17 +127,30 @@ func (c command) RunAndReturnTrimmedOutput() (string, error) { func (c command) RunAndReturnTrimmedCombinedOutput() (string, error) { outBytes, err := c.cmd.CombinedOutput() outStr := string(outBytes) + if err != nil { + if c.errorCollector != nil { + c.errorCollector.collectErrors(outStr) + } + err = c.wrapError(err) + } + return strings.TrimSpace(outStr), err } // Start ... func (c command) Start() error { + c.wrapOutputs() return c.cmd.Start() } // Wait ... func (c command) Wait() error { - return c.cmd.Wait() + err := c.cmd.Wait() + if err != nil { + err = c.wrapError(err) + } + + return err } func printableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string { @@ -118,3 +165,34 @@ func printableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string { return strings.Join(cmdArgsDecorated, " ") } + +func (c command) wrapError(err error) error { + var exitErr *exec.ExitError + if errors.As(err, &exitErr) { + if c.errorCollector != nil && len(c.errorCollector.errorLines) > 0 { + return fmt.Errorf("command failed with exit status %d (%s): %w", exitErr.ExitCode(), c.PrintableCommandArgs(), errors.New(strings.Join(c.errorCollector.errorLines, "\n"))) + } + return fmt.Errorf("command failed with exit status %d (%s): %w", exitErr.ExitCode(), c.PrintableCommandArgs(), errors.New("check the command's output for details")) + } + return fmt.Errorf("executing command failed (%s): %w", c.PrintableCommandArgs(), err) +} + +func (c command) wrapOutputs() { + if c.errorCollector == nil { + return + } + + if c.cmd.Stdout != nil { + outWriter := io.MultiWriter(c.errorCollector, c.cmd.Stdout) + c.cmd.Stdout = outWriter + } else { + c.cmd.Stdout = c.errorCollector + } + + if c.cmd.Stderr != nil { + errWriter := io.MultiWriter(c.errorCollector, c.cmd.Stderr) + c.cmd.Stderr = errWriter + } else { + c.cmd.Stderr = c.errorCollector + } +} diff --git a/vendor/github.com/bitrise-io/go-utils/v2/command/errorcollector.go b/vendor/github.com/bitrise-io/go-utils/v2/command/errorcollector.go new file mode 100644 index 0000000..945e3ff --- /dev/null +++ b/vendor/github.com/bitrise-io/go-utils/v2/command/errorcollector.go @@ -0,0 +1,18 @@ +package command + +type errorCollector struct { + errorLines []string + errorFinder ErrorFinder +} + +func (e *errorCollector) Write(p []byte) (n int, err error) { + e.collectErrors(string(p)) + return len(p), nil +} + +func (e *errorCollector) collectErrors(output string) { + lines := e.errorFinder(output) + if len(lines) > 0 { + e.errorLines = append(e.errorLines, lines...) + } +} diff --git a/formatted_error.go b/vendor/github.com/bitrise-io/go-utils/v2/errorutil/formatted_error.go similarity index 69% rename from formatted_error.go rename to vendor/github.com/bitrise-io/go-utils/v2/errorutil/formatted_error.go index e3cee40..842c74b 100644 --- a/formatted_error.go +++ b/vendor/github.com/bitrise-io/go-utils/v2/errorutil/formatted_error.go @@ -1,15 +1,12 @@ -package main +package errorutil import ( "errors" - "fmt" - "os/exec" "strings" - - "github.com/bitrise-io/go-utils/v2/command" ) -func formattedError(err error) string { +// FormattedError ... +func FormattedError(err error) string { var formatted string i := -1 @@ -69,12 +66,3 @@ func indentedReason(reason string, level int) string { } return indented } - -func (f FastlaneRunner) wrapCommandError(cmd command.Command, err error) error { - var exitErr *exec.ExitError - if errors.As(err, &exitErr) { - return fmt.Errorf("command failed with exit status %d (%s): %w", exitErr.ExitCode(), cmd.PrintableCommandArgs(), errors.New("check the command's output for details")) - } - - return fmt.Errorf("executing command failed (%s): %w", cmd.PrintableCommandArgs(), err) -} diff --git a/vendor/modules.txt b/vendor/modules.txt index e5cf543..c7bd7cf 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -41,10 +41,11 @@ github.com/bitrise-io/go-utils/parseutil github.com/bitrise-io/go-utils/pathutil github.com/bitrise-io/go-utils/pointers github.com/bitrise-io/go-utils/sliceutil -# github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.14 +# github.com/bitrise-io/go-utils/v2 v2.0.0-alpha.15 ## explicit; go 1.17 github.com/bitrise-io/go-utils/v2/command github.com/bitrise-io/go-utils/v2/env +github.com/bitrise-io/go-utils/v2/errorutil github.com/bitrise-io/go-utils/v2/exitcode github.com/bitrise-io/go-utils/v2/log github.com/bitrise-io/go-utils/v2/log/colorstring