github.com/grahambrereton-form3/tilt@v0.10.18/internal/engine/buildcontrol/build_control.go (about)

     1  package buildcontrol
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/windmilleng/tilt/internal/store"
     7  	"github.com/windmilleng/tilt/pkg/model"
     8  )
     9  
    10  // NOTE(maia): we eventually want to move the BuildController into its own package
    11  // (as we do with all subscribers), but for now, just move the underlying functions
    12  // so they can be used from elsewhere.
    13  
    14  // Algorithm to choose a manifest to build next.
    15  func NextTargetToBuild(state store.EngineState) *store.ManifestTarget {
    16  	// Don't build anything if there are pending config file changes.
    17  	// We want the Tiltfile to re-run first.
    18  	if len(state.PendingConfigFileChanges) > 0 {
    19  		return nil
    20  	}
    21  
    22  	targets := RemoveTargetsWaitingOnDependencies(state, state.Targets())
    23  
    24  	// If any of the manifest targets haven't been built yet, build them now.
    25  	unbuilt := FindTargetsNeedingInitialBuild(targets)
    26  
    27  	if len(unbuilt) > 0 {
    28  		ret := NextUnbuiltTargetToBuild(unbuilt)
    29  		return ret
    30  	}
    31  
    32  	// Next prioritize builds that crashed and need a rebuilt to have up-to-date code.
    33  	for _, mt := range targets {
    34  		if mt.State.NeedsRebuildFromCrash {
    35  			return mt
    36  		}
    37  	}
    38  
    39  	// Next prioritize builds that have been manually triggered.
    40  	if len(state.TriggerQueue) > 0 {
    41  		mn := state.TriggerQueue[0]
    42  		mt, ok := state.ManifestTargets[mn]
    43  		if ok {
    44  			return mt
    45  		}
    46  	}
    47  
    48  	return EarliestPendingAutoTriggerTarget(targets)
    49  }
    50  
    51  func NextManifestNameToBuild(state store.EngineState) model.ManifestName {
    52  	mt := NextTargetToBuild(state)
    53  	if mt == nil {
    54  		return ""
    55  	}
    56  	return mt.Manifest.Name
    57  }
    58  
    59  func isWaitingOnDependencies(state store.EngineState, mt *store.ManifestTarget) bool {
    60  	// dependencies only block the first build, so if this manifest has ever built, ignore dependencies
    61  	if mt.State.StartedFirstBuild() {
    62  		return false
    63  	}
    64  
    65  	for _, mn := range mt.Manifest.ResourceDependencies {
    66  		ms, ok := state.ManifestState(mn)
    67  		if !ok || ms == nil || ms.RuntimeState == nil || !ms.RuntimeState.HasEverBeenReady() {
    68  			return true
    69  		}
    70  	}
    71  
    72  	return false
    73  }
    74  
    75  func RemoveTargetsWaitingOnDependencies(state store.EngineState, mts []*store.ManifestTarget) []*store.ManifestTarget {
    76  	var ret []*store.ManifestTarget
    77  	for _, mt := range mts {
    78  		if !isWaitingOnDependencies(state, mt) {
    79  			ret = append(ret, mt)
    80  		}
    81  	}
    82  
    83  	return ret
    84  }
    85  
    86  // Helper function for ordering targets that have never been built before.
    87  func NextUnbuiltTargetToBuild(unbuilt []*store.ManifestTarget) *store.ManifestTarget {
    88  	// unresourced YAML goes first
    89  	unresourced := FindUnresourcedYAML(unbuilt)
    90  	if unresourced != nil {
    91  		return unresourced
    92  	}
    93  
    94  	// Local resources come before all cluster resources (b/c LR's may
    95  	// change things on disk that cluster resources then pull in).
    96  	localTargets := FindLocalTargets(unbuilt)
    97  	if len(localTargets) > 0 {
    98  		return localTargets[0]
    99  	}
   100  
   101  	// If this is Kubernetes, unbuilt resources go first.
   102  	// (If this is Docker Compose, we want to trust the ordering
   103  	// that docker-compose put things in.)
   104  	deployOnlyK8sTargets := FindDeployOnlyK8sManifestTargets(unbuilt)
   105  	if len(deployOnlyK8sTargets) > 0 {
   106  		return deployOnlyK8sTargets[0]
   107  	}
   108  
   109  	return unbuilt[0]
   110  }
   111  
   112  func FindUnresourcedYAML(targets []*store.ManifestTarget) *store.ManifestTarget {
   113  	for _, target := range targets {
   114  		if target.Manifest.ManifestName() == model.UnresourcedYAMLManifestName {
   115  			return target
   116  		}
   117  	}
   118  	return nil
   119  }
   120  
   121  func FindDeployOnlyK8sManifestTargets(targets []*store.ManifestTarget) []*store.ManifestTarget {
   122  	result := []*store.ManifestTarget{}
   123  	for _, target := range targets {
   124  		if target.Manifest.IsK8s() && len(target.Manifest.ImageTargets) == 0 {
   125  			result = append(result, target)
   126  		}
   127  	}
   128  	return result
   129  }
   130  
   131  func FindLocalTargets(targets []*store.ManifestTarget) []*store.ManifestTarget {
   132  	result := []*store.ManifestTarget{}
   133  	for _, target := range targets {
   134  		if target.Manifest.IsLocal() {
   135  			result = append(result, target)
   136  		}
   137  	}
   138  	return result
   139  }
   140  
   141  // Go through all the manifests, and check:
   142  // 1) all pending file changes, and
   143  // 2) all pending manifest changes
   144  // The earliest one is the one we want.
   145  //
   146  // If no targets are pending, return nil
   147  func EarliestPendingAutoTriggerTarget(targets []*store.ManifestTarget) *store.ManifestTarget {
   148  	var choice *store.ManifestTarget
   149  	earliest := time.Now()
   150  
   151  	for _, mt := range targets {
   152  		ok, newTime := mt.State.HasPendingChangesBefore(earliest)
   153  		if ok {
   154  			if !mt.Manifest.TriggerMode.AutoOnChange() {
   155  				// Don't trigger update of a manual manifest just b/c if has
   156  				// pending changes; must come through the TriggerQueue, above.
   157  				continue
   158  			}
   159  			choice = mt
   160  			earliest = newTime
   161  		}
   162  	}
   163  
   164  	return choice
   165  }
   166  
   167  func FindTargetsNeedingInitialBuild(targets []*store.ManifestTarget) []*store.ManifestTarget {
   168  	result := []*store.ManifestTarget{}
   169  	for _, target := range targets {
   170  		if !target.State.StartedFirstBuild() && target.Manifest.TriggerMode.AutoInitial() {
   171  			result = append(result, target)
   172  		}
   173  	}
   174  	return result
   175  }