Skip to content

Commit d4f156c

Browse files
authored
Merge pull request docker#10311 from milas/fw-ephemeral
watch: ignore ephemeral files & minor output tweaks
2 parents c0daf8d + da1ca57 commit d4f156c

File tree

5 files changed

+122
-11
lines changed

5 files changed

+122
-11
lines changed

pkg/compose/watch.go

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@ import (
2222
"time"
2323

2424
"github.com/compose-spec/compose-go/types"
25-
"github.com/docker/compose/v2/pkg/api"
26-
"github.com/docker/compose/v2/pkg/utils"
27-
"github.com/docker/compose/v2/pkg/watch"
2825
"github.com/jonboulle/clockwork"
2926
"github.com/mitchellh/mapstructure"
3027
"github.com/pkg/errors"
3128
"github.com/sirupsen/logrus"
3229
"golang.org/x/sync/errgroup"
30+
31+
"github.com/docker/compose/v2/pkg/api"
32+
"github.com/docker/compose/v2/pkg/utils"
33+
"github.com/docker/compose/v2/pkg/watch"
3334
)
3435

3536
type DevelopmentConfig struct {
@@ -82,10 +83,23 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
8283
}
8384
bc := service.Build.Context
8485

85-
ignore, err := watch.LoadDockerIgnore(bc)
86+
dockerIgnores, err := watch.LoadDockerIgnore(bc)
87+
if err != nil {
88+
return err
89+
}
90+
91+
// add a hardcoded set of ignores on top of what came from .dockerignore
92+
// some of this should likely be configurable (e.g. there could be cases
93+
// where you want `.git` to be synced) but this is suitable for now
94+
dotGitIgnore, err := watch.NewDockerPatternMatcher("/", []string{".git/"})
8695
if err != nil {
8796
return err
8897
}
98+
ignore := watch.NewCompositeMatcher(
99+
dockerIgnores,
100+
watch.EphemeralPathMatcher,
101+
dotGitIgnore,
102+
)
89103

90104
watcher, err := watch.NewWatcher([]string{bc}, ignore)
91105
if err != nil {
@@ -109,7 +123,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
109123
path := event.Path()
110124

111125
for _, trigger := range config.Watch {
112-
logrus.Debugf("change deteced on %s - comparing with %s", path, trigger.Path)
126+
logrus.Debugf("change detected on %s - comparing with %s", path, trigger.Path)
113127
if watch.IsChild(trigger.Path, path) {
114128
fmt.Fprintf(s.stderr(), "change detected on %s\n", path)
115129

@@ -126,7 +140,7 @@ func (s *composeService) Watch(ctx context.Context, project *types.Project, serv
126140
Destination: fmt.Sprintf("%s:%s", name, dest),
127141
}
128142
case WatchActionRebuild:
129-
logrus.Debugf("modified file %s require image to be rebuilt", path)
143+
logrus.Debugf("modified file %s requires image to be rebuilt", path)
130144
needRebuild <- name
131145
default:
132146
return fmt.Errorf("watch action %q is not supported", trigger)
@@ -176,7 +190,7 @@ func (s *composeService) makeRebuildFn(ctx context.Context, project *types.Proje
176190
Services: services,
177191
})
178192
if err != nil {
179-
fmt.Fprintf(s.stderr(), "Build failed")
193+
fmt.Fprintf(s.stderr(), "Build failed\n")
180194
}
181195
for i, service := range project.Services {
182196
if id, ok := imageIds[service.Name]; ok {
@@ -196,7 +210,7 @@ func (s *composeService) makeRebuildFn(ctx context.Context, project *types.Proje
196210
},
197211
})
198212
if err != nil {
199-
fmt.Fprintf(s.stderr(), "Application failed to start after update")
213+
fmt.Fprintf(s.stderr(), "Application failed to start after update\n")
200214
}
201215
}
202216
}
@@ -212,7 +226,7 @@ func (s *composeService) makeSyncFn(ctx context.Context, project *types.Project,
212226
if err != nil {
213227
return err
214228
}
215-
fmt.Fprintf(s.stderr(), "%s updated\n", opt.Source)
229+
fmt.Fprintf(s.stderr(), "%s updated\n", opt.Destination)
216230
}
217231
}
218232
}

pkg/watch/ephemeral.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package watch
18+
19+
// EphemeralPathMatcher filters out spurious changes that we don't want to
20+
// rebuild on, like IDE temp/lock files.
21+
//
22+
// This isn't an ideal solution. In an ideal world, the user would put
23+
// everything to ignore in their tiltignore/dockerignore files. This is a
24+
// stop-gap so they don't have a terrible experience if those files aren't
25+
// there or aren't in the right places.
26+
//
27+
// https://app.clubhouse.io/windmill/story/691/filter-out-ephemeral-file-changes
28+
var EphemeralPathMatcher = initEphemeralPathMatcher()
29+
30+
func initEphemeralPathMatcher() PathMatcher {
31+
golandPatterns := []string{"**/*___jb_old___", "**/*___jb_tmp___", "**/.idea/**"}
32+
emacsPatterns := []string{"**/.#*", "**/#*#"}
33+
// if .swp is taken (presumably because multiple vims are running in that dir),
34+
// vim will go with .swo, .swn, etc, and then even .svz, .svy!
35+
// https://github.com/vim/vim/blob/ea781459b9617aa47335061fcc78403495260315/src/memline.c#L5076
36+
// ignoring .sw? seems dangerous, since things like .swf or .swi exist, but ignoring the first few
37+
// seems safe and should catch most cases
38+
vimPatterns := []string{"**/4913", "**/*~", "**/.*.swp", "**/.*.swx", "**/.*.swo", "**/.*.swn"}
39+
// kate (the default text editor for KDE) uses a file similar to Vim's .swp
40+
// files, but it doesn't have the "incrememnting" character problem mentioned
41+
// above
42+
katePatterns := []string{"**/.*.kate-swp"}
43+
// go stdlib creates tmpfiles to determine umask for setting permissions
44+
// during file creation; they are then immediately deleted
45+
// https://github.com/golang/go/blob/0b5218cf4e3e5c17344ea113af346e8e0836f6c4/src/cmd/go/internal/work/exec.go#L1764
46+
goPatterns := []string{"**/*-go-tmp-umask"}
47+
48+
var allPatterns []string
49+
allPatterns = append(allPatterns, golandPatterns...)
50+
allPatterns = append(allPatterns, emacsPatterns...)
51+
allPatterns = append(allPatterns, vimPatterns...)
52+
allPatterns = append(allPatterns, katePatterns...)
53+
allPatterns = append(allPatterns, goPatterns...)
54+
55+
matcher, err := NewDockerPatternMatcher("/", allPatterns)
56+
if err != nil {
57+
panic(err)
58+
}
59+
return matcher
60+
}

pkg/watch/notify.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,39 @@ func DesiredWindowsBufferSize() int {
106106
func IsWindowsShortReadError(err error) bool {
107107
return runtime.GOOS == "windows" && !errors.Is(err, fsnotify.ErrEventOverflow)
108108
}
109+
110+
type CompositePathMatcher struct {
111+
Matchers []PathMatcher
112+
}
113+
114+
func NewCompositeMatcher(matchers ...PathMatcher) PathMatcher {
115+
if len(matchers) == 0 {
116+
return EmptyMatcher{}
117+
}
118+
return CompositePathMatcher{Matchers: matchers}
119+
}
120+
121+
func (c CompositePathMatcher) Matches(f string) (bool, error) {
122+
for _, t := range c.Matchers {
123+
ret, err := t.Matches(f)
124+
if err != nil {
125+
return false, err
126+
}
127+
if ret {
128+
return true, nil
129+
}
130+
}
131+
return false, nil
132+
}
133+
134+
func (c CompositePathMatcher) MatchesEntireDir(f string) (bool, error) {
135+
for _, t := range c.Matchers {
136+
matches, err := t.MatchesEntireDir(f)
137+
if matches || err != nil {
138+
return matches, err
139+
}
140+
}
141+
return false, nil
142+
}
143+
144+
var _ PathMatcher = CompositePathMatcher{}

pkg/watch/watcher_darwin.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
package watch
2121

2222
import (
23-
"fmt"
2423
"path/filepath"
2524
"time"
2625

@@ -53,7 +52,6 @@ func (d *fseventNotify) loop() {
5352
}
5453

5554
for _, e := range events {
56-
fmt.Println(e)
5755
e.Path = filepath.Join("/", e.Path)
5856

5957
_, isPathWereWatching := d.pathsWereWatching[e.Path]
@@ -67,6 +65,7 @@ func (d *fseventNotify) loop() {
6765
if err != nil {
6866
logrus.Infof("Error matching path %q: %v", e.Path, err)
6967
} else if ignore {
68+
logrus.Tracef("Ignoring event for path: %v", e.Path)
7069
continue
7170
}
7271

pkg/watch/watcher_naive.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ func (d *naiveNotify) watchRecursively(dir string) error {
129129
}
130130

131131
if shouldSkipDir {
132+
logrus.Debugf("Ignoring directory and its contents (recursively): %s", path)
132133
return filepath.SkipDir
133134
}
134135

@@ -234,6 +235,7 @@ func (d *naiveNotify) shouldNotify(path string) bool {
234235
if err != nil {
235236
logrus.Infof("Error matching path %q: %v", path, err)
236237
} else if ignore {
238+
logrus.Tracef("Ignoring event for path: %v", path)
237239
return false
238240
}
239241

0 commit comments

Comments
 (0)