Skip to content

Commit 5ea29c5

Browse files
authored
Analysis Progress Monitoring Improvement (#419)
1 parent b3cd202 commit 5ea29c5

5 files changed

Lines changed: 364 additions & 41 deletions

File tree

analyze/analyze.go

Lines changed: 54 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"errors"
77
"fmt"
88
"io"
9-
"os"
109
"path/filepath"
1110
"regexp"
1211
"strings"
@@ -22,7 +21,6 @@ import (
2221
scm_domain "github.com/boostsecurityio/poutine/providers/scm/domain"
2322
"github.com/boostsecurityio/poutine/scanner"
2423
"github.com/rs/zerolog/log"
25-
"github.com/schollz/progressbar/v3"
2624
)
2725

2826
const TEMP_DIR_PREFIX = "poutine-*"
@@ -96,6 +94,14 @@ type Analyzer struct {
9694
Formatter Formatter
9795
Config *models.Config
9896
Opa *opa.Opa
97+
Observer ProgressObserver
98+
}
99+
100+
func (a *Analyzer) observer() ProgressObserver {
101+
if a.Observer != nil {
102+
return a.Observer
103+
}
104+
return noopObserver{}
99105
}
100106

101107
func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutines *int) ([]*models.PackageInsights, error) {
@@ -114,8 +120,9 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
114120
pkgsupplyClient := pkgsupply.NewStaticClient()
115121
inventory := scanner.NewInventory(a.Opa, pkgsupplyClient, provider, providerVersion)
116122

123+
obs := a.observer()
124+
obs.OnAnalysisStarted("Discovering repositories")
117125
log.Debug().Msgf("Starting repository analysis for organization: %s on %s", org, provider)
118-
bar := a.ProgressBar(0, "Analyzing repositories")
119126

120127
var reposWg sync.WaitGroup
121128
errChan := make(chan error, 1)
@@ -137,23 +144,24 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
137144
}
138145
}()
139146

147+
discoveryCompleted := false
140148
for repoBatch := range orgReposBatches {
141149
if repoBatch.Err != nil {
142150
log.Error().Err(repoBatch.Err).Msg("failed to fetch batch of repos, skipping batch")
143151
continue
144152
}
145-
if repoBatch.TotalCount != 0 {
146-
bar.ChangeMax(repoBatch.TotalCount)
153+
if !discoveryCompleted && repoBatch.TotalCount != 0 {
154+
discoveryCompleted = true
155+
obs.OnDiscoveryCompleted(org, repoBatch.TotalCount)
147156
}
148157

149158
for _, repo := range repoBatch.Repositories {
150159
if a.Config.IgnoreForks && repo.GetIsFork() {
151-
bar.ChangeMax(repoBatch.TotalCount - 1)
160+
obs.OnRepoSkipped(repo.GetRepoIdentifier(), "fork")
152161
continue
153162
}
154163
if repo.GetSize() == 0 {
155-
bar.ChangeMax(repoBatch.TotalCount - 1)
156-
log.Info().Str("repo", repo.GetRepoIdentifier()).Msg("Skipping empty repository")
164+
obs.OnRepoSkipped(repo.GetRepoIdentifier(), "empty")
157165
continue
158166
}
159167
if err := goRoutineLimitSem.Acquire(ctx, 1); err != nil {
@@ -166,22 +174,26 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
166174
defer goRoutineLimitSem.Release(1)
167175
defer reposWg.Done()
168176
repoNameWithOwner := repo.GetRepoIdentifier()
177+
obs.OnRepoStarted(repoNameWithOwner)
169178
repoKey, err := a.cloneRepo(ctx, repo.BuildGitURL(a.ScmClient.GetProviderBaseURL()), a.ScmClient.GetToken(), "HEAD")
170179
if err != nil {
171180
log.Error().Err(err).Str("repo", repoNameWithOwner).Msg("failed to clone repo")
181+
obs.OnRepoError(repoNameWithOwner, err)
172182
return
173183
}
174184
defer a.GitClient.Cleanup(repoKey)
175185

176186
pkg, err := a.GeneratePackageInsights(ctx, repoKey, repo, "HEAD")
177187
if err != nil {
178188
log.Error().Err(err).Str("repo", repoNameWithOwner).Msg("failed to generate package insights")
189+
obs.OnRepoError(repoNameWithOwner, err)
179190
return
180191
}
181192

182193
files, err := a.GitClient.ListFiles(repoKey, []string{".yml", ".yaml"})
183194
if err != nil {
184195
log.Error().Err(err).Str("repo", repoNameWithOwner).Msg("failed to list files")
196+
obs.OnRepoError(repoNameWithOwner, err)
185197
return
186198
}
187199

@@ -199,6 +211,7 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
199211
scannedPkg, err := inventory.ScanPackageScanner(ctx, *pkg, memScanner)
200212
if err != nil {
201213
log.Error().Err(err).Str("repo", repoNameWithOwner).Msg("failed to scan package")
214+
obs.OnRepoError(repoNameWithOwner, err)
202215
return
203216
}
204217

@@ -208,7 +221,7 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
208221
log.Error().Msg("Context canceled while sending package to channel")
209222
return
210223
}
211-
_ = bar.Add(1)
224+
obs.OnRepoCompleted(repoNameWithOwner, scannedPkg)
212225
}(repo)
213226
}
214227
}
@@ -227,12 +240,12 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
227240
}
228241
}
229242

230-
_ = bar.Finish()
231-
243+
obs.OnFinalizeStarted(len(scannedPackages))
232244
err = a.finalizeAnalysis(ctx, scannedPackages)
233245
if err != nil {
234246
return scannedPackages, err
235247
}
248+
obs.OnFinalizeCompleted()
236249

237250
return scannedPackages, nil
238251
}
@@ -259,27 +272,28 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
259272

260273
inventory := scanner.NewInventory(a.Opa, pkgsupplyClient, provider, providerVersion)
261274

275+
obs := a.observer()
276+
obs.OnAnalysisStarted("Cloning repository")
262277
log.Debug().Msgf("Starting repository analysis for: %s/%s on %s", org, repoName, provider)
263-
bar := a.ProgressBar(3, "Cloning repository")
264-
_ = bar.RenderBlank()
265278

279+
obs.OnRepoStarted(repoString)
266280
repoUrl := repo.BuildGitURL(a.ScmClient.GetProviderBaseURL())
267281
repoKey, err := a.fetchCone(ctx, repoUrl, a.ScmClient.GetToken(), "refs/heads/*:refs/remotes/origin/*", ".github/workflows")
268282
if err != nil {
283+
obs.OnRepoError(repoString, err)
269284
return nil, fmt.Errorf("failed to fetch cone: %w", err)
270285
}
271286
defer a.GitClient.Cleanup(repoKey)
272287

273-
bar.Describe("Listing unique workflows")
274-
_ = bar.Add(1)
288+
obs.OnStepCompleted("Listing unique workflows")
275289

276290
workflows, err := a.GitClient.GetUniqWorkflowsBranches(ctx, repoKey)
277291
if err != nil {
292+
obs.OnRepoError(repoString, err)
278293
return nil, fmt.Errorf("failed to get unique workflow: %w", err)
279294
}
280295

281-
bar.Describe("Check which workflows match regex: " + regex.String())
282-
_ = bar.Add(1)
296+
obs.OnStepCompleted("Checking workflows match regex: " + regex.String())
283297

284298
errChan := make(chan error, 1)
285299
maxGoroutines := 5
@@ -340,13 +354,14 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
340354
close(filesChan)
341355
wgConsumer.Wait()
342356
for err := range errChan {
357+
obs.OnRepoError(repoString, err)
343358
return nil, err
344359
}
345360

346-
bar.Describe("Scanning package")
347-
_ = bar.Add(1)
361+
obs.OnStepCompleted("Scanning package")
348362
pkg, err := a.GeneratePackageInsights(ctx, repoKey, repo, "HEAD")
349363
if err != nil {
364+
obs.OnRepoError(repoString, err)
350365
return nil, fmt.Errorf("failed to generate package insight: %w", err)
351366
}
352367

@@ -359,10 +374,13 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
359374

360375
scannedPackage, err := inventory.ScanPackageScanner(ctx, *pkg, &inventoryScanner)
361376
if err != nil {
377+
obs.OnRepoError(repoString, err)
362378
return nil, fmt.Errorf("failed to scan package: %w", err)
363379
}
364380

365-
_ = bar.Finish()
381+
obs.OnRepoCompleted(repoString, scannedPackage)
382+
383+
obs.OnFinalizeStarted(1)
366384
if *expand {
367385
expanded := []results.Finding{}
368386
for _, finding := range scannedPackage.FindingsResults.Findings {
@@ -400,8 +418,8 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
400418
if err := a.Formatter.FormatWithPath(ctx, []*models.PackageInsights{scannedPackage}, results); err != nil {
401419
return nil, fmt.Errorf("failed to finalize analysis of package: %w", err)
402420
}
403-
404421
}
422+
obs.OnFinalizeCompleted()
405423

406424
return scannedPackage, nil
407425
}
@@ -427,26 +445,31 @@ func (a *Analyzer) AnalyzeRepo(ctx context.Context, repoString string, ref strin
427445
pkgsupplyClient := pkgsupply.NewStaticClient()
428446
inventory := scanner.NewInventory(a.Opa, pkgsupplyClient, provider, providerVersion)
429447

448+
obs := a.observer()
449+
obs.OnAnalysisStarted("Cloning repository")
430450
log.Debug().Msgf("Starting repository analysis for: %s/%s on %s", org, repoName, provider)
431-
bar := a.ProgressBar(2, "Cloning repository")
432-
_ = bar.RenderBlank()
433451

452+
obs.OnRepoStarted(repoString)
434453
repoKey, err := a.cloneRepo(ctx, repo.BuildGitURL(a.ScmClient.GetProviderBaseURL()), a.ScmClient.GetToken(), ref)
435454
if err != nil {
455+
obs.OnRepoError(repoString, err)
436456
return nil, err
437457
}
438458
defer a.GitClient.Cleanup(repoKey)
439459

440-
bar.Describe("Analyzing repository")
441-
_ = bar.Add(1)
460+
obs.OnStepCompleted("Cloned repository")
442461

443462
pkg, err := a.GeneratePackageInsights(ctx, repoKey, repo, ref)
444463
if err != nil {
464+
obs.OnRepoError(repoString, err)
445465
return nil, err
446466
}
447467

468+
obs.OnStepCompleted("Generated package insights")
469+
448470
files, err := a.GitClient.ListFiles(repoKey, []string{".yml", ".yaml"})
449471
if err != nil {
472+
obs.OnRepoError(repoString, err)
450473
return nil, fmt.Errorf("failed to list files: %w", err)
451474
}
452475

@@ -463,14 +486,19 @@ func (a *Analyzer) AnalyzeRepo(ctx context.Context, repoString string, ref strin
463486

464487
scannedPackage, err := inventory.ScanPackageScanner(ctx, *pkg, memScanner)
465488
if err != nil {
489+
obs.OnRepoError(repoString, err)
466490
return nil, err
467491
}
468-
_ = bar.Finish()
469492

493+
obs.OnStepCompleted("Scanned repository")
494+
obs.OnRepoCompleted(repoString, scannedPackage)
495+
496+
obs.OnFinalizeStarted(1)
470497
err = a.finalizeAnalysis(ctx, []*models.PackageInsights{scannedPackage})
471498
if err != nil {
472499
return nil, err
473500
}
501+
obs.OnFinalizeCompleted()
474502

475503
return scannedPackage, nil
476504
}
@@ -721,18 +749,3 @@ func (a *Analyzer) cloneRepo(ctx context.Context, gitURL string, token string, r
721749
}
722750
return key, nil
723751
}
724-
725-
func (a *Analyzer) ProgressBar(maxValue int64, description string) *progressbar.ProgressBar {
726-
if a.Config.Quiet {
727-
return progressbar.DefaultSilent(maxValue, description)
728-
} else {
729-
return progressbar.NewOptions64(
730-
maxValue,
731-
progressbar.OptionSetDescription(description),
732-
progressbar.OptionShowCount(),
733-
progressbar.OptionSetWriter(os.Stderr),
734-
progressbar.OptionClearOnFinish(),
735-
)
736-
737-
}
738-
}

0 commit comments

Comments
 (0)