-
Notifications
You must be signed in to change notification settings - Fork 290
/
local_target_build_and_deployer.go
91 lines (76 loc) · 2.8 KB
/
local_target_build_and_deployer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package engine
import (
"context"
"os/exec"
"time"
"github.com/windmilleng/tilt/internal/build"
"github.com/windmilleng/tilt/internal/store"
"github.com/windmilleng/tilt/pkg/logger"
"github.com/windmilleng/tilt/pkg/model"
)
var _ BuildAndDeployer = &LocalTargetBuildAndDeployer{}
// TODO(maia): CommandRunner interface for testability
type LocalTargetBuildAndDeployer struct {
clock build.Clock
}
func NewLocalTargetBuildAndDeployer(c build.Clock) *LocalTargetBuildAndDeployer {
return &LocalTargetBuildAndDeployer{clock: c}
}
func (bd *LocalTargetBuildAndDeployer) BuildAndDeploy(ctx context.Context, st store.RStore, specs []model.TargetSpec, stateSet store.BuildStateSet) (resultSet store.BuildResultSet, err error) {
targets := bd.extract(specs)
if len(targets) != 1 {
return store.BuildResultSet{}, SilentRedirectToNextBuilderf(
"LocalTargetBuildAndDeployer requires exactly one LocalTarget (got %d)", len(targets))
}
targ := targets[0]
err = bd.run(ctx, targ.Cmd, targ.Workdir)
if err != nil {
// (Never fall back from the LocalTargetBaD, none of our other BaDs can handle this target)
return store.BuildResultSet{}, DontFallBackErrorf("Command %q failed: %v", targ.Cmd.String(), err)
}
if state := stateSet[targ.ID()]; state.IsEmpty() {
// HACK(maia): on initial build, give the file change a little extra time to
// propagate through, to increase the chance that we pick it up before we start
// the next build (otherwise, we may build that next resource twice).
time.Sleep(250 * time.Millisecond)
}
return bd.successfulBuildResult(targ), nil
}
// Extract the targets we can apply -- i.e. LocalTargets
func (bd *LocalTargetBuildAndDeployer) extract(specs []model.TargetSpec) []model.LocalTarget {
var targs []model.LocalTarget
for _, s := range specs {
switch s := s.(type) {
case model.LocalTarget:
targs = append(targs, s)
}
}
return targs
}
func (bd *LocalTargetBuildAndDeployer) run(ctx context.Context, c model.Cmd, wd string) error {
if len(c.Argv) == 0 {
panic("LocalTargetBuildAndDeployer tried to run empty command " +
"(should have been caught by Validate() )")
}
l := logger.Get(ctx)
writer := l.Writer(logger.InfoLvl)
cmd := exec.CommandContext(ctx, c.Argv[0], c.Argv[1:]...)
cmd.Stdout = writer
cmd.Stderr = writer
cmd.Dir = wd
ps := build.NewPipelineState(ctx, 1, bd.clock)
ps.StartPipelineStep(ctx, "Running command: %v (in %q)", c.Argv, wd)
defer ps.EndPipelineStep(ctx)
err := cmd.Run()
defer func() { ps.End(ctx, err) }()
if err != nil {
// TODO(maia): any point in checking if it's an ExitError,
// pulling out the error code, etc.?
return err
}
return nil
}
func (bd *LocalTargetBuildAndDeployer) successfulBuildResult(t model.LocalTarget) store.BuildResultSet {
br := store.NewLocalBuildResult(t.ID())
return store.BuildResultSet{t.ID(): br}
}