github.com/tilt-dev/tilt@v0.36.0/internal/controllers/core/tiltfile/reducers.go (about)

     1  package tiltfile
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/tilt-dev/tilt/internal/store"
     7  	"github.com/tilt-dev/tilt/pkg/logger"
     8  	"github.com/tilt-dev/tilt/pkg/model"
     9  )
    10  
    11  const TiltfileBuildSource = "tiltfile"
    12  
    13  func HandleConfigsReloadStarted(
    14  	ctx context.Context,
    15  	state *store.EngineState,
    16  	event ConfigsReloadStartedAction,
    17  ) {
    18  	ms, ok := state.TiltfileStates[event.Name]
    19  	if !ok {
    20  		return
    21  	}
    22  
    23  	status := model.BuildRecord{
    24  		StartTime: event.StartTime,
    25  		Reason:    event.Reason,
    26  		Edits:     event.FilesChanged,
    27  		SpanID:    event.SpanID,
    28  	}
    29  	ms.CurrentBuilds[TiltfileBuildSource] = status
    30  	state.RemoveFromTriggerQueue(event.Name)
    31  }
    32  
    33  // In the original Tilt architecture, the Tiltfile contained
    34  // the whole engine state. Reloading the tiltfile re-created that
    35  // state from scratch.
    36  //
    37  // We've moved towards a more modular architecture, but many of the tilt data
    38  // models aren't modular. For example, if two Tiltfiles set UpdateSettings,
    39  // it's not clear which one "wins" or how their preferences combine.
    40  //
    41  // In the long-term, Tiltfile settings should only take affect in objects created
    42  // by that Tiltfile. (e.g., WatchSettings only affects FileWatches created by
    43  // that Tiltfile.)
    44  //
    45  // In the medium-term, we resolve this in the EngineState in three different ways:
    46  //  1. If a data structure supports merging (like the Manifest map), do a merge.
    47  //  2. If merging fails (like if two Tiltfiles define the same Manifest), log an Error
    48  //     and try to do something reasonable.
    49  //  3. If a data structure does not support merging (like UpdateSettings), only
    50  //     accept that data structure from the "main" tiltfile.
    51  func HandleConfigsReloaded(
    52  	ctx context.Context,
    53  	state *store.EngineState,
    54  	event ConfigsReloadedAction,
    55  ) {
    56  	isMainTiltfile := event.Name == model.MainTiltfileManifestName
    57  
    58  	manifests := event.Manifests
    59  	loadedManifestNames := map[model.ManifestName]bool{}
    60  	for i, m := range manifests {
    61  		loadedManifestNames[m.Name] = true
    62  
    63  		// Properly annotate the manifest with the source tiltfile.
    64  		m.SourceTiltfile = event.Name
    65  		manifests[i] = m
    66  	}
    67  
    68  	ms, ok := state.TiltfileStates[event.Name]
    69  	if !ok {
    70  		return
    71  	}
    72  	b := ms.CurrentBuilds[TiltfileBuildSource]
    73  
    74  	// Remove pending file changes that were consumed by this build.
    75  	for _, status := range ms.BuildStatuses {
    76  		status.ConsumeChangesBefore(b.StartTime)
    77  	}
    78  
    79  	// Track the new secrets and go back to scrub them.
    80  	newSecrets := model.SecretSet{}
    81  	for k, v := range event.Secrets {
    82  		_, exists := state.Secrets[k]
    83  		if !exists {
    84  			newSecrets[k] = v
    85  		}
    86  	}
    87  
    88  	// Add all secrets, even if we failed.
    89  	state.Secrets.AddAll(event.Secrets)
    90  
    91  	// Retroactively scrub secrets
    92  	state.LogStore.ScrubSecretsStartingAt(newSecrets, event.CheckpointAtExecStart)
    93  
    94  	// Add team id if it exists, even if execution failed.
    95  	if isMainTiltfile && (event.TeamID != "" || event.Err == nil) {
    96  		state.TeamID = event.TeamID
    97  	}
    98  
    99  	// if the ConfigsReloadedAction came from a unit test, there might not be a current build
   100  	if !b.Empty() {
   101  		b.FinishTime = event.FinishTime
   102  		b.Error = event.Err
   103  
   104  		if b.SpanID != "" {
   105  			b.WarningCount = len(state.LogStore.Warnings(b.SpanID))
   106  		}
   107  
   108  		ms.AddCompletedBuild(b)
   109  	}
   110  	delete(ms.CurrentBuilds, TiltfileBuildSource)
   111  	if event.Err != nil {
   112  		// When the Tiltfile had an error, we want to differentiate between two cases:
   113  		//
   114  		// 1) You're running `tilt up` for the first time, and a local() command
   115  		// exited with status code 1.  Partial results (like enabling features)
   116  		// would be helpful.
   117  		//
   118  		// 2) You're running 'tilt up' in the happy state. You edit the Tiltfile,
   119  		// and introduce a syntax error.  You don't want partial results to wipe out
   120  		// your "good" state.
   121  
   122  		if isMainTiltfile {
   123  			// Enable any new features in the partial state.
   124  			if len(state.Features) == 0 {
   125  				state.Features = event.Features
   126  			} else {
   127  				for feature, val := range event.Features {
   128  					if val {
   129  						state.Features[feature] = val
   130  					}
   131  				}
   132  			}
   133  		}
   134  		return
   135  	}
   136  
   137  	// Make sure all the new manifests are in the EngineState.
   138  	for _, m := range manifests {
   139  		mt, ok := state.ManifestTargets[m.ManifestName()]
   140  		if ok && mt.Manifest.SourceTiltfile != event.Name {
   141  			logger.Get(ctx).Errorf("Resource defined in two tiltfiles: %s, %s", event.Name, mt.Manifest.SourceTiltfile)
   142  			continue
   143  		}
   144  
   145  		// Create a new manifest if it changed types.
   146  		createNew := !ok ||
   147  			mt.Manifest.IsK8s() != m.IsK8s() ||
   148  			mt.Manifest.IsLocal() != m.IsLocal() ||
   149  			mt.Manifest.IsDC() != m.IsDC()
   150  		if createNew {
   151  			mt = store.NewManifestTarget(m)
   152  		}
   153  
   154  		configFilesThatChanged := ms.LastBuild().Edits
   155  		old := mt.Manifest
   156  		mt.Manifest = m
   157  
   158  		if model.ChangesInvalidateBuild(old, m) {
   159  			// Manifest has changed such that the current build is invalid;
   160  			// ensure we do an image build so that we apply the changes
   161  			ms := mt.State
   162  			ms.ResetBuildStatus(m)
   163  			ms.PendingManifestChange = event.FinishTime
   164  			ms.ConfigFilesThatCausedChange = configFilesThatChanged
   165  		}
   166  		state.UpsertManifestTarget(mt)
   167  	}
   168  
   169  	// Go through all the existing manifest targets. If they were from this
   170  	// Tiltfile, but were removed from the latest Tiltfile execution, delete them.
   171  	for _, mt := range state.Targets() {
   172  		m := mt.Manifest
   173  
   174  		if m.SourceTiltfile == event.Name {
   175  			if !loadedManifestNames[m.Name] {
   176  				state.RemoveManifestTarget(m.Name)
   177  			}
   178  			continue
   179  		}
   180  	}
   181  
   182  	// Global state that's only configurable from the main manifest.
   183  	if isMainTiltfile {
   184  		state.Features = event.Features
   185  		state.TelemetrySettings = event.TelemetrySettings
   186  		state.VersionSettings = event.VersionSettings
   187  		state.AnalyticsTiltfileOpt = event.AnalyticsTiltfileOpt
   188  		state.UpdateSettings = event.UpdateSettings
   189  		state.DockerPruneSettings = event.DockerPruneSettings
   190  	}
   191  }