Skip to content

Commit 2c12991

Browse files
committed
as it should have been removed progress bar entirely from analysis. Now everything goes through the observer interface
1 parent e78a61f commit 2c12991

4 files changed

Lines changed: 91 additions & 61 deletions

File tree

analyze/analyze.go

Lines changed: 8 additions & 32 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-*"
@@ -122,8 +120,9 @@ func (a *Analyzer) AnalyzeOrg(ctx context.Context, org string, numberOfGoroutine
122120
pkgsupplyClient := pkgsupply.NewStaticClient()
123121
inventory := scanner.NewInventory(a.Opa, pkgsupplyClient, provider, providerVersion)
124122

125-
log.Debug().Msgf("Starting repository analysis for organization: %s on %s", org, provider)
126123
obs := a.observer()
124+
obs.OnAnalysisStarted("Discovering repositories")
125+
log.Debug().Msgf("Starting repository analysis for organization: %s on %s", org, provider)
127126

128127
var reposWg sync.WaitGroup
129128
errChan := make(chan error, 1)
@@ -274,9 +273,8 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
274273
inventory := scanner.NewInventory(a.Opa, pkgsupplyClient, provider, providerVersion)
275274

276275
obs := a.observer()
276+
obs.OnAnalysisStarted("Cloning repository")
277277
log.Debug().Msgf("Starting repository analysis for: %s/%s on %s", org, repoName, provider)
278-
bar := a.ProgressBar(3, "Cloning repository")
279-
_ = bar.RenderBlank()
280278

281279
obs.OnRepoStarted(repoString)
282280
repoUrl := repo.BuildGitURL(a.ScmClient.GetProviderBaseURL())
@@ -287,17 +285,15 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
287285
}
288286
defer a.GitClient.Cleanup(repoKey)
289287

290-
bar.Describe("Listing unique workflows")
291-
_ = bar.Add(1)
288+
obs.OnStepCompleted("Listing unique workflows")
292289

293290
workflows, err := a.GitClient.GetUniqWorkflowsBranches(ctx, repoKey)
294291
if err != nil {
295292
obs.OnRepoError(repoString, err)
296293
return nil, fmt.Errorf("failed to get unique workflow: %w", err)
297294
}
298295

299-
bar.Describe("Check which workflows match regex: " + regex.String())
300-
_ = bar.Add(1)
296+
obs.OnStepCompleted("Checking workflows match regex: " + regex.String())
301297

302298
errChan := make(chan error, 1)
303299
maxGoroutines := 5
@@ -362,8 +358,7 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
362358
return nil, err
363359
}
364360

365-
bar.Describe("Scanning package")
366-
_ = bar.Add(1)
361+
obs.OnStepCompleted("Scanning package")
367362
pkg, err := a.GeneratePackageInsights(ctx, repoKey, repo, "HEAD")
368363
if err != nil {
369364
obs.OnRepoError(repoString, err)
@@ -383,7 +378,6 @@ func (a *Analyzer) AnalyzeStaleBranches(ctx context.Context, repoString string,
383378
return nil, fmt.Errorf("failed to scan package: %w", err)
384379
}
385380

386-
_ = bar.Finish()
387381
obs.OnRepoCompleted(repoString, scannedPackage)
388382

389383
obs.OnFinalizeStarted(1)
@@ -452,9 +446,8 @@ func (a *Analyzer) AnalyzeRepo(ctx context.Context, repoString string, ref strin
452446
inventory := scanner.NewInventory(a.Opa, pkgsupplyClient, provider, providerVersion)
453447

454448
obs := a.observer()
449+
obs.OnAnalysisStarted("Cloning repository")
455450
log.Debug().Msgf("Starting repository analysis for: %s/%s on %s", org, repoName, provider)
456-
bar := a.ProgressBar(2, "Cloning repository")
457-
_ = bar.RenderBlank()
458451

459452
obs.OnRepoStarted(repoString)
460453
repoKey, err := a.cloneRepo(ctx, repo.BuildGitURL(a.ScmClient.GetProviderBaseURL()), a.ScmClient.GetToken(), ref)
@@ -464,8 +457,7 @@ func (a *Analyzer) AnalyzeRepo(ctx context.Context, repoString string, ref strin
464457
}
465458
defer a.GitClient.Cleanup(repoKey)
466459

467-
bar.Describe("Analyzing repository")
468-
_ = bar.Add(1)
460+
obs.OnStepCompleted("Cloned repository")
469461

470462
pkg, err := a.GeneratePackageInsights(ctx, repoKey, repo, ref)
471463
if err != nil {
@@ -495,7 +487,6 @@ func (a *Analyzer) AnalyzeRepo(ctx context.Context, repoString string, ref strin
495487
obs.OnRepoError(repoString, err)
496488
return nil, err
497489
}
498-
_ = bar.Finish()
499490
obs.OnRepoCompleted(repoString, scannedPackage)
500491

501492
obs.OnFinalizeStarted(1)
@@ -754,18 +745,3 @@ func (a *Analyzer) cloneRepo(ctx context.Context, gitURL string, token string, r
754745
}
755746
return key, nil
756747
}
757-
758-
func (a *Analyzer) ProgressBar(maxValue int64, description string) *progressbar.ProgressBar {
759-
if a.Config.Quiet {
760-
return progressbar.DefaultSilent(maxValue, description)
761-
} else {
762-
return progressbar.NewOptions64(
763-
maxValue,
764-
progressbar.OptionSetDescription(description),
765-
progressbar.OptionShowCount(),
766-
progressbar.OptionSetWriter(os.Stderr),
767-
progressbar.OptionClearOnFinish(),
768-
)
769-
770-
}
771-
}

analyze/analyze_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,11 @@ type mockObserver struct {
197197
events []string
198198
}
199199

200+
func (m *mockObserver) OnAnalysisStarted(description string) {
201+
m.mu.Lock()
202+
defer m.mu.Unlock()
203+
m.events = append(m.events, "analysis_started:"+description)
204+
}
200205
func (m *mockObserver) OnDiscoveryCompleted(org string, totalCount int) {
201206
m.mu.Lock()
202207
defer m.mu.Unlock()
@@ -222,6 +227,11 @@ func (m *mockObserver) OnRepoSkipped(repo string, reason string) {
222227
defer m.mu.Unlock()
223228
m.events = append(m.events, fmt.Sprintf("repo_skipped:%s:%s", repo, reason))
224229
}
230+
func (m *mockObserver) OnStepCompleted(description string) {
231+
m.mu.Lock()
232+
defer m.mu.Unlock()
233+
m.events = append(m.events, "step_completed:"+description)
234+
}
225235
func (m *mockObserver) OnFinalizeStarted(total int) {
226236
m.mu.Lock()
227237
defer m.mu.Unlock()
@@ -261,6 +271,7 @@ func TestProgressObserverInterface(t *testing.T) {
261271
obs.OnRepoCompleted("test-org/repo1", nil)
262272
obs.OnRepoSkipped("test-org/repo2", "fork")
263273
obs.OnRepoError("test-org/repo3", errors.New("clone failed"))
274+
obs.OnStepCompleted("Analyzing repository")
264275
obs.OnFinalizeStarted(1)
265276
obs.OnFinalizeCompleted()
266277

@@ -270,6 +281,7 @@ func TestProgressObserverInterface(t *testing.T) {
270281
"repo_completed:test-org/repo1",
271282
"repo_skipped:test-org/repo2:fork",
272283
"repo_error:test-org/repo3",
284+
"step_completed:Analyzing repository",
273285
"finalize_started:1",
274286
"finalize_completed",
275287
}, obs.events)

analyze/observer.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import "github.com/boostsecurityio/poutine/models"
1111
// During AnalyzeOrg, methods are called from two contexts:
1212
//
1313
// Main goroutine (sequential, never concurrent with each other):
14+
// - OnAnalysisStarted
1415
// - OnDiscoveryCompleted
1516
// - OnRepoSkipped
1617
// - OnFinalizeStarted
@@ -28,6 +29,11 @@ import "github.com/boostsecurityio/poutine/models"
2829
// During AnalyzeRepo and AnalyzeStaleBranches all methods are called
2930
// from a single goroutine.
3031
type ProgressObserver interface {
32+
// OnAnalysisStarted is called from the main goroutine at the very
33+
// beginning of any analysis, before any work begins. Useful for
34+
// rendering an initial status indicator while discovery or cloning runs.
35+
OnAnalysisStarted(description string)
36+
3137
// OnDiscoveryCompleted is called from the main goroutine when the
3238
// total repo count is known (first batch with TotalCount > 0).
3339
OnDiscoveryCompleted(org string, totalCount int)
@@ -48,6 +54,12 @@ type ProgressObserver interface {
4854
// skipped (fork, empty, etc.).
4955
OnRepoSkipped(repo string, reason string)
5056

57+
// OnStepCompleted is called when a sub-step within a single-repo
58+
// analysis completes (e.g. "Cloning repository", "Analyzing repository").
59+
// Used by AnalyzeRepo and AnalyzeStaleBranches for step-level progress.
60+
// Called from the main goroutine.
61+
OnStepCompleted(description string)
62+
5163
// OnFinalizeStarted is called from the main goroutine when the
5264
// formatting/output phase begins, after all repos are processed.
5365
OnFinalizeStarted(totalPackages int)
@@ -59,10 +71,12 @@ type ProgressObserver interface {
5971

6072
type noopObserver struct{}
6173

74+
func (noopObserver) OnAnalysisStarted(string) {}
6275
func (noopObserver) OnDiscoveryCompleted(string, int) {}
6376
func (noopObserver) OnRepoStarted(string) {}
6477
func (noopObserver) OnRepoCompleted(string, *models.PackageInsights) {}
6578
func (noopObserver) OnRepoError(string, error) {}
6679
func (noopObserver) OnRepoSkipped(string, string) {}
80+
func (noopObserver) OnStepCompleted(string) {}
6781
func (noopObserver) OnFinalizeStarted(int) {}
6882
func (noopObserver) OnFinalizeCompleted() {}

analyze/progress_bar_observer.go

Lines changed: 57 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,87 @@ import (
88
)
99

1010
// ProgressBarObserver implements ProgressObserver by rendering a CLI progress bar.
11+
// For org analysis it shows a repo-count bar (created on OnDiscoveryCompleted).
12+
// For single-repo analysis it shows a step-level bar (created on first OnStepCompleted).
1113
type ProgressBarObserver struct {
1214
bar *progressbar.ProgressBar
1315
quiet bool
1416
}
1517

1618
func NewProgressBarObserver(quiet bool) *ProgressBarObserver {
17-
var bar *progressbar.ProgressBar
18-
if quiet {
19-
bar = progressbar.DefaultSilent(0, "Analyzing repositories")
20-
} else {
21-
bar = progressbar.NewOptions64(0,
22-
progressbar.OptionSetDescription("Analyzing repositories"),
23-
progressbar.OptionShowCount(),
24-
progressbar.OptionSetWriter(os.Stderr),
25-
progressbar.OptionClearOnFinish(),
26-
)
19+
return &ProgressBarObserver{quiet: quiet}
20+
}
21+
22+
func (o *ProgressBarObserver) newBar(max int64, description string) *progressbar.ProgressBar {
23+
if o.quiet {
24+
return progressbar.DefaultSilent(max, description)
2725
}
28-
return &ProgressBarObserver{bar: bar, quiet: quiet}
26+
return progressbar.NewOptions64(max,
27+
progressbar.OptionSetDescription(description),
28+
progressbar.OptionShowCount(),
29+
progressbar.OptionSetWriter(os.Stderr),
30+
progressbar.OptionClearOnFinish(),
31+
)
32+
}
33+
34+
func (o *ProgressBarObserver) OnAnalysisStarted(description string) {
35+
// Create an indeterminate spinner bar (max=-1) to show activity
36+
// before the total count is known or the first step completes.
37+
o.bar = o.newBar(-1, description)
38+
_ = o.bar.RenderBlank()
2939
}
3040

3141
func (o *ProgressBarObserver) OnDiscoveryCompleted(_ string, totalCount int) {
32-
o.bar.ChangeMax(totalCount)
42+
// Finish the spinner before replacing with a counting bar.
43+
if o.bar != nil {
44+
_ = o.bar.Finish()
45+
}
46+
o.bar = o.newBar(int64(totalCount), "Analyzing repositories")
3347
}
3448

3549
func (o *ProgressBarObserver) OnRepoStarted(_ string) {}
3650

3751
func (o *ProgressBarObserver) OnRepoCompleted(_ string, _ *models.PackageInsights) {
38-
_ = o.bar.Add(1)
52+
if o.bar != nil {
53+
_ = o.bar.Add(1)
54+
}
3955
}
4056

4157
func (o *ProgressBarObserver) OnRepoError(_ string, _ error) {
42-
_ = o.bar.Add(1)
58+
if o.bar != nil {
59+
_ = o.bar.Add(1)
60+
}
4361
}
4462

4563
func (o *ProgressBarObserver) OnRepoSkipped(_ string, _ string) {
46-
o.bar.ChangeMax(o.bar.GetMax() - 1)
64+
if o.bar != nil {
65+
if newMax := o.bar.GetMax() - 1; newMax >= 0 {
66+
o.bar.ChangeMax(newMax)
67+
}
68+
}
4769
}
4870

49-
func (o *ProgressBarObserver) OnFinalizeStarted(_ int) {
50-
_ = o.bar.Finish()
71+
func (o *ProgressBarObserver) OnStepCompleted(description string) {
72+
if o.bar != nil && o.bar.GetMax64() == -1 {
73+
// Finish the spinner, then create a step bar.
74+
_ = o.bar.Finish()
75+
o.bar = nil
76+
}
77+
if o.bar == nil {
78+
// First step — create the bar.
79+
o.bar = o.newBar(1, description)
80+
} else {
81+
// Grow the bar to accommodate each new step, then advance.
82+
o.bar.ChangeMax(o.bar.GetMax() + 1)
83+
o.bar.Describe(description)
84+
}
85+
_ = o.bar.Add(1)
5186
}
5287

53-
func (o *ProgressBarObserver) OnFinalizeCompleted() {}
54-
55-
// ProgressBarForSteps creates a step-counting progress bar for single-repo operations.
56-
func (o *ProgressBarObserver) ProgressBarForSteps(steps int64, description string) *progressbar.ProgressBar {
57-
if o.quiet {
58-
return progressbar.DefaultSilent(steps, description)
88+
func (o *ProgressBarObserver) OnFinalizeStarted(_ int) {
89+
if o.bar != nil {
90+
_ = o.bar.Finish()
5991
}
60-
return progressbar.NewOptions64(steps,
61-
progressbar.OptionSetDescription(description),
62-
progressbar.OptionShowCount(),
63-
progressbar.OptionSetWriter(os.Stderr),
64-
progressbar.OptionClearOnFinish(),
65-
)
6692
}
93+
94+
func (o *ProgressBarObserver) OnFinalizeCompleted() {}

0 commit comments

Comments
 (0)