Skip to content

Commit 20ce30c

Browse files
authored
Merge pull request #2684 from lizardruss/ENG-1820
fix: run_watch --excludes with ./ not excluded
2 parents 175f651 + 2021519 commit 20ce30c

5 files changed

Lines changed: 171 additions & 11 deletions

File tree

e2e/tests/pipelines/pipelines.go

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package pipelines
22

33
import (
4+
"bytes"
45
"context"
5-
"github.com/loft-sh/devspace/pkg/devspace/context/values"
6-
"github.com/onsi/ginkgo/v2"
6+
"io"
77
"os"
88
"time"
99

10+
"github.com/loft-sh/devspace/pkg/devspace/context/values"
11+
logpkg "github.com/loft-sh/devspace/pkg/util/log"
12+
"github.com/onsi/ginkgo/v2"
13+
"github.com/onsi/gomega"
14+
"github.com/sirupsen/logrus"
15+
1016
"github.com/loft-sh/devspace/cmd"
1117
"github.com/loft-sh/devspace/cmd/flags"
1218
"github.com/loft-sh/devspace/e2e/framework"
@@ -166,6 +172,136 @@ var _ = DevSpaceDescribe("pipelines", func() {
166172
}
167173
})
168174

175+
ginkgo.It("should watch files with excludes, no ./", func(ctx context.Context) {
176+
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/run_watch")
177+
framework.ExpectNoError(err)
178+
ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir)
179+
180+
ns, err := kubeClient.CreateNamespace("pipelines")
181+
framework.ExpectNoError(err)
182+
ginkgo.DeferCleanup(framework.ExpectDeleteNamespace, kubeClient, ns)
183+
184+
done := make(chan error)
185+
cancelCtx, cancel := context.WithCancel(ctx)
186+
ginkgo.DeferCleanup(cancel)
187+
188+
output := &bytes.Buffer{}
189+
multiWriter := io.MultiWriter(output, os.Stdout)
190+
log := logpkg.NewStreamLogger(multiWriter, multiWriter, logrus.DebugLevel)
191+
192+
go func() {
193+
devCmd := &cmd.RunPipelineCmd{
194+
GlobalFlags: &flags.GlobalFlags{
195+
NoWarn: true,
196+
Namespace: ns,
197+
},
198+
Pipeline: "no-dot-slash-exclude",
199+
Ctx: cancelCtx,
200+
Log: log,
201+
}
202+
err := devCmd.RunDefault(f)
203+
if err != nil {
204+
f.GetLog().Errorf("error: %v", err)
205+
}
206+
done <- err
207+
}()
208+
209+
gomega.Eventually(func(g gomega.Gomega) {
210+
g.Expect(output.String()).Should(gomega.ContainSubstring("Start watching"))
211+
}).
212+
WithTimeout(5 * time.Second).
213+
Should(gomega.Succeed())
214+
215+
err = os.WriteFile("foo1/test.txt", []byte("abc.txt"), 0777)
216+
framework.ExpectNoError(err)
217+
218+
gomega.Eventually(func(g gomega.Gomega) {
219+
g.Expect(output.String()).Should(gomega.ContainSubstring("Restarting command because 'foo1/test.txt' has changed"))
220+
}).
221+
WithTimeout(5 * time.Second).
222+
Should(gomega.Succeed())
223+
224+
err = os.WriteFile("foo2/test.txt", []byte("abc.txt"), 0777)
225+
framework.ExpectNoError(err)
226+
227+
gomega.Consistently(func(g gomega.Gomega) {
228+
g.Expect(output.String()).ShouldNot(gomega.ContainSubstring("Restarting command because 'foo2/test.txt' has changed"))
229+
}).
230+
WithTimeout(5 * time.Second).
231+
Should(gomega.Succeed())
232+
233+
cancel()
234+
err = <-done
235+
if err != nil && err != context.Canceled {
236+
framework.ExpectNoError(err)
237+
}
238+
})
239+
240+
ginkgo.It("should watch files with excludes, with ./", func(ctx context.Context) {
241+
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/run_watch")
242+
framework.ExpectNoError(err)
243+
ginkgo.DeferCleanup(framework.CleanupTempDir, initialDir, tempDir)
244+
245+
ns, err := kubeClient.CreateNamespace("pipelines")
246+
framework.ExpectNoError(err)
247+
ginkgo.DeferCleanup(framework.ExpectDeleteNamespace, kubeClient, ns)
248+
249+
done := make(chan error)
250+
cancelCtx, cancel := context.WithCancel(ctx)
251+
ginkgo.DeferCleanup(cancel)
252+
253+
output := &bytes.Buffer{}
254+
multiWriter := io.MultiWriter(output, os.Stdout)
255+
log := logpkg.NewStreamLogger(multiWriter, multiWriter, logrus.DebugLevel)
256+
257+
go func() {
258+
devCmd := &cmd.RunPipelineCmd{
259+
GlobalFlags: &flags.GlobalFlags{
260+
NoWarn: true,
261+
Namespace: ns,
262+
},
263+
Pipeline: "dot-slash-exclude",
264+
Ctx: cancelCtx,
265+
Log: log,
266+
}
267+
err := devCmd.RunDefault(f)
268+
if err != nil {
269+
f.GetLog().Errorf("error: %v", err)
270+
}
271+
done <- err
272+
}()
273+
274+
gomega.Eventually(func(g gomega.Gomega) {
275+
g.Expect(output.String()).Should(gomega.ContainSubstring("Start watching"))
276+
}).
277+
WithTimeout(5 * time.Second).
278+
Should(gomega.Succeed())
279+
280+
err = os.WriteFile("foo1/test.txt", []byte("abc.txt"), 0777)
281+
framework.ExpectNoError(err)
282+
283+
gomega.Eventually(func(g gomega.Gomega) {
284+
g.Expect(output.String()).Should(gomega.ContainSubstring("Restarting command because 'foo1/test.txt' has changed"))
285+
}).
286+
WithTimeout(5 * time.Second).
287+
Should(gomega.Succeed())
288+
289+
err = os.WriteFile("foo2/test.txt", []byte("abc.txt"), 0777)
290+
framework.ExpectNoError(err)
291+
292+
gomega.Consistently(func(g gomega.Gomega) {
293+
g.Expect(output.String()).ShouldNot(gomega.ContainSubstring("Restarting command because 'foo2/test.txt' has changed"))
294+
}).
295+
WithTimeout(5 * time.Second).
296+
Should(gomega.Succeed())
297+
298+
cancel()
299+
err = <-done
300+
if err != nil && err != context.Canceled {
301+
framework.ExpectNoError(err)
302+
}
303+
})
304+
169305
ginkgo.It("should use --set and --set-string values from run_pipelines command", func() {
170306
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/run_pipelines")
171307
framework.ExpectNoError(err)

e2e/tests/pipelines/testdata/run_watch/devspace.yaml

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,19 @@ pipelines:
1818
1919
dev:
2020
run: |-
21-
run_watch -p *.txt -- run_pipelines other3
21+
run_watch -p '*.txt' -- run_pipelines other3
22+
23+
no-dot-slash-exclude: |-
24+
run_watch \
25+
--path 'foo*/**/*' \
26+
--exclude 'foo2/*' \
27+
-- date
28+
29+
dot-slash-exclude: |-
30+
# Still triggers run_watch for foo2/bar2.txt changes
31+
run_watch \
32+
--path './foo*/**/*' \
33+
--exclude './foo2/*' \
34+
-- date
35+
36+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
aaa
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
aaa

pkg/devspace/pipeline/engine/basichandler/commands/run_watch.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ package commands
33
import (
44
"context"
55
"fmt"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"time"
10+
611
"github.com/bmatcuk/doublestar"
712
"github.com/jessevdk/go-flags"
813
types2 "github.com/loft-sh/devspace/pkg/devspace/pipeline/engine/types"
@@ -12,10 +17,6 @@ import (
1217
"github.com/mgutz/ansi"
1318
"github.com/pkg/errors"
1419
"mvdan.cc/sh/v3/interp"
15-
"os"
16-
"path/filepath"
17-
"strings"
18-
"time"
1920
)
2021

2122
type RunWatchOptions struct {
@@ -54,17 +55,23 @@ type watcher struct {
5455
}
5556

5657
func (w *watcher) Watch(ctx context.Context, action func(ctx context.Context) error, log log.Logger) error {
57-
patterns := w.options.Paths
5858
if w.options.SkipAndSilent {
5959
w.options.Silent = true
6060
w.options.SkipInitial = true
6161
}
6262

6363
// prepare patterns
64+
patterns := w.options.Paths
6465
for i, p := range patterns {
6566
patterns[i] = strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(p), "./"), "/")
6667
}
6768

69+
// prepare excludes
70+
excludes := w.options.Exclude
71+
for i, e := range excludes {
72+
excludes[i] = strings.TrimSuffix(strings.TrimPrefix(strings.TrimSpace(e), "./"), "/")
73+
}
74+
6875
// get folders from patterns
6976
pathsToWatch := map[string]bool{}
7077
for i, p := range patterns {
@@ -147,10 +154,10 @@ func (w *watcher) Watch(ctx context.Context, action func(ctx context.Context) er
147154
}
148155

149156
// start command
150-
return w.handleCommand(ctx, patterns, action, globalChannel)
157+
return w.handleCommand(ctx, patterns, excludes, action, globalChannel)
151158
}
152159

153-
func (w *watcher) handleCommand(ctx context.Context, patterns []string, action func(ctx context.Context) error, events chan string) error {
160+
func (w *watcher) handleCommand(ctx context.Context, patterns []string, excludes []string, action func(ctx context.Context) error, events chan string) error {
154161
hc := interp.HandlerCtx(ctx)
155162
var t *tomb.Tomb
156163
if !w.options.SkipInitial {
@@ -179,7 +186,7 @@ func (w *watcher) handleCommand(ctx context.Context, patterns []string, action f
179186

180187
if hasMatched {
181188
excluded := false
182-
for _, excludePath := range w.options.Exclude {
189+
for _, excludePath := range excludes {
183190
excluded, _ = doublestar.Match(excludePath, e)
184191
if excluded {
185192
break

0 commit comments

Comments
 (0)