Skip to content

Commit ecab062

Browse files
author
Christian Weichel
committed
Introduced an extensible build reporter
1 parent 3cabf78 commit ecab062

File tree

3 files changed

+202
-62
lines changed

3 files changed

+202
-62
lines changed

cmd/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ var buildCmd = &cobra.Command{
6161
log.Fatal(err)
6262
}
6363

64-
err = leeway.Build(pkg, localCache, remoteCache)
64+
err = leeway.Build(pkg, localCache, remoteCache, leeway.NewConsoleReporter())
6565
if err != nil {
6666
log.Fatal(err)
6767
}

pkg/leeway/build.go

Lines changed: 59 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,9 @@ import (
77
"os"
88
"os/exec"
99
"path/filepath"
10-
"sort"
1110
"strings"
1211
"sync"
13-
"text/tabwriter"
1412

15-
"github.com/gookit/color"
16-
"github.com/segmentio/textio"
1713
log "github.com/sirupsen/logrus"
1814
"golang.org/x/xerrors"
1915
)
@@ -48,6 +44,7 @@ type packageDuringBuild struct {
4844

4945
type buildContext struct {
5046
LocalCache Cache
47+
Reporter Reporter
5148

5249
buildDir string
5350

@@ -75,14 +72,15 @@ const (
7572
dockerImageNamesFiles = "imgnames.txt"
7673
)
7774

78-
func newBuildContext(localCache Cache) (ctx *buildContext, err error) {
75+
func newBuildContext(localCache Cache, reporter Reporter) (ctx *buildContext, err error) {
7976
buildDir := os.Getenv(EnvvarBuildDir)
8077
if buildDir == "" {
8178
buildDir = filepath.Join(os.TempDir(), "build")
8279
}
8380

8481
ctx = &buildContext{
8582
LocalCache: localCache,
83+
Reporter: reporter,
8684
buildDir: buildDir,
8785
newlyBuiltPackages: make(map[string]*Package),
8886
pkgLockCond: sync.NewCond(&sync.Mutex{}),
@@ -131,7 +129,6 @@ func (c *buildContext) ReleaseBuildLock(p *Package) {
131129
c.pkgLockCond.L.Lock()
132130
delete(c.pkgLocks, key)
133131
c.pkgLockCond.Broadcast()
134-
log.WithField("package", key).Info("package build done")
135132
c.pkgLockCond.L.Unlock()
136133
}
137134

@@ -162,14 +159,27 @@ func (c *buildContext) GetNewlyBuiltPackages() []*Package {
162159

163160
// Build builds the packages in the order they're given. It's the callers responsibility to ensure the dependencies are built
164161
// in order.
165-
func Build(pkg *Package, localCache Cache, remoteCache RemoteCache) error {
162+
func Build(pkg *Package, localCache Cache, remoteCache RemoteCache, reporter Reporter) (err error) {
163+
ctx, err := newBuildContext(localCache, reporter)
164+
166165
requirements := pkg.GetTransitiveDependencies()
167166
allpkg := append(requirements, pkg)
168167

169-
ctx, err := newBuildContext(localCache)
168+
err = remoteCache.Download(ctx.LocalCache, requirements)
169+
if err != nil {
170+
return err
171+
}
172+
173+
pkgstatus := make(map[*Package]PackageBuildStatus)
170174
unresolvedArgs := make(map[string][]string)
171175
for _, dep := range allpkg {
172176
_, exists := ctx.LocalCache.Location(dep)
177+
if exists {
178+
pkgstatus[dep] = PackageBuilt
179+
} else {
180+
pkgstatus[dep] = PackageNotBuiltYet
181+
}
182+
173183
if exists {
174184
continue
175185
}
@@ -187,6 +197,11 @@ func Build(pkg *Package, localCache Cache, remoteCache RemoteCache) error {
187197
unresolvedArgs[arg] = pkgs
188198
}
189199
}
200+
reporter.BuildStarted(pkg, pkgstatus)
201+
defer func(err *error) {
202+
reporter.BuildFinished(pkg, *err)
203+
}(&err)
204+
190205
if len(unresolvedArgs) != 0 {
191206
var msg string
192207
for arg, pkgs := range unresolvedArgs {
@@ -196,27 +211,6 @@ func Build(pkg *Package, localCache Cache, remoteCache RemoteCache) error {
196211
return xerrors.Errorf(msg)
197212
}
198213

199-
err = remoteCache.Download(ctx.LocalCache, requirements)
200-
if err != nil {
201-
return err
202-
}
203-
204-
// now that the local cache is warm, we can print the list of work we have to do
205-
lines := make([]string, len(allpkg))
206-
for i, dep := range allpkg {
207-
_, exists := ctx.LocalCache.Location(dep)
208-
format := "%s\t%s\n"
209-
if exists {
210-
lines[i] = fmt.Sprintf(format, color.Green.Sprint("📦\tcached"), dep.FullName())
211-
} else {
212-
lines[i] = fmt.Sprintf(format, color.Yellow.Sprint("🔧\tbuild"), dep.FullName())
213-
}
214-
}
215-
sort.Slice(lines, func(i, j int) bool { return lines[i] < lines[j] })
216-
tw := tabwriter.NewWriter(os.Stdout, 0, 2, 2, ' ', 0)
217-
fmt.Fprintln(tw, strings.Join(lines, ""))
218-
tw.Flush()
219-
220214
err = pkg.build(ctx)
221215
if err != nil {
222216
return err
@@ -267,7 +261,13 @@ func (p *Package) build(buildctx *buildContext) (err error) {
267261
if alreadyBuilt {
268262
// some package types still need to do work even if we find their prior build artifact in the cache.
269263
if p.Type == DockerPackage {
270-
err = p.rebuildDocker(filepath.Dir(artifact), artifact)
264+
doBuild := buildctx.ObtainBuildLock(p)
265+
if !doBuild {
266+
return nil
267+
}
268+
defer buildctx.ReleaseBuildLock(p)
269+
270+
err = p.rebuildDocker(buildctx, filepath.Dir(artifact), artifact)
271271
if err != nil {
272272
log.WithError(err).Warn("cannot re-use prior build artifact - building afresh.")
273273
} else {
@@ -295,15 +295,10 @@ func (p *Package) build(buildctx *buildContext) (err error) {
295295
return err
296296
}
297297

298-
log.WithField("package", p.FullName()).Info("building")
299-
defer func() {
300-
if err != nil {
301-
log.WithField("package", p.FullName()).WithError(err).Error("build failed")
302-
return
303-
}
304-
305-
log.WithField("package", p.FullName()).WithField("version", version).Info("build succeded")
306-
}()
298+
buildctx.Reporter.PackageBuildStarted(p)
299+
defer func(err *error) {
300+
buildctx.Reporter.PackageBuildFinished(p, *err)
301+
}(&err)
307302

308303
pkgdir := p.FilesystemSafeName() + "." + version
309304
builddir := filepath.Join(buildctx.BuildDir(), pkgdir)
@@ -324,7 +319,7 @@ func (p *Package) build(buildctx *buildContext) (err error) {
324319
cpargs = append(cpargs, strings.TrimPrefix(src, p.C.Origin+"/"))
325320
}
326321
cpargs = append(cpargs, builddir)
327-
err = run(getRunPrefix(p), nil, p.C.Origin, "cp", cpargs...)
322+
err = run(buildctx.Reporter, p, nil, p.C.Origin, "cp", cpargs...)
328323
if err != nil {
329324
return err
330325
}
@@ -457,7 +452,7 @@ func (p *Package) buildTypescript(buildctx *buildContext, wd, result string) (er
457452
// Make sure that all our yarn install calls lock the yarn cache.
458453
yarnMutex := os.Getenv(EnvvarYarnMutex)
459454
if yarnMutex == "" {
460-
log.Debug("%s is not set, defaulting to \"network\"", EnvvarYarnMutex)
455+
log.Debugf("%s is not set, defaulting to \"network\"", EnvvarYarnMutex)
461456
yarnMutex = "network"
462457
}
463458
yarnCache := filepath.Join(buildctx.BuildDir(), "yarn-cache")
@@ -490,7 +485,7 @@ func (p *Package) buildTypescript(buildctx *buildContext, wd, result string) (er
490485
commands = append(commands, []string{"tar", "cfz", result, "."})
491486
}
492487

493-
return executeCommandsForPackage(p, wd, commands)
488+
return executeCommandsForPackage(buildctx, p, wd, commands)
494489
}
495490

496491
func (p *Package) buildGo(buildctx *buildContext, wd, result string) (err error) {
@@ -551,7 +546,7 @@ func (p *Package) buildGo(buildctx *buildContext, wd, result string) (err error)
551546
{"tar", "cfz", result, "."},
552547
}...)
553548

554-
return executeCommandsForPackage(p, wd, commands)
549+
return executeCommandsForPackage(buildctx, p, wd, commands)
555550
}
556551

557552
func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (err error) {
@@ -619,7 +614,7 @@ func (p *Package) buildDocker(buildctx *buildContext, wd, result string) (err er
619614
commands = append(commands, []string{"tar", "cfz", result, dockerImageNamesFiles})
620615
}
621616

622-
return executeCommandsForPackage(p, wd, commands)
617+
return executeCommandsForPackage(buildctx, p, wd, commands)
623618
}
624619

625620
func (p *Package) buildGeneric(buildctx *buildContext, wd, result string) (err error) {
@@ -631,7 +626,7 @@ func (p *Package) buildGeneric(buildctx *buildContext, wd, result string) (err e
631626
// shortcut: no command == empty package
632627
if len(cfg.Command) == 0 {
633628
log.WithField("package", p.FullName()).Debug("package has no commands - creating empty tar")
634-
return run(getRunPrefix(p), nil, wd, "tar", "cfz", result, "--files-from", "/dev/null")
629+
return run(buildctx.Reporter, p, nil, wd, "tar", "cfz", result, "--files-from", "/dev/null")
635630
}
636631

637632
var commands [][]string
@@ -651,13 +646,13 @@ func (p *Package) buildGeneric(buildctx *buildContext, wd, result string) (err e
651646
commands = append(commands, cfg.Command)
652647
commands = append(commands, []string{"tar", "cfz", result, "."})
653648

654-
return executeCommandsForPackage(p, wd, commands)
649+
return executeCommandsForPackage(buildctx, p, wd, commands)
655650
}
656651

657652
// rebuildDocker is called when we already have the build artifact for this package (and version)
658653
// in the build cache. This function makes sure that if the build arguments changed the name of the
659654
// Docker image this build time, we just re-tag the image.
660-
func (p *Package) rebuildDocker(wd, prev string) (err error) {
655+
func (p *Package) rebuildDocker(buildctx *buildContext, wd, prev string) (err error) {
661656
cfg, ok := p.Config.(DockerPkgConfig)
662657
if !ok {
663658
return xerrors.Errorf("package should have Docker config")
@@ -709,38 +704,30 @@ func (p *Package) rebuildDocker(wd, prev string) (err error) {
709704
return
710705
}
711706

712-
err = executeCommandsForPackage(p, wd, commands)
707+
err = executeCommandsForPackage(buildctx, p, wd, commands)
713708
if err != nil {
714709
return err
715710
}
716711
return nil
717712
}
718713

719-
func getRunPrefix(p *Package) string {
720-
return fmt.Sprintf("[%s] ", p.FullName())
721-
}
722-
723-
func executeCommandsForPackage(p *Package, wd string, commands [][]string) error {
724-
prefix := getRunPrefix(p)
714+
func executeCommandsForPackage(buildctx *buildContext, p *Package, wd string, commands [][]string) error {
725715
env := append(os.Environ(), p.Environment...)
726716
for _, cmd := range commands {
727-
err := run(prefix, env, wd, cmd[0], cmd[1:]...)
717+
err := run(buildctx.Reporter, p, env, wd, cmd[0], cmd[1:]...)
728718
if err != nil {
729719
return err
730720
}
731721
}
732722
return nil
733723
}
734724

735-
func run(prefix string, env []string, cwd, name string, args ...string) error {
725+
func run(rep Reporter, p *Package, env []string, cwd, name string, args ...string) error {
736726
log.WithField("command", strings.Join(append([]string{name}, args...), " ")).Debug("running")
737727

738-
stdout := textio.NewPrefixWriter(os.Stdout, prefix)
739-
stderr := textio.NewPrefixWriter(os.Stderr, prefix)
740-
741728
cmd := exec.Command(name, args...)
742-
cmd.Stdout = stdout
743-
cmd.Stderr = stderr
729+
cmd.Stdout = &reporterStream{R: rep, P: p, IsErr: false}
730+
cmd.Stderr = &reporterStream{R: rep, P: p, IsErr: true}
744731
cmd.Dir = cwd
745732
cmd.Env = env
746733
err := cmd.Run()
@@ -751,3 +738,14 @@ func run(prefix string, env []string, cwd, name string, args ...string) error {
751738

752739
return nil
753740
}
741+
742+
type reporterStream struct {
743+
R Reporter
744+
P *Package
745+
IsErr bool
746+
}
747+
748+
func (s *reporterStream) Write(buf []byte) (n int, err error) {
749+
s.R.PackageBuildLog(s.P, s.IsErr, buf)
750+
return len(buf), nil
751+
}

0 commit comments

Comments
 (0)