github.com/grahambrereton-form3/tilt@v0.10.18/internal/engine/configs/configs_controller.go (about) 1 package configs 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 "github.com/pkg/errors" 9 10 "github.com/windmilleng/tilt/internal/docker" 11 "github.com/windmilleng/tilt/internal/ospath" 12 "github.com/windmilleng/tilt/internal/store" 13 "github.com/windmilleng/tilt/internal/tiltfile" 14 "github.com/windmilleng/tilt/pkg/logger" 15 "github.com/windmilleng/tilt/pkg/model" 16 ) 17 18 type ConfigsController struct { 19 disabledForTesting bool 20 tfl tiltfile.TiltfileLoader 21 dockerClient docker.Client 22 clock func() time.Time 23 } 24 25 func NewConfigsController(tfl tiltfile.TiltfileLoader, dockerClient docker.Client) *ConfigsController { 26 return &ConfigsController{ 27 tfl: tfl, 28 dockerClient: dockerClient, 29 clock: time.Now, 30 } 31 } 32 33 func (cc *ConfigsController) SetTiltfileLoaderForTesting(tfl tiltfile.TiltfileLoader) { 34 cc.tfl = tfl 35 } 36 37 func (cc *ConfigsController) DisableForTesting(disabled bool) { 38 cc.disabledForTesting = disabled 39 } 40 41 // Modeled after BuildController.nextTargetToBuild. Check to see that: 42 // 1) There's currently no Tiltfile build running, 43 // 2) There are pending file changes, and 44 // 3) Those files have changed since the last Tiltfile build 45 // (so that we don't keep re-running a failed build) 46 func (cc *ConfigsController) shouldBuild(state store.EngineState) bool { 47 isRunning := !state.TiltfileState.CurrentBuild.StartTime.IsZero() 48 if isRunning { 49 return false 50 } 51 52 for _, changeTime := range state.PendingConfigFileChanges { 53 lastStartTime := state.TiltfileState.LastBuild().StartTime 54 if changeTime.After(lastStartTime) { 55 return true 56 } 57 } 58 return false 59 } 60 61 func logTiltfileChanges(ctx context.Context, filesChanged map[string]bool) { 62 var filenames []string 63 for k := range filesChanged { 64 filenames = append(filenames, k) 65 } 66 67 l := logger.Get(ctx) 68 69 if len(filenames) > 0 { 70 p := logger.Green(l).Sprintf("%d changed: ", len(filenames)) 71 l.Infof("\n%s%v\n", p, ospath.FormatFileChangeList(filenames)) 72 } 73 } 74 75 func (cc *ConfigsController) loadTiltfile(ctx context.Context, st store.RStore, 76 initManifests []model.ManifestName, filesChanged map[string]bool, tiltfilePath string) { 77 78 startTime := cc.clock() 79 st.Dispatch(ConfigsReloadStartedAction{FilesChanged: filesChanged, StartTime: startTime}) 80 81 actionWriter := NewTiltfileLogWriter(st) 82 ctx = logger.WithLogger(ctx, logger.NewLogger(logger.Get(ctx).Level(), actionWriter)) 83 84 state := st.RLockState() 85 globalLogLineCountAtExecStart := state.Log.LineCount() 86 firstBuild := !state.TiltfileState.StartedFirstBuild() 87 if !firstBuild { 88 logTiltfileChanges(ctx, filesChanged) 89 } 90 st.RUnlockState() 91 92 tlr := cc.tfl.Load(ctx, tiltfilePath, initManifests) 93 if tlr.Error == nil && len(tlr.Manifests) == 0 { 94 tlr.Error = fmt.Errorf("No resources found. Check out https://docs.tilt.dev/tutorial.html to get started!") 95 } 96 if tlr.Error != nil { 97 logger.Get(ctx).Infof(tlr.Error.Error()) 98 } 99 100 if tlr.Orchestrator() != model.OrchestratorUnknown { 101 cc.dockerClient.SetOrchestrator(tlr.Orchestrator()) 102 dockerErr := cc.dockerClient.CheckConnected() 103 if tlr.Error == nil && dockerErr != nil { 104 tlr.Error = errors.Wrap(dockerErr, "Failed to connect to Docker") 105 } 106 } 107 108 st.Dispatch(ConfigsReloadedAction{ 109 Manifests: tlr.Manifests, 110 ConfigFiles: tlr.ConfigFiles, 111 TiltIgnoreContents: tlr.TiltIgnoreContents, 112 FinishTime: cc.clock(), 113 Err: tlr.Error, 114 Warnings: tlr.Warnings, 115 Features: tlr.FeatureFlags, 116 TeamName: tlr.TeamName, 117 Secrets: tlr.Secrets, 118 AnalyticsTiltfileOpt: tlr.AnalyticsOpt, 119 DockerPruneSettings: tlr.DockerPruneSettings, 120 GlobalLogLineCountAtExecStart: globalLogLineCountAtExecStart, 121 }) 122 } 123 124 func (cc *ConfigsController) OnChange(ctx context.Context, st store.RStore) { 125 if cc.disabledForTesting { 126 return 127 } 128 129 state := st.RLockState() 130 defer st.RUnlockState() 131 132 initManifests := state.InitManifests 133 if !cc.shouldBuild(state) { 134 return 135 } 136 137 filesChanged := make(map[string]bool) 138 for k := range state.PendingConfigFileChanges { 139 filesChanged[k] = true 140 } 141 142 tiltfilePath, err := state.RelativeTiltfilePath() 143 if err != nil { 144 st.Dispatch(store.NewErrorAction(err)) 145 return 146 } 147 148 // Release the state lock and load the tiltfile in a separate goroutine 149 go cc.loadTiltfile(ctx, st, initManifests, filesChanged, tiltfilePath) 150 }