Add wildcard (*) support for include_ext to watch all file extensions (#833)
* Initial plan * Add wildcard (*) support for include_ext to watch all file extensions Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com> * Add test coverage for isBinPath empty binPath case Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com> * Extract wildcard string literal to extWildcard constant Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: xiantang <34479567+xiantang@users.noreply.github.com>
This commit is contained in:
+1
-1
@@ -22,7 +22,7 @@ entrypoint = ["./tmp/main"]
|
||||
full_bin = "APP_ENV=dev APP_USER=air ./tmp/main"
|
||||
# Add additional arguments when running binary (bin/full_bin). Will run './tmp/main hello world'.
|
||||
args_bin = ["hello", "world"]
|
||||
# Watch these filename extensions.
|
||||
# Watch these filename extensions. Use ["*"] to watch all file extensions.
|
||||
include_ext = ["go", "tpl", "tmpl", "html"]
|
||||
# Ignore these filename extensions or directories.
|
||||
exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"]
|
||||
|
||||
@@ -21,6 +21,8 @@ import (
|
||||
|
||||
const (
|
||||
sliceCmdArgSeparator = ","
|
||||
// extWildcard is used in include_ext to match all file extensions
|
||||
extWildcard = "*"
|
||||
)
|
||||
|
||||
func (e *Engine) mainLog(format string, v ...interface{}) {
|
||||
@@ -172,6 +174,10 @@ func (e *Engine) checkIncludeFile(path string) bool {
|
||||
func (e *Engine) isIncludeExt(path string) bool {
|
||||
ext := filepath.Ext(path)
|
||||
for _, v := range e.config.Build.IncludeExt {
|
||||
if strings.TrimSpace(v) == extWildcard {
|
||||
// Wildcard matches all files, but exclude the binary file
|
||||
return !e.isBinPath(path)
|
||||
}
|
||||
if ext == "."+strings.TrimSpace(v) {
|
||||
return true
|
||||
}
|
||||
@@ -179,6 +185,24 @@ func (e *Engine) isIncludeExt(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// isBinPath checks if the given path is the binary file path
|
||||
func (e *Engine) isBinPath(path string) bool {
|
||||
binPath := e.config.binPath()
|
||||
if binPath == "" {
|
||||
return false
|
||||
}
|
||||
// Normalize the path for comparison
|
||||
absPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
absBinPath, err := filepath.Abs(binPath)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return absPath == absBinPath
|
||||
}
|
||||
|
||||
func (e *Engine) isExcludeRegex(path string) (bool, error) {
|
||||
regexes, err := e.config.Build.RegexCompiled()
|
||||
if err != nil {
|
||||
|
||||
@@ -398,6 +398,93 @@ func TestCheckIncludeFile(t *testing.T) {
|
||||
assert.False(t, e.checkIncludeFile("."))
|
||||
}
|
||||
|
||||
func TestIsIncludeExt(t *testing.T) {
|
||||
e := Engine{
|
||||
config: &Config{
|
||||
Build: cfgBuild{
|
||||
IncludeExt: []string{"go", "html"},
|
||||
},
|
||||
},
|
||||
}
|
||||
assert.True(t, e.isIncludeExt("main.go"))
|
||||
assert.True(t, e.isIncludeExt("/path/to/file.html"))
|
||||
assert.False(t, e.isIncludeExt("main.js"))
|
||||
assert.False(t, e.isIncludeExt("file"))
|
||||
}
|
||||
|
||||
func TestIsIncludeExtWildcard(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
binPath := filepath.Join(tmpDir, "tmp", "main")
|
||||
|
||||
e := Engine{
|
||||
config: &Config{
|
||||
Root: tmpDir,
|
||||
Build: cfgBuild{
|
||||
IncludeExt: []string{"*"},
|
||||
Entrypoint: entrypoint{binPath},
|
||||
},
|
||||
},
|
||||
}
|
||||
// Wildcard should match all file extensions
|
||||
assert.True(t, e.isIncludeExt("main.go"))
|
||||
assert.True(t, e.isIncludeExt("/path/to/file.html"))
|
||||
assert.True(t, e.isIncludeExt("main.js"))
|
||||
assert.True(t, e.isIncludeExt("file.css"))
|
||||
assert.True(t, e.isIncludeExt("file")) // files without extension
|
||||
assert.True(t, e.isIncludeExt("/path/noext")) // files without extension
|
||||
assert.False(t, e.isIncludeExt(binPath)) // binary file should be excluded
|
||||
assert.True(t, e.isIncludeExt("some/other/bin")) // other files without extension are ok
|
||||
}
|
||||
|
||||
func TestIsIncludeExtWildcardWithSpaces(t *testing.T) {
|
||||
e := Engine{
|
||||
config: &Config{
|
||||
Build: cfgBuild{
|
||||
IncludeExt: []string{" * "},
|
||||
Entrypoint: entrypoint{"/tmp/main"},
|
||||
},
|
||||
},
|
||||
}
|
||||
// Wildcard with spaces should still work
|
||||
assert.True(t, e.isIncludeExt("main.go"))
|
||||
assert.True(t, e.isIncludeExt("file.html"))
|
||||
}
|
||||
|
||||
func TestIsBinPath(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
binPath := filepath.Join(tmpDir, "tmp", "main")
|
||||
|
||||
e := Engine{
|
||||
config: &Config{
|
||||
Root: tmpDir,
|
||||
Build: cfgBuild{
|
||||
Entrypoint: entrypoint{binPath},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Test matching path returns true
|
||||
assert.True(t, e.isBinPath(binPath))
|
||||
// Test non-matching paths return false
|
||||
assert.False(t, e.isBinPath(filepath.Join(tmpDir, "other", "file")))
|
||||
assert.False(t, e.isBinPath("unrelated.go"))
|
||||
}
|
||||
|
||||
func TestIsBinPathEmptyBinPath(t *testing.T) {
|
||||
// Test when binPath is empty (no entrypoint configured)
|
||||
e := Engine{
|
||||
config: &Config{
|
||||
Build: cfgBuild{
|
||||
Entrypoint: entrypoint{}, // empty entrypoint
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Should return false when binPath is empty
|
||||
assert.False(t, e.isBinPath("/some/path"))
|
||||
assert.False(t, e.isBinPath("main.go"))
|
||||
}
|
||||
|
||||
func TestJoinPathRelative(t *testing.T) {
|
||||
root, err := filepath.Abs("test")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user