From 35699f6fdc6418f89ae88e94d32166a6a8cf9064 Mon Sep 17 00:00:00 2001 From: Alex Goodman Date: Thu, 13 Jul 2023 13:21:52 -0400 Subject: [PATCH] remove jotframe UI (#1932) Signed-off-by: Alex Goodman --- go.mod | 1 - go.sum | 6 - ui/deprecated.go | 783 ----------------------------------------------- 3 files changed, 790 deletions(-) delete mode 100644 ui/deprecated.go diff --git a/go.mod b/go.mod index 5f326b26997..452223b9549 100644 --- a/go.mod +++ b/go.mod @@ -40,7 +40,6 @@ require ( github.com/vifraa/gopom v0.2.1 github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 - github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb github.com/xeipuuv/gojsonschema v1.2.0 golang.org/x/mod v0.12.0 golang.org/x/net v0.12.0 diff --git a/go.sum b/go.sum index 3000e71559f..142f3dad7c3 100644 --- a/go.sum +++ b/go.sum @@ -419,7 +419,6 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4= @@ -466,7 +465,6 @@ github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.6/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= @@ -681,8 +679,6 @@ github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651 h1:jIVmlAFIq github.com/wagoodman/go-partybus v0.0.0-20230516145632-8ccac152c651/go.mod h1:b26F2tHLqaoRQf8DywqzVaV1MQ9yvjb0OMcNl7Nxu20= github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5 h1:lwgTsTy18nYqASnH58qyfRW/ldj7Gt2zzBvgYPzdA4s= github.com/wagoodman/go-progress v0.0.0-20230301185719-21920a456ad5/go.mod h1:jLXFoL31zFaHKAAyZUh+sxiTDFe1L1ZHrcK2T1itVKA= -github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb h1:Yz6VVOcLuWLAHYlJzTw7JKnWxdV/WXpug2X0quEzRnY= -github.com/wagoodman/jotframe v0.0.0-20211129225309-56b0d0a4aebb/go.mod h1:nDi3BAC5nEbVbg+WSJDHLbjHv0ZToq8nMPA97XMxF3E= github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM= github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= @@ -722,7 +718,6 @@ go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9i go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -949,7 +944,6 @@ golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= diff --git a/ui/deprecated.go b/ui/deprecated.go deleted file mode 100644 index fb542ca5d5c..00000000000 --- a/ui/deprecated.go +++ /dev/null @@ -1,783 +0,0 @@ -/* -Package ui provides all public UI elements intended to be repurposed in other applications. Specifically, a single -Handler object is provided to allow consuming applications (such as grype) to check if there are UI elements the handler -can respond to (given a specific event type) and handle the event in context of the given screen frame object. -*/ -package ui - -import ( - "bufio" - "container/list" - "context" - "errors" - "fmt" - "io" - "strings" - "sync" - "time" - - "github.com/dustin/go-humanize" - "github.com/gookit/color" - "github.com/wagoodman/go-partybus" - "github.com/wagoodman/go-progress" - "github.com/wagoodman/go-progress/format" - "github.com/wagoodman/jotframe/pkg/frame" - - stereoscopeEvent "github.com/anchore/stereoscope/pkg/event" - stereoEventParsers "github.com/anchore/stereoscope/pkg/event/parsers" - "github.com/anchore/stereoscope/pkg/image/docker" - "github.com/anchore/syft/internal" - syftEvent "github.com/anchore/syft/syft/event" - syftEventParsers "github.com/anchore/syft/syft/event/parsers" -) - -const maxBarWidth = 50 -const statusSet = SpinnerDotSet -const completedStatus = "✔" -const failedStatus = "✘" -const titleFormat = color.Bold -const subTitleFormat = color.Normal -const interval = 150 * time.Millisecond - -// StatusTitleColumn is the column index in a given row where status text will be displayed. -const StatusTitleColumn = 31 - -var ( - auxInfoFormat = color.HEX("#777777") - dockerPullCompletedColor = color.HEX("#fcba03") - dockerPullDownloadColor = color.HEX("#777777") - dockerPullExtractColor = color.White - dockerPullStageChars = strings.Split("▁▃▄▅▆▇█", "") - statusTitleTemplate = fmt.Sprintf(" %%s %%-%ds ", StatusTitleColumn) - subStatusTitleTemplate = fmt.Sprintf(" └── %%-%ds ", StatusTitleColumn-3) -) - -// Handler is an aggregated event handler for the set of supported events (PullDockerImage, ReadImage, FetchImage, PackageCatalogerStarted) -// Deprecated: use the bubbletea event handler in cmd/syft/ui/handler.go instead. -type Handler struct { -} - -// NewHandler returns an empty Handler -// Deprecated: use the bubbletea event handler in cmd/syft/ui/handler.go instead. -func NewHandler() *Handler { - return &Handler{} -} - -// RespondsTo indicates if the handler is capable of handling the given event. -// Deprecated: use the bubbletea event handler in cmd/syft/ui/handler.go instead. -func (r *Handler) RespondsTo(event partybus.Event) bool { - switch event.Type { - case stereoscopeEvent.PullDockerImage, - stereoscopeEvent.ReadImage, - stereoscopeEvent.FetchImage, - syftEvent.PackageCatalogerStarted, - syftEvent.SecretsCatalogerStarted, - syftEvent.FileDigestsCatalogerStarted, - syftEvent.FileMetadataCatalogerStarted, - syftEvent.FileIndexingStarted, - syftEvent.AttestationStarted, - syftEvent.CatalogerTaskStarted: - return true - default: - return false - } -} - -// Handle calls the specific event handler for the given event within the context of the screen frame. -// Deprecated: use the bubbletea event handler in cmd/syft/ui/handler.go instead. -func (r *Handler) Handle(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - switch event.Type { - case stereoscopeEvent.PullDockerImage: - return PullDockerImageHandler(ctx, fr, event, wg) - - case stereoscopeEvent.ReadImage: - return ReadImageHandler(ctx, fr, event, wg) - - case stereoscopeEvent.FetchImage: - return FetchImageHandler(ctx, fr, event, wg) - - case syftEvent.PackageCatalogerStarted: - return PackageCatalogerStartedHandler(ctx, fr, event, wg) - - case syftEvent.SecretsCatalogerStarted: - return SecretsCatalogerStartedHandler(ctx, fr, event, wg) - - case syftEvent.FileDigestsCatalogerStarted: - return FileDigestsCatalogerStartedHandler(ctx, fr, event, wg) - - case syftEvent.FileMetadataCatalogerStarted: - return FileMetadataCatalogerStartedHandler(ctx, fr, event, wg) - - case syftEvent.FileIndexingStarted: - return FileIndexingStartedHandler(ctx, fr, event, wg) - - case syftEvent.AttestationStarted: - return AttestationStartedHandler(ctx, fr, event, wg) - - case syftEvent.CatalogerTaskStarted: - return CatalogerTaskStartedHandler(ctx, fr, event, wg) - } - return nil -} - -const ( - SpinnerDotSet = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" -) - -type spinner struct { - index int - charset []string - lock sync.Mutex -} - -func newSpinner(charset string) spinner { - return spinner{ - charset: strings.Split(charset, ""), - } -} - -func (s *spinner) Current() string { - s.lock.Lock() - defer s.lock.Unlock() - - return s.charset[s.index] -} - -func (s *spinner) Next() string { - s.lock.Lock() - defer s.lock.Unlock() - c := s.charset[s.index] - s.index++ - if s.index >= len(s.charset) { - s.index = 0 - } - return c -} - -// startProcess is a helper function for providing common elements for long-running UI elements (such as a -// progress bar formatter and status spinner) -func startProcess() (format.Simple, *spinner) { - width, _ := frame.GetTerminalSize() - barWidth := int(0.25 * float64(width)) - if barWidth > maxBarWidth { - barWidth = maxBarWidth - } - formatter := format.NewSimpleWithTheme(barWidth, format.HeavyNoBarTheme, format.ColorCompleted, format.ColorTodo) - spinner := newSpinner(statusSet) - - return formatter, &spinner -} - -// formatDockerPullPhase returns a single character that represents the status of a layer pull. -func formatDockerPullPhase(phase docker.PullPhase, inputStr string) string { - switch phase { - case docker.WaitingPhase: - // ignore any progress related to waiting - return " " - case docker.PullingFsPhase, docker.DownloadingPhase: - return dockerPullDownloadColor.Sprint(inputStr) - case docker.DownloadCompletePhase: - return dockerPullDownloadColor.Sprint(dockerPullStageChars[len(dockerPullStageChars)-1]) - case docker.ExtractingPhase: - return dockerPullExtractColor.Sprint(inputStr) - case docker.VerifyingChecksumPhase, docker.PullCompletePhase: - return dockerPullCompletedColor.Sprint(inputStr) - case docker.AlreadyExistsPhase: - return dockerPullCompletedColor.Sprint(dockerPullStageChars[len(dockerPullStageChars)-1]) - default: - return inputStr - } -} - -// formatDockerImagePullStatus writes the docker image pull status summarized into a single line for the given state. -func formatDockerImagePullStatus(pullStatus *docker.PullStatus, spinner *spinner, line *frame.Line) { - var size, current uint64 - - title := titleFormat.Sprint("Pulling image") - - layers := pullStatus.Layers() - status := make(map[docker.LayerID]docker.LayerState) - completed := make([]string, len(layers)) - - // fetch the current state - for idx, layer := range layers { - completed[idx] = " " - status[layer] = pullStatus.Current(layer) - } - - numCompleted := 0 - for idx, layer := range layers { - prog := status[layer].PhaseProgress - current := prog.Current() - size := prog.Size() - - if progress.IsCompleted(prog) { - input := dockerPullStageChars[len(dockerPullStageChars)-1] - completed[idx] = formatDockerPullPhase(status[layer].Phase, input) - } else if current != 0 { - var ratio float64 - switch { - case current == 0 || size < 0: - ratio = 0 - case current >= size: - ratio = 1 - default: - ratio = float64(current) / float64(size) - } - - i := int(ratio * float64(len(dockerPullStageChars)-1)) - input := dockerPullStageChars[i] - completed[idx] = formatDockerPullPhase(status[layer].Phase, input) - } - - if progress.IsErrCompleted(status[layer].DownloadProgress.Error()) { - numCompleted++ - } - } - - for _, layer := range layers { - prog := status[layer].DownloadProgress - size += uint64(prog.Size()) - current += uint64(prog.Current()) - } - - var progStr, auxInfo string - if len(layers) > 0 { - render := strings.Join(completed, "") - prefix := dockerPullCompletedColor.Sprintf("%d Layers", len(layers)) - auxInfo = auxInfoFormat.Sprintf("[%s / %s]", humanize.Bytes(current), humanize.Bytes(size)) - if len(layers) == numCompleted { - auxInfo = auxInfoFormat.Sprintf("[%s] Extracting...", humanize.Bytes(size)) - } - - progStr = fmt.Sprintf("%s▕%s▏", prefix, render) - } - - spin := color.Magenta.Sprint(spinner.Next()) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s%s", spin, title, progStr, auxInfo)) -} - -// PullDockerImageHandler periodically writes a formatted line widget representing a docker image pull event. -func PullDockerImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - _, pullStatus, err := stereoEventParsers.ParsePullDockerImage(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - _, spinner := startProcess() - - go func() { - defer wg.Done() - - loop: - for { - select { - case <-ctx.Done(): - break loop - case <-time.After(interval): - formatDockerImagePullStatus(pullStatus, spinner, line) - if pullStatus.Complete() { - break loop - } - } - } - - if pullStatus.Complete() { - spin := color.Green.Sprint(completedStatus) - title := titleFormat.Sprint("Pulled image") - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) - } - }() - return err -} - -// FetchImageHandler periodically writes a the image save and write-to-disk process in the form of a progress bar. -func FetchImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - _, prog, err := stereoEventParsers.ParseFetchImage(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, interval) - title := titleFormat.Sprint("Loading image") - - formatFn := func(p progress.Progress) { - progStr, err := formatter.Format(p) - spin := color.Magenta.Sprint(spinner.Next()) - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - auxInfo := auxInfoFormat.Sprintf("[%s]", prog.Stage()) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s %s", spin, title, progStr, auxInfo)) - } - } - - go func() { - defer wg.Done() - - formatFn(progress.Progress{}) - for p := range stream { - formatFn(p) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprint("Loaded image") - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) - }() - return err -} - -// ReadImageHandler periodically writes a the image read/parse/build-tree status in the form of a progress bar. -func ReadImageHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - _, prog, err := stereoEventParsers.ParseReadImage(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - - wg.Add(1) - - formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, interval) - title := titleFormat.Sprint("Parsing image") - - formatFn := func(p progress.Progress) { - progStr, err := formatter.Format(p) - spin := color.Magenta.Sprint(spinner.Next()) - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, progStr)) - } - } - - go func() { - defer wg.Done() - - formatFn(progress.Progress{}) - for p := range stream { - formatFn(p) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprint("Parsed image") - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) - }() - - return nil -} - -// PackageCatalogerStartedHandler periodically writes catalog statistics to a single line. -func PackageCatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - monitor, err := syftEventParsers.ParsePackageCatalogerStarted(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - - wg.Add(1) - - _, spinner := startProcess() - stream := progress.StreamMonitors(ctx, []progress.Monitorable{monitor.FilesProcessed, monitor.PackagesDiscovered}, interval) - title := titleFormat.Sprint("Cataloging packages") - - formatFn := func(p int64) { - spin := color.Magenta.Sprint(spinner.Next()) - auxInfo := auxInfoFormat.Sprintf("[packages %d]", p) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo)) - } - - go func() { - defer wg.Done() - - formatFn(0) - for p := range stream { - formatFn(p[1]) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprint("Cataloged packages") - auxInfo := auxInfoFormat.Sprintf("[%d packages]", monitor.PackagesDiscovered.Current()) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo)) - }() - - return nil -} - -// SecretsCatalogerStartedHandler shows the intermittent secrets searching progress. -func SecretsCatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - prog, err := syftEventParsers.ParseSecretsCatalogingStarted(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, interval) - title := titleFormat.Sprint("Cataloging secrets") - - formatFn := func(p progress.Progress) { - progStr, err := formatter.Format(p) - spin := color.Magenta.Sprint(spinner.Next()) - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - auxInfo := auxInfoFormat.Sprintf("[%d secrets]", prog.SecretsDiscovered.Current()) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s %s", spin, title, progStr, auxInfo)) - } - } - - go func() { - defer wg.Done() - - formatFn(progress.Progress{}) - for p := range stream { - formatFn(p) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprint("Cataloged secrets") - auxInfo := auxInfoFormat.Sprintf("[%d secrets]", prog.SecretsDiscovered.Current()) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo)) - }() - return err -} - -// FileMetadataCatalogerStartedHandler shows the intermittent secrets searching progress. -// -//nolint:dupl -func FileMetadataCatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - prog, err := syftEventParsers.ParseFileMetadataCatalogingStarted(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, interval) - title := titleFormat.Sprint("Cataloging file metadata") - - formatFn := func(p progress.Progress) { - progStr, err := formatter.Format(p) - spin := color.Magenta.Sprint(spinner.Next()) - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, progStr)) - } - } - - go func() { - defer wg.Done() - - formatFn(progress.Progress{}) - for p := range stream { - formatFn(p) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprint("Cataloged file metadata") - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) - }() - return err -} - -// FileIndexingStartedHandler shows the intermittent indexing progress from a directory resolver. -func FileIndexingStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - path, prog, err := syftEventParsers.ParseFileIndexingStarted(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - _, spinner := startProcess() - stream := progress.Stream(ctx, prog, interval) - title := titleFormat.Sprintf("Indexing %s", path) - - formatFn := func(_ progress.Progress) { - spin := color.Magenta.Sprint(spinner.Next()) - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - auxInfo := auxInfoFormat.Sprintf("[file: %s]", internal.TruncateMiddleEllipsis(prog.Stage(), 100)) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo)) - } - } - - go func() { - defer wg.Done() - - formatFn(progress.Progress{}) - for p := range stream { - formatFn(p) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprintf("Indexed %s", path) - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) - }() - return err -} - -// FileMetadataCatalogerStartedHandler shows the intermittent secrets searching progress. -// -//nolint:dupl -func FileDigestsCatalogerStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - prog, err := syftEventParsers.ParseFileDigestsCatalogingStarted(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - formatter, spinner := startProcess() - stream := progress.Stream(ctx, prog, interval) - title := titleFormat.Sprint("Cataloging file digests") - - formatFn := func(p progress.Progress) { - progStr, err := formatter.Format(p) - spin := color.Magenta.Sprint(spinner.Next()) - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, progStr)) - } - } - - go func() { - defer wg.Done() - - formatFn(progress.Progress{}) - for p := range stream { - formatFn(p) - } - - spin := color.Green.Sprint(completedStatus) - title = titleFormat.Sprint("Cataloged file digests") - _, _ = io.WriteString(line, fmt.Sprintf(statusTitleTemplate, spin, title)) - }() - return err -} - -// AttestationStartedHandler takes bytes from a event.ShellOutput and publishes them to the frame. -// -//nolint:funlen,gocognit -func AttestationStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - reader, prog, taskInfo, err := syftEventParsers.ParseAttestationStartedEvent(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - titleLine, err := fr.Append() - if err != nil { - return err - } - wg.Add(2) - - _, spinner := startProcess() - - title := titleFormat.Sprintf(taskInfo.Title.WhileRunning) - - s := bufio.NewScanner(reader) - l := list.New() - - formatFn := func() { - auxInfo := auxInfoFormat.Sprintf("[running %s]", taskInfo.Context) - spin := color.Magenta.Sprint(spinner.Next()) - _, _ = io.WriteString(titleLine, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo)) - } - - formatFn() - var failed bool - formatComplete := func(aux string) { - spin := color.Green.Sprint(completedStatus) - if failed { - spin = color.Red.Sprint(failedStatus) - aux = prog.Error().Error() - } else { - title = titleFormat.Sprintf(taskInfo.Title.OnSuccess) - } - - auxInfo := auxInfoFormat.Sprintf("[%s]", aux) - - _, _ = io.WriteString(titleLine, fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo)) - } - - endWg := &sync.WaitGroup{} - endWg.Add(1) - - go func() { - defer wg.Done() - defer endWg.Done() - - stream := progress.Stream(ctx, prog, interval) - for range stream { - formatFn() - } - err := prog.Error() - if err != nil && !errors.Is(err, io.EOF) { - failed = true - } - }() - - go func() { - defer wg.Done() - - var tlogEntry string - - // only show the last 5 lines of the shell output - for s.Scan() { - line, _ := fr.Append() - if l.Len() > 5 { - elem := l.Front() - line, ok := elem.Value.(*frame.Line) - if !ok { - continue - } - err = line.Remove() - if err != nil { - return - } - l.Remove(elem) - } - l.PushBack(line) - text := s.Text() - if strings.Contains(text, "tlog entry created with index") { - tlogEntry = text - } else { - // no tlog entry create so user used personal PKI - tlogEntry = "signed attestation using provided key" - } - _, err = line.Write([]byte(fmt.Sprintf(" %s %s", auxInfoFormat.Sprintf("░░"), text))) - if err != nil { - return - } - } - - endWg.Wait() - - if !failed { - // roll up logs into completed status (only if successful) - for e := l.Back(); e != nil; e = e.Prev() { - line, ok := e.Value.(*frame.Line) - if !ok { - continue - } - err = line.Remove() - if err != nil { - return - } - } - } - - formatComplete(tlogEntry) - }() - return nil -} - -// CatalogerTaskStartedHandler shows the intermittent progress for a cataloger subprocess messages -func CatalogerTaskStartedHandler(ctx context.Context, fr *frame.Frame, event partybus.Event, wg *sync.WaitGroup) error { - prog, err := syftEventParsers.ParseCatalogerTaskStarted(event) - if err != nil { - return fmt.Errorf("bad %s event: %w", event.Type, err) - } - - line, err := fr.Append() - if err != nil { - return err - } - wg.Add(1) - - stream := progress.Stream(ctx, prog.GetMonitor(), interval) - - _, spinner := startProcess() - - formatLine := func(complete bool, auxInfo string) string { - title := prog.Title - if complete && prog.TitleOnCompletion != "" { - title = prog.TitleOnCompletion - } - if prog.SubStatus { - title = subTitleFormat.Sprintf("%s", title) - if auxInfo == "" { - return fmt.Sprintf(subStatusTitleTemplate, title) - } - return fmt.Sprintf(subStatusTitleTemplate+"%s", title, auxInfo) - } - - spin := color.Magenta.Sprint(spinner.Next()) - if complete { - spin = color.Green.Sprint(completedStatus) - } - title = titleFormat.Sprintf("%s", title) - if auxInfo == "" { - return fmt.Sprintf(statusTitleTemplate, spin, title) - } - return fmt.Sprintf(statusTitleTemplate+"%s", spin, title, auxInfo) - } - - formatFn := func() { - if err != nil { - _, _ = io.WriteString(line, fmt.Sprintf("Error: %+v", err)) - } else { - auxInfo := auxInfoFormat.Sprintf("[%s]", internal.TruncateMiddleEllipsis(prog.GetValue(), 100)) - _, _ = io.WriteString(line, formatLine(false, auxInfo)) - } - } - - go func() { - defer wg.Done() - - formatFn() - for range stream { - formatFn() - } - - if prog.RemoveOnCompletion { - _ = fr.Remove(line) - } else { - _, _ = io.WriteString(line, formatLine(true, "")) - } - }() - return err -}