diff --git a/src/app/project_runner.go b/src/app/project_runner.go index 919bc1a..05d8700 100644 --- a/src/app/project_runner.go +++ b/src/app/project_runner.go @@ -15,6 +15,14 @@ import ( "github.com/rs/zerolog/log" ) +type ExitError struct { + Code int +} + +func (e *ExitError) Error() string { + return fmt.Sprintf("project non-zero exit code: %d", e.Code) +} + type ProjectRunner struct { procConfMutex sync.Mutex project *types.Project @@ -47,7 +55,7 @@ func (p *ProjectRunner) init() { p.initProcessLogs() } -func (p *ProjectRunner) Run() int { +func (p *ProjectRunner) Run() error { p.runningProcesses = make(map[string]*Process) runOrder := []types.ProcessConfig{} err := p.project.WithProcesses([]string{}, func(process types.ProcessConfig) error { @@ -55,7 +63,7 @@ func (p *ProjectRunner) Run() int { return nil }) if err != nil { - log.Error().Msgf("Failed to build project run order: %s", err.Error()) + return fmt.Errorf("failed to build project run order: %e", err) } var nameOrder []string for _, v := range runOrder { @@ -75,7 +83,10 @@ func (p *ProjectRunner) Run() int { } p.waitGroup.Wait() log.Info().Msg("Project completed") - return p.exitCode + if p.exitCode != 0 { + err = &ExitError{p.exitCode} + } + return err } func (p *ProjectRunner) runProcess(config *types.ProcessConfig) { diff --git a/src/app/system_test.go b/src/app/system_test.go index e868dc8..80cde96 100644 --- a/src/app/system_test.go +++ b/src/app/system_test.go @@ -146,10 +146,10 @@ func TestSystem_TestComposeChainExit(t *testing.T) { t.Errorf(err.Error()) return } - exitCode := runner.Run() - want := 42 - if want != exitCode { - t.Errorf("Project.Run() = %v, want %v", exitCode, want) + err = runner.Run() + want := "project non-zero exit code: 42" + if want != err.Error() { + t.Errorf("Project.Run() = %v, want %v", err, want) } }) } diff --git a/src/cmd/project_runner.go b/src/cmd/project_runner.go index 6390a62..bfe3f05 100644 --- a/src/cmd/project_runner.go +++ b/src/cmd/project_runner.go @@ -38,16 +38,16 @@ func getProjectRunner(process []string, noDeps bool, mainProcess string, mainPro return runner } -func runProject(runner *app.ProjectRunner) { - exitCode := 0 +func runProject(runner *app.ProjectRunner) error { + var err error if *pcFlags.IsTuiEnabled { - exitCode = runTui(runner) + err = runTui(runner) } else { - exitCode = runHeadless(runner) + err = runHeadless(runner) } os.Remove(*pcFlags.UnixSocketPath) log.Info().Msg("Thank you for using process-compose") - os.Exit(exitCode) + return err } func setSignal(signalHandler func()) { @@ -60,23 +60,22 @@ func setSignal(signalHandler func()) { }() } -func runHeadless(project *app.ProjectRunner) int { +func runHeadless(project *app.ProjectRunner) error { setSignal(func() { _ = project.ShutDownProject() }) - exitCode := project.Run() - return exitCode + return project.Run() } -func runTui(project *app.ProjectRunner) int { +func runTui(project *app.ProjectRunner) error { go startTui(project) - exitCode := project.Run() + err := project.Run() if !*pcFlags.KeepTuiOn { tui.Stop() } else { tui.Wait() } - return exitCode + return err } func startTui(runner app.IProject) { diff --git a/src/cmd/root.go b/src/cmd/root.go index 07b0561..bb5cf4b 100644 --- a/src/cmd/root.go +++ b/src/cmd/root.go @@ -36,18 +36,18 @@ var ( pcFlags.PcThemeChanged = cmd.Flags().Changed(flagTheme) pcFlags.SortColumnChanged = cmd.Flags().Changed(flagSort) }, - RunE: run, + Run: run, } ) -func run(cmd *cobra.Command, args []string) error { +func run(cmd *cobra.Command, args []string) { defer func() { _ = logFile.Close() }() runner := getProjectRunner([]string{}, false, "", []string{}) startHttpServerIfEnabled(!*pcFlags.IsTuiEnabled, runner) - runProject(runner) - return nil + err := runProject(runner) + handleErrorAndExit(err) } // Execute adds all child commands to the root command and sets flags appropriately. @@ -137,6 +137,17 @@ func setupLogger() *os.File { return file } +// Logs and exits with a non-zero code if there are any errors. +func handleErrorAndExit(err error) { + if err != nil { + log.Error().Err(err) + if exitErr, ok := err.(*app.ExitError); ok { + os.Exit(exitErr.Code) + } + os.Exit(1) + } +} + func startHttpServerIfEnabled(useLogger bool, runner *app.ProjectRunner) { if !*pcFlags.NoServer { if *pcFlags.IsUnixSocket { diff --git a/src/cmd/run.go b/src/cmd/run.go index ebab098..b74fd32 100644 --- a/src/cmd/run.go +++ b/src/cmd/run.go @@ -40,7 +40,8 @@ Command line arguments, provided after --, are passed to the PROCESS.`, ) startHttpServerIfEnabled(false, runner) - runProject(runner) + err := runProject(runner) + handleErrorAndExit(err) }, } diff --git a/src/cmd/up.go b/src/cmd/up.go index f40ce83..8850c13 100644 --- a/src/cmd/up.go +++ b/src/cmd/up.go @@ -15,7 +15,8 @@ will start them and their dependencies only`, Run: func(cmd *cobra.Command, args []string) { runner := getProjectRunner(args, *pcFlags.NoDependencies, "", []string{}) startHttpServerIfEnabled(!*pcFlags.IsTuiEnabled, runner) - runProject(runner) + err := runProject(runner) + handleErrorAndExit(err) }, }