feat: initial recursive archive cataloging impl

Signed-off-by: Keith Zantow <keith.zantow@anchore.com>
This commit is contained in:
Keith Zantow
2026-09-10 08:45:36 -04:00
parent 0a93e60b16
commit 8c0d3747ce
85 changed files with 23477 additions and 868 deletions
+35 -1
View File
@@ -1,6 +1,8 @@
package bus
import (
"context"
"github.com/wagoodman/go-partybus"
"github.com/wagoodman/go-progress"
@@ -36,7 +38,39 @@ func Notify(message string) {
})
}
func StartCatalogerTask(info monitor.GenericTask, size int64, initialStage string) *monitor.TaskProgress {
type catalogerTaskProgressKey struct{}
// WithCatalogerTaskProgress returns a context whose cataloger tasks report into prog rather than
// starting a row of their own.
//
// Nested cataloging runs under such a context: re-running the cataloger pipeline against the
// contents of an archive is work that belongs to the progress of the task driving the recursion, not
// a second start of every cataloger. Starting rows again per archive floods a consumer's display,
// and where the new row carries an ID the consumer has already seen (every package cataloger does -
// the ID is the cataloger name) it replaces the live row for that ID. The replaced row is then never
// rendered again, its completion is never observed, and a UI that waits on every row it started - as
// the syft CLI does - blocks forever on teardown.
func WithCatalogerTaskProgress(ctx context.Context, prog *monitor.TaskProgress) context.Context {
return context.WithValue(ctx, catalogerTaskProgressKey{}, prog)
}
// catalogerTaskProgressFromContext returns the progress that cataloger tasks started under this
// context report into, or nil when they should start one of their own.
func catalogerTaskProgressFromContext(ctx context.Context) *monitor.TaskProgress {
prog, _ := ctx.Value(catalogerTaskProgressKey{}).(*monitor.TaskProgress)
return prog
}
func StartCatalogerTask(ctx context.Context, info monitor.GenericTask, size int64, initialStage string) *monitor.TaskProgress {
if parent := catalogerTaskProgressFromContext(ctx); parent != nil {
// the stage is shared, so what this cataloger is working on shows up on the row already on
// screen; the count is not, so this task completing does not complete the row it reports into
return &monitor.TaskProgress{
AtomicStage: parent.AtomicStage,
Manual: progress.NewManual(size),
}
}
t := &monitor.TaskProgress{
AtomicStage: progress.NewAtomicStage(initialStage),
Manual: progress.NewManual(size),