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  }