Skip to content

Commit 2bda086

Browse files
authored
Add ability to scan snaps (as a source) (#3929)
1 parent 4eb8ba4 commit 2bda086

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+5060
-175
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
[TestHandler_handlePullSourceStarted/snap_download_in_progress - 1]
3+
Downloading snap file... ━━━━━━━━━━━━━━━━━━━━ example-app_1.0_amd64.snap
4+
---
5+
6+
[TestHandler_handlePullSourceStarted/snap_download_complete - 1]
7+
Snap downloaded successfully example-app_1.0_amd64.snap
8+
---

cmd/syft/cli/ui/handle_cataloger_task_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
2626
{
2727
name: "cataloging task in progress",
2828
eventFn: func(t *testing.T) partybus.Event {
29-
value := &monitor.CatalogerTaskProgress{
29+
value := &monitor.TaskProgress{
3030
AtomicStage: progress.NewAtomicStage("some stage"),
3131
Manual: progress.NewManual(100),
3232
}
@@ -48,7 +48,7 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
4848
{
4949
name: "cataloging sub task in progress",
5050
eventFn: func(t *testing.T) partybus.Event {
51-
value := &monitor.CatalogerTaskProgress{
51+
value := &monitor.TaskProgress{
5252
AtomicStage: progress.NewAtomicStage("some stage"),
5353
Manual: progress.NewManual(100),
5454
}
@@ -71,7 +71,7 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
7171
{
7272
name: "cataloging sub task complete",
7373
eventFn: func(t *testing.T) partybus.Event {
74-
value := &monitor.CatalogerTaskProgress{
74+
value := &monitor.TaskProgress{
7575
AtomicStage: progress.NewAtomicStage("some stage"),
7676
Manual: progress.NewManual(100),
7777
}
@@ -94,7 +94,7 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
9494
{
9595
name: "cataloging sub task complete -- hide stage",
9696
eventFn: func(t *testing.T) partybus.Event {
97-
value := &monitor.CatalogerTaskProgress{
97+
value := &monitor.TaskProgress{
9898
AtomicStage: progress.NewAtomicStage("some stage"),
9999
Manual: progress.NewManual(100),
100100
}
@@ -117,7 +117,7 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
117117
{
118118
name: "cataloging sub task complete with removal",
119119
eventFn: func(t *testing.T) partybus.Event {
120-
value := &monitor.CatalogerTaskProgress{
120+
value := &monitor.TaskProgress{
121121
AtomicStage: progress.NewAtomicStage("some stage"),
122122
Manual: progress.NewManual(100),
123123
}
@@ -162,7 +162,7 @@ func TestHandler_handleCatalogerTaskStarted(t *testing.T) {
162162
}
163163

164164
// note: this line / event is not under test, only needed to show a sub status
165-
kickoffEvent := &monitor.CatalogerTaskProgress{
165+
kickoffEvent := &monitor.TaskProgress{
166166
AtomicStage: progress.NewAtomicStage(""),
167167
Manual: progress.NewManual(-1),
168168
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package ui
2+
3+
import (
4+
tea "github.com/charmbracelet/bubbletea"
5+
"github.com/wagoodman/go-partybus"
6+
7+
"github.com/anchore/bubbly/bubbles/taskprogress"
8+
"github.com/anchore/syft/internal/log"
9+
syftEventParsers "github.com/anchore/syft/syft/event/parsers"
10+
)
11+
12+
func (m *Handler) handlePullSourceStarted(e partybus.Event) []tea.Model {
13+
prog, info, err := syftEventParsers.ParsePullSourceStarted(e)
14+
if err != nil {
15+
log.WithFields("error", err).Debug("unable to parse event")
16+
return nil
17+
}
18+
19+
tsk := m.newTaskProgress(
20+
taskprogress.Title{
21+
Default: info.Title.Default,
22+
Running: info.Title.WhileRunning,
23+
Success: info.Title.OnSuccess,
24+
},
25+
taskprogress.WithStagedProgressable(prog),
26+
)
27+
28+
tsk.HideOnSuccess = info.HideOnSuccess
29+
tsk.HideStageOnSuccess = info.HideStageOnSuccess
30+
tsk.HideProgressOnSuccess = true
31+
32+
if info.Context != "" {
33+
tsk.Context = []string{info.Context}
34+
}
35+
36+
return []tea.Model{tsk}
37+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package ui
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
tea "github.com/charmbracelet/bubbletea"
8+
"github.com/gkampitakis/go-snaps/snaps"
9+
"github.com/stretchr/testify/require"
10+
"github.com/wagoodman/go-partybus"
11+
"github.com/wagoodman/go-progress"
12+
13+
"github.com/anchore/bubbly/bubbles/taskprogress"
14+
"github.com/anchore/syft/syft/event"
15+
"github.com/anchore/syft/syft/event/monitor"
16+
)
17+
18+
func TestHandler_handlePullSourceStarted(t *testing.T) {
19+
20+
tests := []struct {
21+
name string
22+
eventFn func(*testing.T) partybus.Event
23+
iterations int
24+
}{
25+
{
26+
name: "snap download in progress",
27+
eventFn: func(t *testing.T) partybus.Event {
28+
stage := progress.NewAtomicStage("")
29+
manual := progress.NewManual(0)
30+
manual.SetTotal(1000000) // 1MB file
31+
manual.Set(250000) // 25% downloaded
32+
33+
taskProg := &monitor.TaskProgress{
34+
AtomicStage: stage,
35+
Manual: manual,
36+
}
37+
38+
genericTask := monitor.GenericTask{
39+
Title: monitor.Title{
40+
Default: "Downloading snap",
41+
WhileRunning: "Downloading snap file...",
42+
OnSuccess: "Snap downloaded",
43+
},
44+
Context: "example-app_1.0_amd64.snap",
45+
HideOnSuccess: false,
46+
HideStageOnSuccess: true,
47+
ID: "snap-download-123",
48+
}
49+
50+
return partybus.Event{
51+
Type: event.PullSourceStarted,
52+
Source: genericTask,
53+
Value: taskProg,
54+
}
55+
},
56+
iterations: 5,
57+
},
58+
{
59+
name: "snap download complete",
60+
eventFn: func(t *testing.T) partybus.Event {
61+
stage := progress.NewAtomicStage("")
62+
manual := progress.NewManual(0)
63+
manual.SetTotal(1000000) // 1MB file
64+
manual.Set(1000000) // 100% downloaded
65+
manual.SetCompleted()
66+
67+
taskProg := &monitor.TaskProgress{
68+
AtomicStage: stage,
69+
Manual: manual,
70+
}
71+
72+
genericTask := monitor.GenericTask{
73+
Title: monitor.Title{
74+
Default: "Downloading snap",
75+
WhileRunning: "Downloading snap file...",
76+
OnSuccess: "Snap downloaded successfully",
77+
},
78+
Context: "example-app_1.0_amd64.snap",
79+
HideOnSuccess: false,
80+
HideStageOnSuccess: true,
81+
ID: "snap-download-123",
82+
}
83+
84+
return partybus.Event{
85+
Type: event.PullSourceStarted,
86+
Source: genericTask,
87+
Value: taskProg,
88+
}
89+
},
90+
iterations: 3,
91+
},
92+
}
93+
for _, tt := range tests {
94+
t.Run(tt.name, func(t *testing.T) {
95+
event := tt.eventFn(t)
96+
handler := New(DefaultHandlerConfig())
97+
handler.WindowSize = tea.WindowSizeMsg{
98+
Width: 100,
99+
Height: 80,
100+
}
101+
102+
models := handler.handlePullSourceStarted(event)
103+
require.Len(t, models, 1)
104+
model := models[0]
105+
106+
tsk, ok := model.(taskprogress.Model)
107+
require.True(t, ok)
108+
109+
gotModel := runModel(t, tsk, tt.iterations, taskprogress.TickMsg{
110+
Time: time.Now(),
111+
Sequence: tsk.Sequence(),
112+
ID: tsk.ID(),
113+
})
114+
115+
got := gotModel.View()
116+
117+
t.Log(got)
118+
snaps.MatchSnapshot(t, got)
119+
})
120+
}
121+
}

cmd/syft/cli/ui/handler.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ func New(cfg HandlerConfig) *Handler {
5757
stereoscopeEvent.FetchImage: simpleHandler(h.handleFetchImage),
5858
syftEvent.FileIndexingStarted: simpleHandler(h.handleFileIndexingStarted),
5959
syftEvent.AttestationStarted: simpleHandler(h.handleAttestationStarted),
60+
syftEvent.PullSourceStarted: simpleHandler(h.handlePullSourceStarted),
6061
syftEvent.CatalogerTaskStarted: h.handleCatalogerTaskStarted,
6162
})
6263

cmd/syft/internal/clio_setup_config.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func AppClioSetupConfig(id clio.Identification, out io.Writer) *clio.SetupConfig
4444
redact.Set(state.RedactStore)
4545

4646
log.Set(state.Logger)
47-
stereoscope.SetLogger(state.Logger)
47+
stereoscope.SetLogger(state.Logger.Nested("from", "stereoscope"))
4848
return nil
4949
},
5050
).

go.mod

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ require (
3535
github.com/charmbracelet/lipgloss v1.1.0
3636
github.com/dave/jennifer v1.7.1
3737
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
38+
github.com/diskfs/go-diskfs v1.6.1-0.20250601133945-2af1c7ece24c
3839
github.com/distribution/reference v0.6.0
3940
github.com/dustin/go-humanize v1.0.1
4041
github.com/elliotchance/phpserialize v1.4.0
@@ -51,6 +52,8 @@ require (
5152
github.com/google/licensecheck v0.3.1
5253
github.com/google/uuid v1.6.0
5354
github.com/gookit/color v1.5.4
55+
github.com/hashicorp/go-cleanhttp v0.5.2
56+
github.com/hashicorp/go-getter v1.7.8
5457
github.com/hashicorp/go-multierror v1.1.1
5558
github.com/hashicorp/hcl/v2 v2.23.0
5659
github.com/iancoleman/strcase v0.3.0
@@ -93,6 +96,12 @@ require (
9396
)
9497

9598
require (
99+
cloud.google.com/go v0.116.0 // indirect
100+
cloud.google.com/go/auth v0.9.9 // indirect
101+
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
102+
cloud.google.com/go/compute/metadata v0.7.0 // indirect
103+
cloud.google.com/go/iam v1.2.2 // indirect
104+
cloud.google.com/go/storage v1.43.0 // indirect
96105
dario.cat/mergo v1.0.1 // indirect
97106
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
98107
github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 // indirect
@@ -103,14 +112,17 @@ require (
103112
github.com/ProtonMail/go-crypto v1.2.0 // indirect
104113
github.com/STARRY-S/zip v0.2.1 // indirect
105114
github.com/agext/levenshtein v1.2.1 // indirect; indirectt
115+
github.com/anchore/go-lzo v0.1.0 // indirect
106116
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 // indirect
107117
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect
108118
github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect
109119
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
110120
github.com/aquasecurity/go-version v0.0.1 // indirect
111121
github.com/atotto/clipboard v0.1.4 // indirect
122+
github.com/aws/aws-sdk-go v1.44.122 // indirect
112123
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
113124
github.com/becheran/wildmatch-go v1.0.0 // indirect
125+
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
114126
github.com/bodgit/plumbing v1.3.0 // indirect
115127
github.com/bodgit/sevenzip v1.6.0 // indirect
116128
github.com/bodgit/windows v1.0.1 // indirect
@@ -153,17 +165,23 @@ require (
153165
github.com/go-logr/logr v1.4.3 // indirect
154166
github.com/go-logr/stdr v1.2.2 // indirect
155167
github.com/go-restruct/restruct v1.2.0-alpha // indirect
156-
github.com/goccy/go-yaml v1.18.0 // indirect
168+
github.com/goccy/go-yaml v1.18.0
157169
github.com/gogo/protobuf v1.3.2 // indirect
158170
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
159171
github.com/golang/snappy v0.0.4 // indirect
160172
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e // indirect
173+
github.com/google/s2a-go v0.1.8 // indirect
174+
github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
175+
github.com/googleapis/gax-go/v2 v2.13.0 // indirect
161176
github.com/hashicorp/errwrap v1.1.0 // indirect
177+
github.com/hashicorp/go-safetemp v1.0.0 // indirect
178+
github.com/hashicorp/go-version v1.6.0 // indirect
162179
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
163180
github.com/huandu/xstrings v1.5.0 // indirect
164181
github.com/iancoleman/orderedmap v0.0.0-20190318233801-ac98e3ecb4b0 // indirect
165182
github.com/inconshreveable/mousetrap v1.1.0 // indirect
166183
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
184+
github.com/jmespath/go-jmespath v0.4.0 // indirect
167185
github.com/kevinburke/ssh_config v1.2.0 // indirect
168186
github.com/klauspost/compress v1.18.0 // indirect
169187
github.com/klauspost/pgzip v1.2.6 // indirect
@@ -180,6 +198,7 @@ require (
180198
github.com/minio/minlz v1.0.0 // indirect
181199
github.com/mitchellh/copystructure v1.2.0 // indirect
182200
github.com/mitchellh/go-homedir v1.1.0 // indirect
201+
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
183202
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
184203
github.com/mitchellh/reflectwalk v1.0.2 // indirect
185204
github.com/moby/docker-image-spec v1.3.1 // indirect
@@ -203,20 +222,22 @@ require (
203222
github.com/pjbgf/sha1cd v0.3.2 // indirect
204223
github.com/pkg/errors v0.9.1 // indirect
205224
github.com/pkg/profile v1.7.0 // indirect
225+
github.com/pkg/xattr v0.4.9 // indirect
206226
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
207227
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
208228
github.com/rivo/uniseg v0.4.7 // indirect
209229
github.com/rogpeppe/go-internal v1.14.1 // indirect
210230
github.com/sagikazarmark/locafero v0.7.0 // indirect
211231
github.com/sahilm/fuzzy v0.1.1 // indirect
212232
github.com/shopspring/decimal v1.4.0 // indirect
213-
github.com/sirupsen/logrus v1.9.3 // indirect
233+
github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect
214234
github.com/skeema/knownhosts v1.3.1 // indirect
215235
github.com/sorairolake/lzip-go v0.3.5 // indirect
216236
github.com/sourcegraph/conc v0.3.0 // indirect
217237
github.com/spf13/cast v1.7.1 // indirect
218238
github.com/spf13/pflag v1.0.6 // indirect
219239
github.com/spf13/viper v1.20.0 // indirect
240+
github.com/stretchr/objx v0.5.2 // indirect
220241
github.com/subosito/gotenv v1.6.0 // indirect
221242
github.com/sylabs/sif/v2 v2.21.1 // indirect
222243
github.com/sylabs/squashfs v1.0.6 // indirect
@@ -235,6 +256,7 @@ require (
235256
github.com/zclconf/go-cty v1.13.0 // indirect
236257
go.opencensus.io v0.24.0 // indirect
237258
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
259+
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
238260
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect
239261
go.opentelemetry.io/otel v1.36.0 // indirect
240262
go.opentelemetry.io/otel/metric v1.36.0 // indirect
@@ -243,13 +265,17 @@ require (
243265
go.uber.org/multierr v1.9.0 // indirect
244266
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
245267
golang.org/x/crypto v0.39.0 // indirect
268+
golang.org/x/oauth2 v0.30.0 // indirect
246269
golang.org/x/sync v0.15.0 // indirect
247270
golang.org/x/sys v0.33.0 // indirect
248271
golang.org/x/term v0.32.0 // indirect
249272
golang.org/x/text v0.26.0 // indirect
273+
golang.org/x/time v0.7.0 // indirect
250274
golang.org/x/tools v0.34.0 // indirect
251275
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
276+
google.golang.org/api v0.203.0 // indirect
252277
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
278+
google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f // indirect
253279
google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 // indirect
254280
google.golang.org/grpc v1.67.3 // indirect
255281
google.golang.org/protobuf v1.36.4 // indirect

0 commit comments

Comments
 (0)