Skip to content

Commit bb80a01

Browse files
authored
Merge pull request #93 from github/parametrized-output
Don't write directly to `os.Stdout` and `os.Stderr`
2 parents 5370ec2 + 6778d12 commit bb80a01

File tree

6 files changed

+69
-52
lines changed

6 files changed

+69
-52
lines changed

git-sizer.go

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ import (
88
"os"
99
"runtime/pprof"
1010
"strconv"
11+
"time"
1112

1213
"github.com/spf13/pflag"
1314

1415
"github.com/github/git-sizer/git"
1516
"github.com/github/git-sizer/internal/refopts"
1617
"github.com/github/git-sizer/isatty"
18+
"github.com/github/git-sizer/meter"
1719
"github.com/github/git-sizer/sizes"
1820
)
1921

@@ -91,29 +93,30 @@ var ReleaseVersion string
9193
var BuildVersion string
9294

9395
func main() {
94-
err := mainImplementation(os.Args[1:])
96+
err := mainImplementation(os.Stdout, os.Stderr, os.Args[1:])
9597
if err != nil {
9698
fmt.Fprintf(os.Stderr, "error: %s\n", err)
9799
os.Exit(1)
98100
}
99101
}
100102

101-
func mainImplementation(args []string) error {
103+
func mainImplementation(stdout, stderr io.Writer, args []string) error {
102104
var nameStyle sizes.NameStyle = sizes.NameStyleFull
103105
var cpuprofile string
104106
var jsonOutput bool
105107
var jsonVersion int
106108
var threshold sizes.Threshold = 1
107109
var progress bool
108110
var version bool
111+
var showRefs bool
109112

110113
// Try to open the repository, but it's not an error yet if this
111114
// fails, because the user might only be asking for `--help`.
112115
repo, repoErr := git.NewRepository(".")
113116

114117
flags := pflag.NewFlagSet("git-sizer", pflag.ContinueOnError)
115118
flags.Usage = func() {
116-
fmt.Print(usage)
119+
fmt.Fprint(stdout, usage)
117120
}
118121

119122
flags.VarP(
@@ -151,11 +154,15 @@ func mainImplementation(args []string) error {
151154
flags.BoolVarP(&jsonOutput, "json", "j", false, "output results in JSON format")
152155
flags.IntVar(&jsonVersion, "json-version", 1, "JSON format version to output (1 or 2)")
153156

154-
atty, err := isatty.Isatty(os.Stderr.Fd())
155-
if err != nil {
156-
atty = false
157+
defaultProgress := false
158+
if f, ok := stderr.(*os.File); ok {
159+
atty, err := isatty.Isatty(f.Fd())
160+
if err == nil && atty {
161+
defaultProgress = true
162+
}
157163
}
158-
flags.BoolVar(&progress, "progress", atty, "report progress to stderr")
164+
165+
flags.BoolVar(&progress, "progress", defaultProgress, "report progress to stderr")
159166
flags.BoolVar(&version, "version", false, "report the git-sizer version number")
160167
flags.Var(&NegatedBoolValue{&progress}, "no-progress", "suppress progress output")
161168
flags.Lookup("no-progress").NoOptDefVal = "true"
@@ -177,6 +184,8 @@ func mainImplementation(args []string) error {
177184

178185
rgb.AddRefopts(flags)
179186

187+
flags.BoolVar(&showRefs, "show-refs", false, "list the references being processed")
188+
180189
flags.SortFlags = false
181190

182191
err = flags.Parse(args)
@@ -200,9 +209,9 @@ func mainImplementation(args []string) error {
200209

201210
if version {
202211
if ReleaseVersion != "" {
203-
fmt.Printf("git-sizer release %s\n", ReleaseVersion)
212+
fmt.Fprintf(stdout, "git-sizer release %s\n", ReleaseVersion)
204213
} else {
205-
fmt.Printf("git-sizer build %s\n", BuildVersion)
214+
fmt.Fprintf(stdout, "git-sizer build %s\n", BuildVersion)
206215
}
207216
return nil
208217
}
@@ -269,7 +278,17 @@ func mainImplementation(args []string) error {
269278
return err
270279
}
271280

272-
historySize, err := sizes.ScanRepositoryUsingGraph(repo, rg, nameStyle, progress)
281+
if showRefs {
282+
fmt.Fprintf(stderr, "References (included references marked with '+'):\n")
283+
rg = refopts.NewShowRefGrouper(rg, stderr)
284+
}
285+
286+
var progressMeter meter.Progress = meter.NoProgressMeter
287+
if progress {
288+
progressMeter = meter.NewProgressMeter(stderr, 100*time.Millisecond)
289+
}
290+
291+
historySize, err := sizes.ScanRepositoryUsingGraph(repo, rg, nameStyle, progressMeter)
273292
if err != nil {
274293
return fmt.Errorf("error scanning repository: %w", err)
275294
}
@@ -288,11 +307,10 @@ func mainImplementation(args []string) error {
288307
if err != nil {
289308
return fmt.Errorf("could not convert %v to json: %w", historySize, err)
290309
}
291-
fmt.Printf("%s\n", j)
310+
fmt.Fprintf(stdout, "%s\n", j)
292311
} else {
293312
if _, err := io.WriteString(
294-
os.Stdout,
295-
historySize.TableString(rg.Groups(), threshold, nameStyle),
313+
stdout, historySize.TableString(rg.Groups(), threshold, nameStyle),
296314
); err != nil {
297315
return fmt.Errorf("writing output: %w", err)
298316
}

git_sizer_test.go

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/github/git-sizer/counts"
2121
"github.com/github/git-sizer/git"
2222
"github.com/github/git-sizer/internal/testutils"
23+
"github.com/github/git-sizer/meter"
2324
"github.com/github/git-sizer/sizes"
2425
)
2526

@@ -561,7 +562,7 @@ func TestBomb(t *testing.T) {
561562

562563
h, err := sizes.ScanRepositoryUsingGraph(
563564
repo.Repository(t),
564-
refGrouper{}, sizes.NameStyleFull, false,
565+
refGrouper{}, sizes.NameStyleFull, meter.NoProgressMeter,
565566
)
566567
require.NoError(t, err)
567568

@@ -634,7 +635,7 @@ func TestTaggedTags(t *testing.T) {
634635

635636
h, err := sizes.ScanRepositoryUsingGraph(
636637
repo.Repository(t),
637-
refGrouper{}, sizes.NameStyleNone, false,
638+
refGrouper{}, sizes.NameStyleNone, meter.NoProgressMeter,
638639
)
639640
require.NoError(t, err, "scanning repository")
640641
assert.Equal(t, counts.Count32(3), h.MaxTagDepth, "tag depth")
@@ -656,7 +657,7 @@ func TestFromSubdir(t *testing.T) {
656657

657658
h, err := sizes.ScanRepositoryUsingGraph(
658659
repo.Repository(t),
659-
refGrouper{}, sizes.NameStyleNone, false,
660+
refGrouper{}, sizes.NameStyleNone, meter.NoProgressMeter,
660661
)
661662
require.NoError(t, err, "scanning repository")
662663
assert.Equal(t, counts.Count32(2), h.MaxPathDepth, "max path depth")
@@ -709,7 +710,7 @@ func TestSubmodule(t *testing.T) {
709710
// Analyze the main repo:
710711
h, err := sizes.ScanRepositoryUsingGraph(
711712
mainRepo.Repository(t),
712-
refGrouper{}, sizes.NameStyleNone, false,
713+
refGrouper{}, sizes.NameStyleNone, meter.NoProgressMeter,
713714
)
714715
require.NoError(t, err, "scanning repository")
715716
assert.Equal(t, counts.Count32(2), h.UniqueBlobCount, "unique blob count")
@@ -722,7 +723,7 @@ func TestSubmodule(t *testing.T) {
722723
}
723724
h, err = sizes.ScanRepositoryUsingGraph(
724725
submRepo2.Repository(t),
725-
refGrouper{}, sizes.NameStyleNone, false,
726+
refGrouper{}, sizes.NameStyleNone, meter.NoProgressMeter,
726727
)
727728
require.NoError(t, err, "scanning repository")
728729
assert.Equal(t, counts.Count32(2), h.UniqueBlobCount, "unique blob count")

internal/refopts/ref_group_builder.go

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package refopts
22

33
import (
44
"fmt"
5-
"os"
65
"strings"
76

87
"github.com/spf13/pflag"
@@ -21,8 +20,6 @@ type Configger interface {
2120
type RefGroupBuilder struct {
2221
topLevelGroup *refGroup
2322
groups map[sizes.RefGroupSymbol]*refGroup
24-
25-
ShowRefs bool
2623
}
2724

2825
// NewRefGroupBuilder creates and returns a `RefGroupBuilder`
@@ -253,8 +250,6 @@ func (rgb *RefGroupBuilder) AddRefopts(flags *pflag.FlagSet) {
253250
)
254251
flag.Hidden = true
255252
flag.Deprecated = "use --include=@REFGROUP"
256-
257-
flags.BoolVar(&rgb.ShowRefs, "show-refs", false, "list the references being processed")
258253
}
259254

260255
// Finish collects the information gained from processing the options
@@ -280,11 +275,6 @@ func (rgb *RefGroupBuilder) Finish() (sizes.RefGrouper, error) {
280275
refGrouper.refGroups = append(refGrouper.refGroups, *refGrouper.ignoredRefGroup)
281276
}
282277

283-
if rgb.ShowRefs {
284-
fmt.Fprintf(os.Stderr, "References (included references marked with '+'):\n")
285-
return showRefGrouper{&refGrouper, os.Stderr}, nil
286-
}
287-
288278
return &refGrouper, nil
289279
}
290280

internal/refopts/show_ref_grouper.go

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,28 @@ import (
77
"github.com/github/git-sizer/sizes"
88
)
99

10-
// showRefFilter is a `git.ReferenceFilter` that logs its choices to Stderr.
10+
// showRefFilter is a `git.ReferenceFilter` that logs its choices to
11+
// an `io.Writer`.
1112
type showRefGrouper struct {
12-
*refGrouper
13+
sizes.RefGrouper
1314
w io.Writer
1415
}
1516

16-
func (refGrouper showRefGrouper) Categorize(refname string) (bool, []sizes.RefGroupSymbol) {
17-
walk, symbols := refGrouper.refGrouper.Categorize(refname)
17+
// Return a `sizes.RefGrouper` that wraps its argument and behaves
18+
// like it except that it also logs its decisions to an `io.Writer`.
19+
func NewShowRefGrouper(rg sizes.RefGrouper, w io.Writer) sizes.RefGrouper {
20+
return showRefGrouper{
21+
RefGrouper: rg,
22+
w: w,
23+
}
24+
}
25+
26+
func (rg showRefGrouper) Categorize(refname string) (bool, []sizes.RefGroupSymbol) {
27+
walk, symbols := rg.RefGrouper.Categorize(refname)
1828
if walk {
19-
fmt.Fprintf(refGrouper.w, "+ %s\n", refname)
29+
fmt.Fprintf(rg.w, "+ %s\n", refname)
2030
} else {
21-
fmt.Fprintf(refGrouper.w, " %s\n", refname)
31+
fmt.Fprintf(rg.w, " %s\n", refname)
2232
}
2333
return walk, symbols
2434
}

meter/meter.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package meter
22

33
import (
44
"fmt"
5-
"os"
5+
"io"
66
"sync"
77
"sync/atomic"
88
"time"
@@ -30,9 +30,10 @@ type Progress interface {
3030
var Spinners = []string{"|", "(", "<", "-", "<", "(", "|", ")", ">", "-", ">", ")"}
3131

3232
// progressMeter is a `Progress` that reports the current state every
33-
// `period`.
33+
// `period` to an `io.Writer`.
3434
type progressMeter struct {
3535
lock sync.Mutex
36+
w io.Writer
3637
format string
3738
period time.Duration
3839
lastShownCount int64
@@ -48,8 +49,9 @@ type progressMeter struct {
4849
// NewProgressMeter returns a progress meter that can be used to show
4950
// progress to a TTY periodically, including an increasing int64
5051
// value.
51-
func NewProgressMeter(period time.Duration) Progress {
52+
func NewProgressMeter(w io.Writer, period time.Duration) Progress {
5253
return &progressMeter{
54+
w: w,
5355
period: period,
5456
}
5557
}
@@ -81,7 +83,7 @@ func (p *progressMeter) Start(format string) {
8183
} else {
8284
s = ""
8385
}
84-
fmt.Fprintf(os.Stderr, p.format, c, s, "\r")
86+
fmt.Fprintf(p.w, p.format, c, s, "\r")
8587
p.lock.Unlock()
8688
}
8789
}()
@@ -100,14 +102,16 @@ func (p *progressMeter) Done() {
100102
defer p.lock.Unlock()
101103
p.ticker = nil
102104
c := atomic.LoadInt64(&p.count)
103-
fmt.Fprintf(os.Stderr, p.format, c, " ", "\n")
105+
fmt.Fprintf(p.w, p.format, c, " ", "\n")
104106
}
105107

106108
// NoProgressMeter is a `Progress` that doesn't actually report
107109
// anything.
108-
type NoProgressMeter struct{}
110+
var NoProgressMeter noProgressMeter
109111

110-
func (p *NoProgressMeter) Start(string) {}
111-
func (p *NoProgressMeter) Inc() {}
112-
func (p *NoProgressMeter) Add(int64) {}
113-
func (p *NoProgressMeter) Done() {}
112+
type noProgressMeter struct{}
113+
114+
func (p noProgressMeter) Start(string) {}
115+
func (p noProgressMeter) Inc() {}
116+
func (p noProgressMeter) Add(int64) {}
117+
func (p noProgressMeter) Done() {}

sizes/graph.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"io"
88
"sync"
9-
"time"
109

1110
"github.com/github/git-sizer/counts"
1211
"github.com/github/git-sizer/git"
@@ -65,15 +64,10 @@ type refSeen struct {
6564
//
6665
// It returns the size data for the repository.
6766
func ScanRepositoryUsingGraph(
68-
repo *git.Repository, rg RefGrouper, nameStyle NameStyle, progress bool,
67+
repo *git.Repository, rg RefGrouper, nameStyle NameStyle,
68+
progressMeter meter.Progress,
6969
) (HistorySize, error) {
7070
graph := NewGraph(rg, nameStyle)
71-
var progressMeter meter.Progress
72-
if progress {
73-
progressMeter = meter.NewProgressMeter(100 * time.Millisecond)
74-
} else {
75-
progressMeter = &meter.NoProgressMeter{}
76-
}
7771

7872
refIter, err := repo.NewReferenceIter()
7973
if err != nil {

0 commit comments

Comments
 (0)