diff --git a/cli/internal/runsummary/format_execution_summary.go b/cli/internal/runsummary/format_execution_summary.go index 37092beb06a1b9..6c9911a0d97a8f 100644 --- a/cli/internal/runsummary/format_execution_summary.go +++ b/cli/internal/runsummary/format_execution_summary.go @@ -1,7 +1,9 @@ package runsummary import ( + "fmt" "os" + "strings" "time" "github.com/fatih/color" @@ -16,6 +18,7 @@ func (rsm *Meta) printExecutionSummary() { attempted := summary.ExecutionSummary.attempted successful := summary.ExecutionSummary.cached + summary.ExecutionSummary.success + failed := rsm.RunSummary.getFailedTasks() // Note: ExecutionSummary.failure exists, but we need the task names cached := summary.ExecutionSummary.cached // TODO: can we use a method on ExecutionSummary here? duration := time.Since(summary.ExecutionSummary.startedAt).Truncate(time.Millisecond) @@ -32,39 +35,58 @@ func (rsm *Meta) printExecutionSummary() { } } - if attempted == 0 { - ui.Output("") // Clear the line - ui.Warn("No tasks were executed as part of this run.") + lineData := []summaryLine{ + {header: "Tasks", trailer: util.Sprintf("${BOLD_GREEN}%v successful${RESET}${GRAY}, %v total", successful, attempted)}, + {header: "Cached", trailer: util.Sprintf("%v cached${RESET}${GRAY}, %v total", cached, attempted)}, + {header: "Time", trailer: util.Sprintf("%v${RESET} %v", duration, maybeFullTurbo)}, } - ui.Output("") // Clear the line - spacer := " " // 4 chars - - var lines []string - - // The only difference between these two branches is that when there is a run summary - // we print the path to that file and we adjust the whitespace in the printed text so it aligns. - // We could just always align to account for the summary line, but that would require a whole - // bunch of test output assertions to change. if rsm.getPath().FileExists() { - lines = []string{ - util.Sprintf("${BOLD} Tasks:${BOLD_GREEN}%s%v successful${RESET}${GRAY}, %v total${RESET}", spacer, successful, attempted), - util.Sprintf("${BOLD} Cached:%s%v cached${RESET}${GRAY}, %v total${RESET}", spacer, cached, attempted), - util.Sprintf("${BOLD} Time:%s%v${RESET} %v${RESET}", spacer, duration, maybeFullTurbo), - util.Sprintf("${BOLD}Summary:%s%s${RESET}", spacer, rsm.getPath()), + l := summaryLine{header: "Summary", trailer: util.Sprintf("%s", rsm.getPath())} + lineData = append(lineData, l) + } + + if len(failed) > 0 { + formatted := []string{} + for _, t := range failed { + formatted = append(formatted, util.Sprintf("${BOLD_RED}%s${RESET}", t.TaskID)) } - } else { - lines = []string{ - util.Sprintf("${BOLD} Tasks:${BOLD_GREEN}%s%v successful${RESET}${GRAY}, %v total${RESET}", spacer, successful, attempted), - util.Sprintf("${BOLD}Cached:%s%v cached${RESET}${GRAY}, %v total${RESET}", spacer, cached, attempted), - util.Sprintf("${BOLD} Time:%s%v${RESET} %v${RESET}", spacer, duration, maybeFullTurbo), + l := summaryLine{header: "Failed", trailer: strings.Join(formatted, ", ")} + lineData = append(lineData, l) + } + + // Some info we need for left padding + maxlength := 0 + for _, sl := range lineData { + if len(sl.header) > maxlength { + maxlength = len(sl.header) } } - // Print the real thing + minPadding := 1 + lines := []string{} + for _, sl := range lineData { + paddedHeader := fmt.Sprintf("%*s", maxlength+minPadding, sl.header) + line := util.Sprintf("${BOLD}%s: %s${RESET}", paddedHeader, sl.trailer) + lines = append(lines, line) + } + + // Print the lines to terminal + if attempted == 0 { + ui.Output("") // Clear the line + ui.Warn("No tasks were executed as part of this run.") + } + + ui.Output("") // Clear the line + for _, line := range lines { ui.Output(line) } ui.Output("") } + +type summaryLine struct { + header string + trailer string +} diff --git a/cli/internal/runsummary/run_summary.go b/cli/internal/runsummary/run_summary.go index b1770204ce4ba8..51ee5c38fe0b4f 100644 --- a/cli/internal/runsummary/run_summary.go +++ b/cli/internal/runsummary/run_summary.go @@ -230,6 +230,17 @@ func (summary *RunSummary) TrackTask(taskID string) (func(outcome executionEvent return summary.ExecutionSummary.run(taskID) } +func (summary *RunSummary) getFailedTasks() []*TaskSummary { + failed := []*TaskSummary{} + + for _, t := range summary.Tasks { + if *t.Execution.exitCode != 0 { + failed = append(failed, t) + } + } + return failed +} + // Save saves the run summary to a file func (rsm *Meta) save() error { json, err := rsm.FormatJSON()