Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 33eb969778 | |||
| 8b8fa70ce4 | |||
| 4d0b91ed94 | |||
| 12e0612b48 |
5
main.go
5
main.go
@@ -55,7 +55,10 @@ Features:
|
||||
- Parallel file processing
|
||||
- Command filtering and organization`,
|
||||
PersistentPreRun: func(cmd *cobra.Command, args []string) {
|
||||
logger.InitFlag()
|
||||
logLevelStr, _ := cmd.Flags().GetString("loglevel")
|
||||
logLevel := logger.ParseLevel(logLevelStr)
|
||||
logger.SetLevel(logLevel)
|
||||
mainLogger.SetLevel(logLevel)
|
||||
mainLogger.Info("Initializing with log level: %s", logger.GetLevel().String())
|
||||
mainLogger.Trace("Full argv: %v", os.Args)
|
||||
},
|
||||
|
||||
@@ -223,7 +223,7 @@ func ProcessRegex(content string, command utils.ModifyCommand, filename string)
|
||||
modifiedGroupsCount++
|
||||
}
|
||||
}
|
||||
matchLogger.Info("%d of %d capture groups identified for modification", modifiedGroupsCount, len(updatedCaptureGroups))
|
||||
matchLogger.Debug("%d of %d capture groups identified for modification", modifiedGroupsCount, len(updatedCaptureGroups))
|
||||
|
||||
for _, capture := range updatedCaptureGroups {
|
||||
if capture.Value == capture.Updated {
|
||||
|
||||
@@ -91,10 +91,10 @@ func SplitPattern(pattern string) (string, string) {
|
||||
static, remainingPattern := doublestar.SplitPattern(pattern)
|
||||
splitPatternLogger.Trace("After split: static=%q, pattern=%q", static, remainingPattern)
|
||||
|
||||
// Resolve the static part to handle ~ expansion and make it absolute
|
||||
// ResolvePath already normalizes to forward slashes
|
||||
static = ResolvePath(static)
|
||||
splitPatternLogger.Trace("Resolved static part: %q", static)
|
||||
// Normalize to forward slashes but DON'T resolve relative to CWD
|
||||
// Paths should already be resolved by the caller (AggregateGlobs, etc.)
|
||||
static = filepath.ToSlash(static)
|
||||
splitPatternLogger.Trace("Normalized static part: %q", static)
|
||||
|
||||
splitPatternLogger.Trace("Final static path: %q, Remaining pattern: %q", static, remainingPattern)
|
||||
return static, remainingPattern
|
||||
@@ -126,9 +126,22 @@ func AssociateFilesWithCommands(files []string, commands []ModifyCommand) (map[s
|
||||
for _, command := range commands {
|
||||
associateFilesLogger.Debug("Checking command %q for file %q", command.Name, file)
|
||||
for _, glob := range command.Files {
|
||||
// SplitPattern now handles tilde expansion and path resolution
|
||||
static, pattern := SplitPattern(glob)
|
||||
associateFilesLogger.Trace("Glob parts for %q → static=%q pattern=%q", glob, static, pattern)
|
||||
// Resolve glob relative to SourceDir if it's a relative path
|
||||
var resolvedGlob string
|
||||
if !filepath.IsAbs(glob) && command.SourceDir != "" {
|
||||
resolvedGlob = filepath.Join(command.SourceDir, glob)
|
||||
associateFilesLogger.Trace("Joined relative glob %q to %q using SourceDir %q", glob, resolvedGlob, command.SourceDir)
|
||||
} else {
|
||||
resolvedGlob = glob
|
||||
}
|
||||
|
||||
// Make absolute and normalize
|
||||
resolvedGlob = ResolvePath(resolvedGlob)
|
||||
associateFilesLogger.Trace("Final resolved glob: %q", resolvedGlob)
|
||||
|
||||
// SplitPattern just splits, doesn't resolve
|
||||
static, pattern := SplitPattern(resolvedGlob)
|
||||
associateFilesLogger.Trace("Glob parts for %q → static=%q pattern=%q", resolvedGlob, static, pattern)
|
||||
|
||||
// Use resolved file for matching (already normalized to forward slashes by ResolvePath)
|
||||
absFile := resolvedFile
|
||||
@@ -181,15 +194,23 @@ func AggregateGlobs(commands []ModifyCommand) map[string]struct{} {
|
||||
globs := make(map[string]struct{})
|
||||
for _, command := range commands {
|
||||
aggregateGlobsLogger.Debug("Processing command %q for glob patterns", command.Name)
|
||||
aggregateGlobsLogger.Trace("Command SourceDir: %q", command.SourceDir)
|
||||
for _, glob := range command.Files {
|
||||
// Split the glob into static and pattern parts, then resolve ONLY the static part
|
||||
static, pattern := SplitPattern(glob)
|
||||
// Reconstruct the glob with resolved static part
|
||||
resolvedGlob := static
|
||||
if pattern != "" {
|
||||
resolvedGlob += "/" + pattern
|
||||
// If the glob is relative and we have a SourceDir, resolve relative to SourceDir
|
||||
var resolvedGlob string
|
||||
if !filepath.IsAbs(glob) && command.SourceDir != "" {
|
||||
// Relative path - resolve relative to the TOML file's directory
|
||||
resolvedGlob = filepath.Join(command.SourceDir, glob)
|
||||
aggregateGlobsLogger.Trace("Joined relative glob %q to %q using SourceDir %q", glob, resolvedGlob, command.SourceDir)
|
||||
} else {
|
||||
// Absolute path or no SourceDir - use as-is
|
||||
resolvedGlob = glob
|
||||
}
|
||||
aggregateGlobsLogger.Trace("Adding glob: %q (resolved to %q) [static=%s, pattern=%s]", glob, resolvedGlob, static, pattern)
|
||||
|
||||
// Make absolute and normalize (ResolvePath handles both)
|
||||
resolvedGlob = ResolvePath(resolvedGlob)
|
||||
aggregateGlobsLogger.Trace("Final resolved glob: %q", resolvedGlob)
|
||||
|
||||
globs[resolvedGlob] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
303
utils/modifycommand_path_resolution_test.go
Normal file
303
utils/modifycommand_path_resolution_test.go
Normal file
@@ -0,0 +1,303 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAggregateGlobsWithSourceDir(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "aggregate-globs-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create a subdirectory structure
|
||||
subDir := filepath.Join(tmpDir, "subdir")
|
||||
err = os.MkdirAll(subDir, 0755)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Create test files
|
||||
testFile := filepath.Join(subDir, "test.xml")
|
||||
err = os.WriteFile(testFile, []byte("<test/>"), 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test1",
|
||||
Files: []string{"subdir/*.xml"},
|
||||
SourceDir: tmpDir,
|
||||
},
|
||||
{
|
||||
Name: "test2",
|
||||
Files: []string{"*.txt"},
|
||||
SourceDir: tmpDir,
|
||||
},
|
||||
}
|
||||
|
||||
globs := AggregateGlobs(commands)
|
||||
|
||||
// Both should be resolved relative to tmpDir
|
||||
expectedSubdir := ResolvePath(filepath.Join(tmpDir, "subdir/*.xml"))
|
||||
expectedTxt := ResolvePath(filepath.Join(tmpDir, "*.txt"))
|
||||
|
||||
assert.Contains(t, globs, expectedSubdir, "Should contain resolved subdir glob")
|
||||
assert.Contains(t, globs, expectedTxt, "Should contain resolved txt glob")
|
||||
assert.Len(t, globs, 2, "Should have 2 unique globs")
|
||||
}
|
||||
|
||||
func TestAggregateGlobsWithAbsolutePaths(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "aggregate-globs-abs-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
absPath := ResolvePath(tmpDir)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test1",
|
||||
Files: []string{absPath + "/*.xml"},
|
||||
SourceDir: tmpDir, // SourceDir should be ignored for absolute paths
|
||||
},
|
||||
}
|
||||
|
||||
globs := AggregateGlobs(commands)
|
||||
|
||||
// Absolute path should be used as-is (after ResolvePath normalization)
|
||||
expected := ResolvePath(absPath + "/*.xml")
|
||||
assert.Contains(t, globs, expected, "Should contain absolute path glob")
|
||||
assert.Len(t, globs, 1, "Should have 1 glob")
|
||||
}
|
||||
|
||||
func TestAggregateGlobsWithoutSourceDir(t *testing.T) {
|
||||
cwd, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test1",
|
||||
Files: []string{"*.xml"},
|
||||
SourceDir: "", // No SourceDir
|
||||
},
|
||||
}
|
||||
|
||||
globs := AggregateGlobs(commands)
|
||||
|
||||
// Without SourceDir, should resolve relative to CWD
|
||||
expected := ResolvePath(filepath.Join(cwd, "*.xml"))
|
||||
assert.Contains(t, globs, expected, "Should resolve relative to CWD when SourceDir is empty")
|
||||
}
|
||||
|
||||
func TestAggregateGlobsConsistentRegardlessOfCWD(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "aggregate-globs-cwd-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create test structure
|
||||
testFile := filepath.Join(tmpDir, "test.xml")
|
||||
err = os.WriteFile(testFile, []byte("<test/>"), 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test",
|
||||
Files: []string{"test.xml"},
|
||||
SourceDir: tmpDir,
|
||||
},
|
||||
}
|
||||
|
||||
// Get original CWD
|
||||
originalCwd, err := os.Getwd()
|
||||
assert.NoError(t, err)
|
||||
defer os.Chdir(originalCwd)
|
||||
|
||||
// Test from original directory
|
||||
globs1 := AggregateGlobs(commands)
|
||||
expected1 := ResolvePath(filepath.Join(tmpDir, "test.xml"))
|
||||
|
||||
// Change to tmpDir
|
||||
err = os.Chdir(tmpDir)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Test from tmpDir - should produce same result
|
||||
globs2 := AggregateGlobs(commands)
|
||||
expected2 := ResolvePath(filepath.Join(tmpDir, "test.xml"))
|
||||
|
||||
// Both should resolve to the same absolute path
|
||||
assert.Equal(t, expected1, expected2, "Paths should be identical regardless of CWD")
|
||||
assert.Contains(t, globs1, expected1, "First run should contain expected path")
|
||||
assert.Contains(t, globs2, expected2, "Second run should contain expected path")
|
||||
assert.Equal(t, globs1, globs2, "Globs should be identical regardless of CWD")
|
||||
}
|
||||
|
||||
func TestAssociateFilesWithCommandsSourceDir(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "associate-files-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
// Create test structure
|
||||
subDir := filepath.Join(tmpDir, "data")
|
||||
err = os.MkdirAll(subDir, 0755)
|
||||
assert.NoError(t, err)
|
||||
|
||||
testFile := filepath.Join(subDir, "test.xml")
|
||||
err = os.WriteFile(testFile, []byte("<test/>"), 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test",
|
||||
Regex: "pattern",
|
||||
Lua: "expr",
|
||||
Files: []string{"data/test.xml"},
|
||||
SourceDir: tmpDir,
|
||||
},
|
||||
}
|
||||
|
||||
files := []string{testFile}
|
||||
associations, err := AssociateFilesWithCommands(files, commands)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// File should be associated with command
|
||||
assert.Contains(t, associations, testFile, "File should be in associations")
|
||||
association := associations[testFile]
|
||||
assert.Len(t, association.Commands, 1, "Should have 1 command")
|
||||
assert.Equal(t, "test", association.Commands[0].Name, "Command name should match")
|
||||
}
|
||||
|
||||
func TestAssociateFilesWithCommandsAbsolutePath(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "associate-files-abs-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
testFile := filepath.Join(tmpDir, "test.xml")
|
||||
err = os.WriteFile(testFile, []byte("<test/>"), 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
absPath := ResolvePath(testFile)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test",
|
||||
Regex: "pattern",
|
||||
Lua: "expr",
|
||||
Files: []string{absPath},
|
||||
SourceDir: tmpDir, // Should be ignored for absolute paths
|
||||
},
|
||||
}
|
||||
|
||||
files := []string{testFile}
|
||||
associations, err := AssociateFilesWithCommands(files, commands)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// File should be associated
|
||||
assert.Contains(t, associations, testFile, "File should be in associations")
|
||||
association := associations[testFile]
|
||||
assert.Len(t, association.Commands, 1, "Should have 1 command")
|
||||
}
|
||||
|
||||
func TestAssociateFilesWithCommandsNoSourceDir(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "associate-files-no-sourcedir-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
testFile := filepath.Join(tmpDir, "test.xml")
|
||||
err = os.WriteFile(testFile, []byte("<test/>"), 0644)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// Use absolute path since we have no SourceDir
|
||||
absPath := ResolvePath(testFile)
|
||||
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test",
|
||||
Regex: "pattern",
|
||||
Lua: "expr",
|
||||
Files: []string{absPath},
|
||||
SourceDir: "", // No SourceDir
|
||||
},
|
||||
}
|
||||
|
||||
files := []string{testFile}
|
||||
associations, err := AssociateFilesWithCommands(files, commands)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// File should be associated
|
||||
assert.Contains(t, associations, testFile, "File should be in associations")
|
||||
association := associations[testFile]
|
||||
assert.Len(t, association.Commands, 1, "Should have 1 command")
|
||||
}
|
||||
|
||||
func TestSourceDirConditionCoverage(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "sourcedir-condition-test-*")
|
||||
assert.NoError(t, err)
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
glob string
|
||||
sourceDir string
|
||||
shouldResolve bool
|
||||
}{
|
||||
{
|
||||
name: "Relative path with SourceDir",
|
||||
glob: "test.xml",
|
||||
sourceDir: tmpDir,
|
||||
shouldResolve: true,
|
||||
},
|
||||
{
|
||||
name: "Absolute path with SourceDir",
|
||||
glob: ResolvePath(filepath.Join(tmpDir, "test.xml")),
|
||||
sourceDir: tmpDir,
|
||||
shouldResolve: false, // Should use absolute path as-is
|
||||
},
|
||||
{
|
||||
name: "Relative path without SourceDir",
|
||||
glob: "test.xml",
|
||||
sourceDir: "",
|
||||
shouldResolve: false, // Should resolve relative to CWD
|
||||
},
|
||||
{
|
||||
name: "Absolute path without SourceDir",
|
||||
glob: ResolvePath(filepath.Join(tmpDir, "test.xml")),
|
||||
sourceDir: "",
|
||||
shouldResolve: false, // Should use absolute path as-is
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
commands := []ModifyCommand{
|
||||
{
|
||||
Name: "test",
|
||||
Files: []string{tt.glob},
|
||||
SourceDir: tt.sourceDir,
|
||||
},
|
||||
}
|
||||
|
||||
globs := AggregateGlobs(commands)
|
||||
assert.Len(t, globs, 1, "Should have 1 glob")
|
||||
|
||||
var resolvedGlob string
|
||||
for g := range globs {
|
||||
resolvedGlob = g
|
||||
}
|
||||
|
||||
if tt.shouldResolve {
|
||||
// Should be resolved relative to SourceDir
|
||||
expected := ResolvePath(filepath.Join(tt.sourceDir, tt.glob))
|
||||
assert.Equal(t, expected, resolvedGlob, "Should resolve relative to SourceDir")
|
||||
} else if filepath.IsAbs(tt.glob) {
|
||||
// Absolute path should be normalized but not changed
|
||||
expected := ResolvePath(tt.glob)
|
||||
assert.Equal(t, expected, resolvedGlob, "Absolute path should be normalized")
|
||||
} else {
|
||||
// Relative path without SourceDir should resolve to CWD
|
||||
cwd, _ := os.Getwd()
|
||||
expected := ResolvePath(filepath.Join(cwd, tt.glob))
|
||||
assert.Equal(t, expected, resolvedGlob, "Should resolve relative to CWD")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user