Add support for multiple output files in different formats (#732)

This commit is contained in:
Keith Zantow
2022-01-06 17:52:20 -05:00
committed by GitHub
parent 38c4b17847
commit 5e5312c72d
51 changed files with 642 additions and 243 deletions
+6 -7
View File
@@ -2,23 +2,22 @@ package ui
import (
"fmt"
"io"
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
"github.com/wagoodman/go-partybus"
)
// handleCatalogerPresenterReady is a UI function for processing the CatalogerFinished bus event, displaying the catalog
// via the given presenter to stdout.
func handleCatalogerPresenterReady(event partybus.Event, reportOutput io.Writer) error {
// handleExit is a UI function for processing the Exit bus event,
// and calling the given function to output the contents.
func handleExit(event partybus.Event) error {
// show the report to stdout
pres, err := syftEventParsers.ParsePresenterReady(event)
fn, err := syftEventParsers.ParseExit(event)
if err != nil {
return fmt.Errorf("bad CatalogerFinished event: %w", err)
}
if err := pres.Present(reportOutput); err != nil {
return fmt.Errorf("unable to show package catalog report: %w", err)
if err := fn(); err != nil {
return fmt.Errorf("unable to show package catalog report: %v", err)
}
return nil
}
+13 -15
View File
@@ -36,22 +36,20 @@ import (
// or in the shared ui package as a function on the main handler object. All handler functions should be completed
// processing an event before the ETUI exits (coordinated with a sync.WaitGroup)
type ephemeralTerminalUI struct {
unsubscribe func() error
handler *ui.Handler
waitGroup *sync.WaitGroup
frame *frame.Frame
logBuffer *bytes.Buffer
uiOutput *os.File
reportOutput io.Writer
unsubscribe func() error
handler *ui.Handler
waitGroup *sync.WaitGroup
frame *frame.Frame
logBuffer *bytes.Buffer
uiOutput *os.File
}
// NewEphemeralTerminalUI writes all events to a TUI and writes the final report to the given writer.
func NewEphemeralTerminalUI(reportWriter io.Writer) UI {
func NewEphemeralTerminalUI() UI {
return &ephemeralTerminalUI{
handler: ui.NewHandler(),
waitGroup: &sync.WaitGroup{},
uiOutput: os.Stderr,
reportOutput: reportWriter,
handler: ui.NewHandler(),
waitGroup: &sync.WaitGroup{},
uiOutput: os.Stderr,
}
}
@@ -82,12 +80,12 @@ func (h *ephemeralTerminalUI) Handle(event partybus.Event) error {
log.Errorf("unable to show %s event: %+v", event.Type, err)
}
case event.Type == syftEvent.PresenterReady:
// we need to close the screen now since signaling the the presenter is ready means that we
case event.Type == syftEvent.Exit:
// we need to close the screen now since signaling the sbom is ready means that we
// are about to write bytes to stdout, so we should reset the terminal state first
h.closeScreen(false)
if err := handleCatalogerPresenterReady(event, h.reportOutput); err != nil {
if err := handleExit(event); err != nil {
log.Errorf("unable to show %s event: %+v", event.Type, err)
}
+5 -10
View File
@@ -1,23 +1,18 @@
package ui
import (
"io"
"github.com/anchore/syft/internal/log"
syftEvent "github.com/anchore/syft/syft/event"
"github.com/wagoodman/go-partybus"
)
type loggerUI struct {
unsubscribe func() error
reportOutput io.Writer
unsubscribe func() error
}
// NewLoggerUI writes all events to the common application logger and writes the final report to the given writer.
func NewLoggerUI(reportWriter io.Writer) UI {
return &loggerUI{
reportOutput: reportWriter,
}
func NewLoggerUI() UI {
return &loggerUI{}
}
func (l *loggerUI) Setup(unsubscribe func() error) error {
@@ -27,11 +22,11 @@ func (l *loggerUI) Setup(unsubscribe func() error) error {
func (l loggerUI) Handle(event partybus.Event) error {
// ignore all events except for the final event
if event.Type != syftEvent.PresenterReady {
if event.Type != syftEvent.Exit {
return nil
}
if err := handleCatalogerPresenterReady(event, l.reportOutput); err != nil {
if err := handleExit(event); err != nil {
log.Warnf("unable to show catalog image finished event: %+v", err)
}
+3 -4
View File
@@ -4,7 +4,6 @@
package ui
import (
"io"
"os"
"runtime"
@@ -16,16 +15,16 @@ import (
// is intended to be used and the UIs that follow are meant to be attempted only in a fallback posture when there
// are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of
// the final SBOM report.
func Select(verbose, quiet bool, reportWriter io.Writer) (uis []UI) {
func Select(verbose, quiet bool) (uis []UI) {
isStdoutATty := term.IsTerminal(int(os.Stdout.Fd()))
isStderrATty := term.IsTerminal(int(os.Stderr.Fd()))
notATerminal := !isStderrATty && !isStdoutATty
switch {
case runtime.GOOS == "windows" || verbose || quiet || notATerminal || !isStderrATty:
uis = append(uis, NewLoggerUI(reportWriter))
uis = append(uis, NewLoggerUI())
default:
uis = append(uis, NewEphemeralTerminalUI(reportWriter), NewLoggerUI(reportWriter))
uis = append(uis, NewEphemeralTerminalUI())
}
return uis
+2 -6
View File
@@ -3,15 +3,11 @@
package ui
import (
"io"
)
// Select is responsible for determining the specific UI function given select user option, the current platform
// config values, and environment status (such as a TTY being present). The first UI in the returned slice of UIs
// is intended to be used and the UIs that follow are meant to be attempted only in a fallback posture when there
// are environmental problems (e.g. cannot write to the terminal). A writer is provided to capture the output of
// the final SBOM report.
func Select(verbose, quiet bool, reportWriter io.Writer) (uis []UI) {
return append(uis, NewLoggerUI(reportWriter))
func Select(verbose, quiet bool) (uis []UI) {
return append(uis, NewLoggerUI())
}