github.com/lineaje-labs/syft@v0.98.1-0.20231227153149-9e393f60ff1b/cmd/syft/internal/ui/event_writer.go (about) 1 package ui 2 3 import ( 4 "fmt" 5 "io" 6 "strings" 7 8 "github.com/charmbracelet/lipgloss" 9 "github.com/hashicorp/go-multierror" 10 "github.com/wagoodman/go-partybus" 11 12 "github.com/anchore/syft/syft/event" 13 "github.com/anchore/syft/syft/event/parsers" 14 "github.com/lineaje-labs/syft/internal/log" 15 ) 16 17 func writeEvents(out, err io.Writer, quiet bool, events ...partybus.Event) error { 18 handles := []struct { 19 event partybus.EventType 20 respectQuiet bool 21 writer io.Writer 22 dispatch func(writer io.Writer, events ...partybus.Event) error 23 }{ 24 { 25 event: event.CLIReport, 26 respectQuiet: false, 27 writer: out, 28 dispatch: writeReports, 29 }, 30 { 31 event: event.CLINotification, 32 respectQuiet: true, 33 writer: err, 34 dispatch: writeNotifications, 35 }, 36 { 37 event: event.CLIAppUpdateAvailable, 38 respectQuiet: true, 39 writer: err, 40 dispatch: writeAppUpdate, 41 }, 42 } 43 44 var errs error 45 for _, h := range handles { 46 if quiet && h.respectQuiet { 47 continue 48 } 49 50 for _, e := range events { 51 if e.Type != h.event { 52 continue 53 } 54 55 if err := h.dispatch(h.writer, e); err != nil { 56 errs = multierror.Append(errs, err) 57 } 58 } 59 } 60 return errs 61 } 62 63 func writeReports(writer io.Writer, events ...partybus.Event) error { 64 var reports []string 65 for _, e := range events { 66 _, report, err := parsers.ParseCLIReport(e) 67 if err != nil { 68 log.WithFields("error", err).Warn("failed to gather final report") 69 continue 70 } 71 72 // remove all whitespace padding from the end of the report 73 reports = append(reports, strings.TrimRight(report, "\n ")+"\n") 74 } 75 76 // prevent the double new-line at the end of the report 77 report := strings.Join(reports, "\n") 78 79 if _, err := fmt.Fprint(writer, report); err != nil { 80 return fmt.Errorf("failed to write final report to stdout: %w", err) 81 } 82 return nil 83 } 84 85 func writeNotifications(writer io.Writer, events ...partybus.Event) error { 86 // 13 = high intensity magenta (ANSI 16 bit code) 87 style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")) 88 89 for _, e := range events { 90 _, notification, err := parsers.ParseCLINotification(e) 91 if err != nil { 92 log.WithFields("error", err).Warn("failed to parse notification") 93 continue 94 } 95 96 if _, err := fmt.Fprintln(writer, style.Render(notification)); err != nil { 97 // don't let this be fatal 98 log.WithFields("error", err).Warn("failed to write final notifications") 99 } 100 } 101 return nil 102 } 103 104 func writeAppUpdate(writer io.Writer, events ...partybus.Event) error { 105 // 13 = high intensity magenta (ANSI 16 bit code) + italics 106 style := lipgloss.NewStyle().Foreground(lipgloss.Color("13")).Italic(true) 107 108 for _, e := range events { 109 updateCheck, err := parsers.ParseCLIAppUpdateAvailable(e) 110 if err != nil { 111 log.WithFields("error", err).Warn("failed to parse app update notification") 112 continue 113 } 114 115 if updateCheck.Current == updateCheck.New { 116 log.Tracef("update check event with identical versions: %s", updateCheck.Current) 117 continue 118 } 119 120 notice := fmt.Sprintf("A newer version of syft is available for download: %s (installed version is %s)", updateCheck.New, updateCheck.Current) 121 122 if _, err := fmt.Fprintln(writer, style.Render(notice)); err != nil { 123 // don't let this be fatal 124 log.WithFields("error", err).Warn("failed to write app update notification") 125 } 126 } 127 return nil 128 }