github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/controllers/core/tiltfile/filewatch.go (about)

     1  package tiltfile
     2  
     3  import (
     4  	"fmt"
     5  	"path/filepath"
     6  
     7  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
     8  
     9  	"github.com/tilt-dev/tilt/internal/controllers/apiset"
    10  	"github.com/tilt-dev/tilt/internal/store"
    11  	"github.com/tilt-dev/tilt/pkg/apis"
    12  	"github.com/tilt-dev/tilt/pkg/apis/core/v1alpha1"
    13  	"github.com/tilt-dev/tilt/pkg/model"
    14  )
    15  
    16  type WatchInputs struct {
    17  	TiltfileManifestName model.ManifestName
    18  	TiltfilePath         string
    19  	Manifests            []model.Manifest
    20  	ConfigFiles          []string
    21  	WatchSettings        model.WatchSettings
    22  	Tiltignore           model.Dockerignore
    23  	EngineMode           store.EngineMode
    24  }
    25  
    26  type WatchableTarget interface {
    27  	GetFileWatchIgnores() []v1alpha1.IgnoreDef
    28  	Dependencies() []string
    29  	ID() model.TargetID
    30  }
    31  
    32  var _ WatchableTarget = model.ImageTarget{}
    33  var _ WatchableTarget = model.LocalTarget{}
    34  var _ WatchableTarget = model.K8sTarget{}
    35  
    36  func specForTarget(t WatchableTarget, globalIgnores []model.Dockerignore) *v1alpha1.FileWatchSpec {
    37  	watchedPaths := append([]string(nil), t.Dependencies()...)
    38  	if len(watchedPaths) == 0 {
    39  		return nil
    40  	}
    41  
    42  	spec := &v1alpha1.FileWatchSpec{
    43  		WatchedPaths: watchedPaths,
    44  		Ignores:      t.GetFileWatchIgnores(),
    45  	}
    46  
    47  	// process global ignores last
    48  	addGlobalIgnoresToSpec(spec, globalIgnores)
    49  
    50  	return spec
    51  }
    52  
    53  func addGlobalIgnoresToSpec(spec *v1alpha1.FileWatchSpec, globalIgnores []model.Dockerignore) {
    54  	for _, gi := range globalIgnores {
    55  		spec.Ignores = append(spec.Ignores, v1alpha1.IgnoreDef{
    56  			BasePath: gi.LocalPath,
    57  			Patterns: append([]string(nil), gi.Patterns...),
    58  		})
    59  	}
    60  }
    61  
    62  // Create FileWatch specs from Tilt manifests in the engine state.
    63  func ToFileWatchObjects(watchInputs WatchInputs, disableSources map[model.ManifestName]*v1alpha1.DisableSource) apiset.TypedObjectSet {
    64  	result := apiset.TypedObjectSet{}
    65  	if !watchInputs.EngineMode.WatchesFiles() {
    66  		return result
    67  	}
    68  
    69  	// TODO(milas): how can global ignores fit into the API model more cleanly?
    70  	globalIgnores := globalIgnores(watchInputs)
    71  	for _, m := range watchInputs.Manifests {
    72  		for _, t := range m.TargetSpecs() {
    73  			targetID := t.ID()
    74  			// ignore targets that have already been processed or aren't watchable
    75  			t, ok := t.(WatchableTarget)
    76  			if !ok || targetID.Empty() {
    77  				continue
    78  			}
    79  			name := apis.SanitizeName(targetID.String())
    80  			existing, ok := result[name]
    81  			if ok {
    82  				fw := existing.(*v1alpha1.FileWatch)
    83  				fw.Spec.DisableSource = mergeDisableSource(fw.Spec.DisableSource, disableSources[m.Name])
    84  				continue
    85  			}
    86  
    87  			spec := specForTarget(t, globalIgnores)
    88  			if spec != nil {
    89  				fw := &v1alpha1.FileWatch{
    90  					ObjectMeta: metav1.ObjectMeta{
    91  						Name: name,
    92  						Annotations: map[string]string{
    93  							v1alpha1.AnnotationManifest: string(m.Name),
    94  							v1alpha1.AnnotationTargetID: targetID.String(),
    95  						},
    96  					},
    97  					Spec: *spec.DeepCopy(),
    98  				}
    99  				fw.Spec.DisableSource = disableSources[m.Name]
   100  				result[fw.Name] = fw
   101  			}
   102  		}
   103  	}
   104  
   105  	paths := []string{}
   106  	if len(watchInputs.ConfigFiles) > 0 {
   107  		paths = append(paths, watchInputs.ConfigFiles...)
   108  	} else if watchInputs.TiltfilePath != "" {
   109  		// A complete ConfigFiles set should include the Tiltfile. If it doesn't,
   110  		// add it to the watch list now.
   111  		paths = append(paths, watchInputs.TiltfilePath)
   112  	}
   113  
   114  	if len(paths) > 0 {
   115  		id := fmt.Sprintf("%s:%s", model.TargetTypeConfigs, watchInputs.TiltfileManifestName)
   116  		configFw := &v1alpha1.FileWatch{
   117  			ObjectMeta: metav1.ObjectMeta{
   118  				Name: apis.SanitizeName(id),
   119  				Annotations: map[string]string{
   120  					v1alpha1.AnnotationManifest: watchInputs.TiltfileManifestName.String(),
   121  					v1alpha1.AnnotationTargetID: id,
   122  				},
   123  			},
   124  			Spec: v1alpha1.FileWatchSpec{
   125  				WatchedPaths: paths,
   126  			},
   127  		}
   128  
   129  		addGlobalIgnoresToSpec(&configFw.Spec, globalIgnores)
   130  		result[configFw.Name] = configFw
   131  	}
   132  
   133  	return result
   134  }
   135  
   136  // globalIgnores returns a list of global ignore patterns.
   137  func globalIgnores(watchInputs WatchInputs) []model.Dockerignore {
   138  	ignores := []model.Dockerignore{}
   139  	if !watchInputs.Tiltignore.Empty() {
   140  		ignores = append(ignores, watchInputs.Tiltignore)
   141  	}
   142  	ignores = append(ignores, watchInputs.WatchSettings.Ignores...)
   143  
   144  	for _, manifest := range watchInputs.Manifests {
   145  		for _, iTarget := range manifest.ImageTargets {
   146  			customBuild := iTarget.CustomBuildInfo()
   147  			if customBuild.OutputsImageRefTo != "" {
   148  				// this could be smarter and try to group by local path
   149  				ignores = append(ignores, model.Dockerignore{
   150  					LocalPath: filepath.Dir(customBuild.OutputsImageRefTo),
   151  					Source:    "outputs_image_ref_to",
   152  					Patterns:  []string{filepath.Base(customBuild.OutputsImageRefTo)},
   153  				})
   154  			}
   155  		}
   156  	}
   157  
   158  	return ignores
   159  }