Explorar o código

Merge pull request #10311 from milas/fw-ephemeral

watch: ignore ephemeral files & minor output tweaks
Milas Bowman %!s(int64=2) %!d(string=hai) anos
pai
achega
d4f156cc7d
Modificáronse 5 ficheiros con 122 adicións e 11 borrados
  1. 23 9
      pkg/compose/watch.go
  2. 60 0
      pkg/watch/ephemeral.go
  3. 36 0
      pkg/watch/notify.go
  4. 1 2
      pkg/watch/watcher_darwin.go
  5. 2 0
      pkg/watch/watcher_naive.go

+ 23 - 9
pkg/compose/watch.go

@@ -22,14 +22,15 @@ import (
 	"time"
 
 	"github.com/compose-spec/compose-go/types"
-	"github.com/docker/compose/v2/pkg/api"
-	"github.com/docker/compose/v2/pkg/utils"
-	"github.com/docker/compose/v2/pkg/watch"
 	"github.com/jonboulle/clockwork"
 	"github.com/mitchellh/mapstructure"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 	"golang.org/x/sync/errgroup"
+
+	"github.com/docker/compose/v2/pkg/api"
+	"github.com/docker/compose/v2/pkg/utils"
+	"github.com/docker/compose/v2/pkg/watch"
 )
 
 type DevelopmentConfig struct {
@@ -82,10 +83,23 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
 		}
 		bc := service.Build.Context
 
-		ignore, err := watch.LoadDockerIgnore(bc)
+		dockerIgnores, err := watch.LoadDockerIgnore(bc)
+		if err != nil {
+			return err
+		}
+
+		// add a hardcoded set of ignores on top of what came from .dockerignore
+		// some of this should likely be configurable (e.g. there could be cases
+		// where you want `.git` to be synced) but this is suitable for now
+		dotGitIgnore, err := watch.NewDockerPatternMatcher("/", []string{".git/"})
 		if err != nil {
 			return err
 		}
+		ignore := watch.NewCompositeMatcher(
+			dockerIgnores,
+			watch.EphemeralPathMatcher,
+			dotGitIgnore,
+		)
 
 		watcher, err := watch.NewWatcher([]string{bc}, ignore)
 		if err != nil {
@@ -109,7 +123,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
 					path := event.Path()
 
 					for _, trigger := range config.Watch {
-						logrus.Debugf("change deteced on %s - comparing with %s", path, trigger.Path)
+						logrus.Debugf("change detected on %s - comparing with %s", path, trigger.Path)
 						if watch.IsChild(trigger.Path, path) {
 							fmt.Fprintf(s.stderr(), "change detected on %s\n", path)
 
@@ -126,7 +140,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
 									Destination: fmt.Sprintf("%s:%s", name, dest),
 								}
 							case WatchActionRebuild:
-								logrus.Debugf("modified file %s require image to be rebuilt", path)
+								logrus.Debugf("modified file %s requires image to be rebuilt", path)
 								needRebuild <- name
 							default:
 								return fmt.Errorf("watch action %q is not supported", trigger)
@@ -176,7 +190,7 @@ func (s *composeService) makeRebuildFn(ctx context.Context, project *types.Proje
 			Services: services,
 		})
 		if err != nil {
-			fmt.Fprintf(s.stderr(), "Build failed")
+			fmt.Fprintf(s.stderr(), "Build failed\n")
 		}
 		for i, service := range project.Services {
 			if id, ok := imageIds[service.Name]; ok {
@@ -196,7 +210,7 @@ func (s *composeService) makeRebuildFn(ctx context.Context, project *types.Proje
 			},
 		})
 		if err != nil {
-			fmt.Fprintf(s.stderr(), "Application failed to start after update")
+			fmt.Fprintf(s.stderr(), "Application failed to start after update\n")
 		}
 	}
 }
@@ -212,7 +226,7 @@ func (s *composeService) makeSyncFn(ctx context.Context, project *types.Project,
 				if err != nil {
 					return err
 				}
-				fmt.Fprintf(s.stderr(), "%s updated\n", opt.Source)
+				fmt.Fprintf(s.stderr(), "%s updated\n", opt.Destination)
 			}
 		}
 	}

+ 60 - 0
pkg/watch/ephemeral.go

@@ -0,0 +1,60 @@
+/*
+   Copyright 2020 Docker Compose CLI authors
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+*/
+
+package watch
+
+// EphemeralPathMatcher filters out spurious changes that we don't want to
+// rebuild on, like IDE temp/lock files.
+//
+// This isn't an ideal solution. In an ideal world, the user would put
+// everything to ignore in their tiltignore/dockerignore files. This is a
+// stop-gap so they don't have a terrible experience if those files aren't
+// there or aren't in the right places.
+//
+// https://app.clubhouse.io/windmill/story/691/filter-out-ephemeral-file-changes
+var EphemeralPathMatcher = initEphemeralPathMatcher()
+
+func initEphemeralPathMatcher() PathMatcher {
+	golandPatterns := []string{"**/*___jb_old___", "**/*___jb_tmp___", "**/.idea/**"}
+	emacsPatterns := []string{"**/.#*", "**/#*#"}
+	// if .swp is taken (presumably because multiple vims are running in that dir),
+	// vim will go with .swo, .swn, etc, and then even .svz, .svy!
+	// https://github.com/vim/vim/blob/ea781459b9617aa47335061fcc78403495260315/src/memline.c#L5076
+	// ignoring .sw? seems dangerous, since things like .swf or .swi exist, but ignoring the first few
+	// seems safe and should catch most cases
+	vimPatterns := []string{"**/4913", "**/*~", "**/.*.swp", "**/.*.swx", "**/.*.swo", "**/.*.swn"}
+	// kate (the default text editor for KDE) uses a file similar to Vim's .swp
+	// files, but it doesn't have the "incrememnting" character problem mentioned
+	// above
+	katePatterns := []string{"**/.*.kate-swp"}
+	// go stdlib creates tmpfiles to determine umask for setting permissions
+	// during file creation; they are then immediately deleted
+	// https://github.com/golang/go/blob/0b5218cf4e3e5c17344ea113af346e8e0836f6c4/src/cmd/go/internal/work/exec.go#L1764
+	goPatterns := []string{"**/*-go-tmp-umask"}
+
+	var allPatterns []string
+	allPatterns = append(allPatterns, golandPatterns...)
+	allPatterns = append(allPatterns, emacsPatterns...)
+	allPatterns = append(allPatterns, vimPatterns...)
+	allPatterns = append(allPatterns, katePatterns...)
+	allPatterns = append(allPatterns, goPatterns...)
+
+	matcher, err := NewDockerPatternMatcher("/", allPatterns)
+	if err != nil {
+		panic(err)
+	}
+	return matcher
+}

+ 36 - 0
pkg/watch/notify.go

@@ -106,3 +106,39 @@ func DesiredWindowsBufferSize() int {
 func IsWindowsShortReadError(err error) bool {
 	return runtime.GOOS == "windows" && !errors.Is(err, fsnotify.ErrEventOverflow)
 }
+
+type CompositePathMatcher struct {
+	Matchers []PathMatcher
+}
+
+func NewCompositeMatcher(matchers ...PathMatcher) PathMatcher {
+	if len(matchers) == 0 {
+		return EmptyMatcher{}
+	}
+	return CompositePathMatcher{Matchers: matchers}
+}
+
+func (c CompositePathMatcher) Matches(f string) (bool, error) {
+	for _, t := range c.Matchers {
+		ret, err := t.Matches(f)
+		if err != nil {
+			return false, err
+		}
+		if ret {
+			return true, nil
+		}
+	}
+	return false, nil
+}
+
+func (c CompositePathMatcher) MatchesEntireDir(f string) (bool, error) {
+	for _, t := range c.Matchers {
+		matches, err := t.MatchesEntireDir(f)
+		if matches || err != nil {
+			return matches, err
+		}
+	}
+	return false, nil
+}
+
+var _ PathMatcher = CompositePathMatcher{}

+ 1 - 2
pkg/watch/watcher_darwin.go

@@ -20,7 +20,6 @@
 package watch
 
 import (
-	"fmt"
 	"path/filepath"
 	"time"
 
@@ -53,7 +52,6 @@ func (d *fseventNotify) loop() {
 			}
 
 			for _, e := range events {
-				fmt.Println(e)
 				e.Path = filepath.Join("/", e.Path)
 
 				_, isPathWereWatching := d.pathsWereWatching[e.Path]
@@ -67,6 +65,7 @@ func (d *fseventNotify) loop() {
 				if err != nil {
 					logrus.Infof("Error matching path %q: %v", e.Path, err)
 				} else if ignore {
+					logrus.Tracef("Ignoring event for path: %v", e.Path)
 					continue
 				}
 

+ 2 - 0
pkg/watch/watcher_naive.go

@@ -129,6 +129,7 @@ func (d *naiveNotify) watchRecursively(dir string) error {
 		}
 
 		if shouldSkipDir {
+			logrus.Debugf("Ignoring directory and its contents (recursively): %s", path)
 			return filepath.SkipDir
 		}
 
@@ -234,6 +235,7 @@ func (d *naiveNotify) shouldNotify(path string) bool {
 	if err != nil {
 		logrus.Infof("Error matching path %q: %v", path, err)
 	} else if ignore {
+		logrus.Tracef("Ignoring event for path: %v", path)
 		return false
 	}