Skip to content

Commit af18bf3

Browse files
JeffreyCACopilot
andauthored
Fix azd x watch output loop (#8273)
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 927abf2 commit af18bf3

2 files changed

Lines changed: 87 additions & 29 deletions

File tree

cli/azd/extensions/microsoft.azd.extensions/internal/cmd/watch.go

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -142,38 +142,10 @@ func runWatchAction(ctx context.Context, flags *watchFlags) error {
142142
return nil
143143
}
144144

145-
// Fast path: ignore events matching hardcoded glob patterns.
146-
shouldIgnore := false
147-
for _, pattern := range globIgnorePaths {
148-
matched, _ := doublestar.PathMatch(pattern, event.Name)
149-
if matched {
150-
shouldIgnore = true
151-
break
152-
}
153-
}
154-
if shouldIgnore {
145+
if shouldIgnoreWatchEvent(cwd, event.Name, globIgnorePaths, ignoreMatcher) {
155146
continue
156147
}
157148

158-
// Check user-defined ignore patterns (.azdxignore / .gitignore).
159-
// Use os.Stat once to determine if the path is a directory.
160-
info, statErr := os.Stat(event.Name)
161-
isDir := statErr == nil && info.IsDir()
162-
163-
if relPath, relErr := filepath.Rel(cwd, event.Name); relErr != nil {
164-
log.Printf("debug: failed to compute relative path for %s: %v", event.Name, relErr)
165-
} else {
166-
if ignoreMatcher.IsIgnored(relPath, isDir) {
167-
continue
168-
}
169-
// When the path no longer exists (e.g. Remove event), os.Stat fails
170-
// and isDir defaults to false. Re-check as a directory so that
171-
// directory-only patterns (trailing slash) still filter the event.
172-
if statErr != nil && ignoreMatcher.IsIgnored(relPath, true) {
173-
continue
174-
}
175-
}
176-
177149
// Collect unique changes
178150
uniqueChanges[event.Name] = struct{}{}
179151

@@ -237,6 +209,47 @@ func watchRecursive(
237209
})
238210
}
239211

212+
func shouldIgnoreWatchEvent(
213+
root string,
214+
eventName string,
215+
globIgnorePaths []string,
216+
ignoreMatcher *ignore.Matcher,
217+
) bool {
218+
relPath := relativeWatchPath(root, eventName)
219+
220+
// Fast path: ignore events matching hardcoded glob patterns.
221+
for _, pattern := range globIgnorePaths {
222+
matched, _ := doublestar.PathMatch(pattern, relPath)
223+
if matched {
224+
return true
225+
}
226+
}
227+
228+
// Check user-defined ignore patterns (.azdxignore / .gitignore).
229+
// Use os.Stat once to determine if the path is a directory.
230+
info, statErr := os.Stat(eventName)
231+
isDir := statErr == nil && info.IsDir()
232+
233+
if ignoreMatcher.IsIgnored(relPath, isDir) {
234+
return true
235+
}
236+
237+
// When the path no longer exists (e.g. Remove event), os.Stat fails
238+
// and isDir defaults to false. Re-check as a directory so that
239+
// directory-only patterns (trailing slash) still filter the event.
240+
return statErr != nil && ignoreMatcher.IsIgnored(relPath, true)
241+
}
242+
243+
func relativeWatchPath(root string, eventName string) string {
244+
relPath, err := filepath.Rel(root, eventName)
245+
if err != nil {
246+
log.Printf("debug: failed to compute relative path for %s: %v", eventName, err)
247+
relPath = eventName
248+
}
249+
250+
return filepath.ToSlash(relPath)
251+
}
252+
240253
func rebuild(ctx context.Context, extensionPath string) {
241254
flags := &buildFlags{}
242255
defaultBuildFlags(flags)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
package cmd
5+
6+
import (
7+
"os"
8+
"path/filepath"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
13+
"github.com/azure/azure-dev/cli/azd/pkg/ignore"
14+
)
15+
16+
func TestShouldIgnoreWatchEventUsesRelativePathForHardcodedIgnores(t *testing.T) {
17+
root := t.TempDir()
18+
binPath := filepath.Join(root, "bin")
19+
require.NoError(t, os.MkdirAll(binPath, 0755))
20+
21+
ignoreMatcher, err := ignore.NewMatcher(root)
22+
require.NoError(t, err)
23+
24+
globIgnorePaths := []string{"bin", "bin/**/*"}
25+
26+
require.True(t, shouldIgnoreWatchEvent(root, binPath, globIgnorePaths, ignoreMatcher))
27+
require.True(t, shouldIgnoreWatchEvent(root, filepath.Join(binPath, "extension.exe"), globIgnorePaths, ignoreMatcher))
28+
require.True(t, shouldIgnoreWatchEvent(root, "bin", globIgnorePaths, ignoreMatcher))
29+
}
30+
31+
func TestShouldIgnoreWatchEventUsesRelativePathForIgnoreMatcher(t *testing.T) {
32+
root := t.TempDir()
33+
require.NoError(t, os.WriteFile(filepath.Join(root, ignore.AzdxIgnoreFile), []byte("dist/\n"), 0600))
34+
35+
distPath := filepath.Join(root, "dist")
36+
srcPath := filepath.Join(root, "src")
37+
require.NoError(t, os.MkdirAll(distPath, 0755))
38+
require.NoError(t, os.MkdirAll(srcPath, 0755))
39+
40+
ignoreMatcher, err := ignore.NewMatcher(root)
41+
require.NoError(t, err)
42+
43+
require.True(t, shouldIgnoreWatchEvent(root, distPath, nil, ignoreMatcher))
44+
require.False(t, shouldIgnoreWatchEvent(root, srcPath, nil, ignoreMatcher))
45+
}

0 commit comments

Comments
 (0)