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 }