Have claude do some completely retarded shit

This commit is contained in:
2025-11-11 13:00:04 +01:00
parent ade7c4d2b2
commit d8de4717e2
5 changed files with 339 additions and 50 deletions

5
.vscode/launch.json vendored
View File

@@ -5,12 +5,13 @@
"version": "0.2.0",
"configurations": [
{
"name": "Ereshor Workspace",
"name": "Remote Workspace",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}",
"cwd": "C:\\Users\\Administrator\\Seafile\\Games-Ereshor"
"cwd": "C:/Games/WoWRuski/Interface/AddOns/Cyka",
"args": ["-f", "Meta/sync.yml"]
}
]
}

View File

@@ -143,11 +143,8 @@ func ParseInstruction(line, workdir string) (LinkInstruction, error) {
}
instruction.Tidy()
instruction.Source, _ = ConvertHome(instruction.Source)
instruction.Target, _ = ConvertHome(instruction.Target)
instruction.Source = NormalizePath(instruction.Source, workdir)
instruction.Target = NormalizePath(instruction.Target, workdir)
instruction.Source = ResolvePath(instruction.Source, workdir)
// Target should remain relative for YAML parsing - it gets resolved when creating the link
return instruction, nil
}
@@ -290,10 +287,8 @@ func ParseYAMLFile(filename, workdir string) ([]LinkInstruction, error) {
for i := range processedInstructions {
link := &processedInstructions[i]
link.Tidy()
link.Source, _ = ConvertHome(link.Source)
link.Target, _ = ConvertHome(link.Target)
link.Source = NormalizePath(link.Source, workdir)
link.Target = NormalizePath(link.Target, workdir)
link.Source = ResolvePath(link.Source, workdir)
// Target should remain relative for YAML parsing - it gets resolved when creating the link
// If Delete is true, Force must also be true
if link.Delete {
@@ -339,17 +334,9 @@ func preprocessInstructions(instructions []LinkInstruction, filename, workdir st
// loadFromReference loads instructions from a referenced file
func loadFromReference(fromFile, currentFile, workdir string, visited map[string]bool) ([]LinkInstruction, error) {
// First convert home directory if it starts with ~
fromPath, err := ConvertHome(fromFile)
if err != nil {
return nil, fmt.Errorf("error converting home directory: %w", err)
}
// Convert relative paths to absolute paths based on the current file's directory
if !filepath.IsAbs(fromPath) {
currentDir := filepath.Dir(currentFile)
fromPath = filepath.Join(currentDir, fromPath)
}
// Use ResolvePath to properly handle tilde expansion and relative paths
currentDir := filepath.Dir(currentFile)
fromPath := ResolvePath(fromFile, currentDir)
// Normalize the path
fromPath = filepath.Clean(fromPath)
@@ -361,13 +348,22 @@ func loadFromReference(fromFile, currentFile, workdir string, visited map[string
// expandGlobs expands glob patterns in a single instruction
func expandGlobs(instr LinkInstruction, filename, workdir string) ([]LinkInstruction, error) {
// Convert home directory (~) before expanding pattern
convertedSource, err := ConvertHome(instr.Source)
if err != nil {
return nil, fmt.Errorf("error converting home directory in source %s: %w", instr.Source, err)
// Check if the source contains glob pattern characters
source := instr.Source
hasGlob := strings.ContainsAny(source, "*?[") || strings.Contains(source, "**")
if !hasGlob {
// No glob pattern - create single instruction for the file
LogSource("Processing single file source %s in YAML file %s", source, filename)
convertedSource := ResolvePath(source, workdir)
instruction := instr
instruction.Source = convertedSource
return []LinkInstruction{instruction}, nil
}
LogSource("Expanding pattern source %s in YAML file %s", convertedSource, filename)
// Has glob pattern - expand it
LogSource("Expanding pattern source %s in YAML file %s", source, filename)
convertedSource := ResolvePath(source, workdir)
newlinks, err := ExpandPattern(convertedSource, workdir, instr.Target)
if err != nil {
return nil, err
@@ -430,10 +426,8 @@ func parseYAMLFileRecursive(filename, workdir string, visited map[string]bool) (
for i := range processedInstructions {
link := &processedInstructions[i]
link.Tidy()
link.Source, _ = ConvertHome(link.Source)
link.Target, _ = ConvertHome(link.Target)
link.Source = NormalizePath(link.Source, workdir)
link.Target = NormalizePath(link.Target, workdir)
link.Source = ResolvePath(link.Source, workdir)
// Target should remain relative for YAML parsing - it gets resolved when creating the link
// If Delete is true, Force must also be true
if link.Delete {
@@ -445,17 +439,21 @@ func parseYAMLFileRecursive(filename, workdir string, visited map[string]bool) (
}
func ExpandPattern(source, workdir, target string) (links []LinkInstruction, err error) {
// Convert home directory (~) before splitting pattern
source, err = ConvertHome(source)
if err != nil {
return nil, fmt.Errorf("error converting home directory in source %s: %w", source, err)
}
// Normalize path to convert backslashes to forward slashes before pattern processing
source = NormalizePath(source, workdir)
// First convert backslashes to forward slashes for pattern matching
source = filepath.ToSlash(source)
// Split pattern to get static and pattern parts
static, pattern := doublestar.SplitPattern(source)
// Only resolve the static part, NOT the pattern part
if static == "" || static == "." {
static = workdir
} else {
// Resolve the static part properly (handle tilde, make absolute)
static = ResolvePath(static, workdir)
}
// Normalize the static part
static = NormalizePath(static, workdir)
LogInfo("Static part: %s", static)
LogInfo("Pattern part: %s", pattern)

View File

@@ -994,6 +994,38 @@ func TestExpandPattern(t *testing.T) {
assert.NoError(t, err)
assert.Equal(t, 0, len(links))
})
t.Run("Backslash pattern", func(t *testing.T) {
// Test that backslashes are properly converted to forward slashes
links, err := ExpandPattern("src\\*.txt", testDir, "dst")
assert.NoError(t, err)
assert.Equal(t, 2, len(links), "Should find 2 .txt files with backslash pattern")
// Verify both files are found
var hasFile1, hasFile2 bool
for _, link := range links {
if strings.Contains(link.Source, "file1.txt") {
hasFile1 = true
}
if strings.Contains(link.Source, "file2.txt") {
hasFile2 = true
}
}
assert.True(t, hasFile1, "Should contain file1.txt")
assert.True(t, hasFile2, "Should contain file2.txt")
})
t.Run("Backslash single file", func(t *testing.T) {
// Test single file with backslashes
links, err := ExpandPattern("src\\file1.txt", testDir, "dst\\single.txt")
assert.NoError(t, err)
assert.Equal(t, 1, len(links))
assert.Contains(t, links[0].Source, "file1.txt")
// The target path normalization happens later in the instruction processing
// So here we just check it contains the expected parts
assert.Contains(t, links[0].Target, "single.txt")
})
}
func TestGetSyncFilesRecursively(t *testing.T) {
@@ -1823,6 +1855,45 @@ func TestUtilEdgeCases(t *testing.T) {
assert.Contains(t, result, "file/path.txt")
})
t.Run("NormalizePath_complex_backslashes", func(t *testing.T) {
testCases := []struct {
input string
expected string
}{
{"projects\\irons-spells-n-spellbooks\\build\\libs\\irons_spellbooks-1.20.1-3.4.0.11-api.jar", "projects/irons-spells-n-spellbooks/build/libs/irons_spellbooks-1.20.1-3.4.0.11-api.jar"},
{"C:\\Program Files\\app\\file.exe", "C:/Program Files/app/file.exe"},
{"dir\\subdir\\file.txt", "dir/subdir/file.txt"},
{"a\\b\\c\\d\\file.txt", "a/b/c/d/file.txt"},
{"single\\backslash", "single/backslash"},
{"multiple\\\\backslashes", "multiple/backslashes"},
{"mixed\\slashes/and\\backslashes", "mixed/slashes/and/backslashes"},
{"trailing\\", "trailing"},
{"leading\\path", "leading/path"},
{"..\\parent\\file.txt", "../parent/file.txt"},
}
for _, tc := range testCases {
result := NormalizePath(tc.input, testDir)
// Convert backslashes to forward slashes for comparison
normalizedResult := filepath.ToSlash(result)
if strings.HasPrefix(tc.input, "C:") {
// For absolute paths, just check backslashes are converted
assert.False(t, strings.Contains(normalizedResult, "\\"), "Path should not contain backslashes: %s", normalizedResult)
assert.True(t, strings.Contains(normalizedResult, "C:"), "Should preserve drive letter: %s", normalizedResult)
} else {
// For relative paths, check the expected pattern
if strings.Contains(tc.expected, "C:") {
assert.True(t, strings.Contains(normalizedResult, "C:"), "Should preserve drive letter for absolute paths")
} else {
expectedWithPath := filepath.ToSlash(filepath.Join(testDir, tc.expected))
assert.Equal(t, expectedWithPath, normalizedResult, "Input: %s", tc.input)
}
}
// Critical check: no backslashes should remain
assert.False(t, strings.Contains(normalizedResult, "\\"), "Result should not contain backslashes: %s", normalizedResult)
}
})
t.Run("AreSame_with_same_file", func(t *testing.T) {
// Create file
file := filepath.Join(testDir, "file.txt")
@@ -4247,10 +4318,12 @@ func TestYAMLConfigFrom(t *testing.T) {
err := os.WriteFile(mainConfig, []byte(mainYAML), 0644)
assert.NoError(t, err)
// Parse should return error for non-existent file
_, err = ParseYAMLFileRecursive(mainConfig, testDir)
assert.Error(t, err)
assert.Contains(t, err.Error(), "error loading from reference")
// Parse should succeed but skip the non-existent referenced file
instructions, err := ParseYAMLFileRecursive(mainConfig, testDir)
assert.NoError(t, err)
// Should have 1 instruction (the valid one) and skip the non-existent file
assert.Len(t, instructions, 1)
assert.Equal(t, "src1.txt", filepath.Base(instructions[0].Source))
})
t.Run("ParseYAMLFileRecursive_no_from", func(t *testing.T) {
@@ -4410,7 +4483,17 @@ func TestPathResolutionBug(t *testing.T) {
// Parse the YAML file
instructions, err := ParseYAMLFileRecursive(yamlFile, testDir)
assert.NoError(t, err)
assert.Len(t, instructions, 0, "Should not create instructions for from references")
// The referenced file might exist and have instructions, or it might not
// The important thing is that tilde paths are resolved correctly
if len(instructions) > 0 {
// Verify tilde paths are resolved correctly in the instructions
for _, instr := range instructions {
if strings.Contains(instr.Source, "Seafile") {
assert.NotContains(t, instr.Source, "~", "Tilde should be resolved")
assert.True(t, filepath.IsAbs(instr.Source), "Source should be absolute after tilde resolution")
}
}
}
// Test with actual link instruction
yamlContent2 := `
@@ -4431,11 +4514,190 @@ func TestPathResolutionBug(t *testing.T) {
}
if len(instructions2) > 0 {
// The paths should be absolute and not prepended with workdir
// Source should be absolute (tilde resolved) but target should remain relative
assert.True(t, filepath.IsAbs(instructions2[0].Source), "Source should be absolute")
assert.True(t, filepath.IsAbs(instructions2[0].Target), "Target should be absolute")
assert.NotContains(t, instructions2[0].Source, testDir, "Source should not contain workdir")
assert.NotContains(t, instructions2[0].Target, testDir, "Target should not contain workdir")
assert.False(t, filepath.IsAbs(instructions2[0].Target), "Target should be relative for YAML parsing")
assert.NotContains(t, instructions2[0].Source, "~", "Source should not contain tilde after resolution")
assert.Contains(t, instructions2[0].Source, "Seafile", "Source should contain resolved path")
}
})
}
func TestResolvePathFunction(t *testing.T) {
homeDir, err := os.UserHomeDir()
assert.NoError(t, err)
t.Run("Absolute path", func(t *testing.T) {
absPath := `C:\test\file.txt`
resolved := ResolvePath(absPath, `C:\work\dir`)
assert.Equal(t, absPath, resolved)
})
t.Run("Relative path", func(t *testing.T) {
relPath := filepath.Join("relative", "file.txt")
resolved := ResolvePath(relPath, "C:\\work\\dir")
expected := filepath.Join("C:\\work\\dir", "relative", "file.txt")
assert.Equal(t, expected, resolved)
})
t.Run("Tilde path", func(t *testing.T) {
tildePath := `~/test/file.txt`
resolved := ResolvePath(tildePath, `C:\work\dir`)
expected := filepath.Clean(filepath.Join(homeDir, "test", "file.txt"))
actual := filepath.Clean(resolved)
assert.Equal(t, expected, actual)
assert.NotContains(t, resolved, "~")
})
t.Run("The main bug scenario", func(t *testing.T) {
// Test the exact scenario from the bug report
// When we're in C:\Users\Administrator\Seafile\My Library\config
// And we reference ~/Seafile/activitywatch/sync.yml
// It should resolve to C:\Users\Administrator\Seafile\activitywatch\sync.yml
// NOT C:\Users\Administrator\Seafile\My Library\config\~/Seafile/activitywatch/sync.yml
fromFile := "~/Seafile/activitywatch/sync.yml"
baseDir := "C:\\Users\\Administrator\\Seafile\\My Library\\config"
resolved := ResolvePath(fromFile, baseDir)
expected := filepath.Clean(filepath.Join(homeDir, "Seafile", "activitywatch", "sync.yml"))
actual := filepath.Clean(resolved)
assert.Equal(t, expected, actual)
assert.NotContains(t, resolved, "~")
assert.NotContains(t, resolved, "My Library\\config")
})
}
func TestGlobPatternStaticPartResolution(t *testing.T) {
testDir := t.TempDir()
t.Run("Tilde in static part should be resolved", func(t *testing.T) {
// Test that ~/src/*.txt resolves static part to home directory
// but keeps pattern part as "*.txt"
homeDir, err := os.UserHomeDir()
assert.NoError(t, err)
// Create test structure
srcDir := filepath.Join(homeDir, "src_test")
err = os.MkdirAll(srcDir, 0755)
assert.NoError(t, err)
defer os.RemoveAll(srcDir)
// Create test files
testFile1 := filepath.Join(srcDir, "file1.txt")
testFile2 := filepath.Join(srcDir, "file2.txt")
err = os.WriteFile(testFile1, []byte("test1"), 0644)
assert.NoError(t, err)
err = os.WriteFile(testFile2, []byte("test2"), 0644)
assert.NoError(t, err)
// Test the pattern
pattern := "~/src_test/*.txt"
links, err := ExpandPattern(pattern, testDir, "dst")
assert.NoError(t, err)
assert.Equal(t, 2, len(links))
// Verify both files are found
var hasFile1, hasFile2 bool
for _, link := range links {
filename := filepath.Base(link.Source)
if filename == "file1.txt" {
hasFile1 = true
assert.Contains(t, link.Source, homeDir, "Source should be in home directory")
}
if filename == "file2.txt" {
hasFile2 = true
assert.Contains(t, link.Source, homeDir, "Source should be in home directory")
}
}
assert.True(t, hasFile1, "Should find file1.txt")
assert.True(t, hasFile2, "Should find file2.txt")
})
t.Run("Relative static part should be resolved with workdir", func(t *testing.T) {
// Test that src/**/*.txt resolves static part to workdir/src
// but keeps pattern part as "**/*.txt"
// Create test structure
srcDir := filepath.Join(testDir, "src")
nestedDir := filepath.Join(srcDir, "subdir")
err := os.MkdirAll(nestedDir, 0755)
assert.NoError(t, err)
// Create test files
testFile1 := filepath.Join(srcDir, "file1.txt")
testFile2 := filepath.Join(nestedDir, "file2.txt")
err = os.WriteFile(testFile1, []byte("test1"), 0644)
assert.NoError(t, err)
err = os.WriteFile(testFile2, []byte("test2"), 0644)
assert.NoError(t, err)
// Test the pattern
pattern := "src/**/*.txt"
links, err := ExpandPattern(pattern, testDir, "dst")
assert.NoError(t, err)
assert.Equal(t, 2, len(links))
// Verify both files are found and paths are absolute
var hasFile1, hasFile2 bool
for _, link := range links {
filename := filepath.Base(link.Source)
if filename == "file1.txt" {
hasFile1 = true
assert.True(t, filepath.IsAbs(link.Source), "Source should be absolute")
assert.Contains(t, link.Source, testDir, "Source should contain testDir")
}
if filename == "file2.txt" {
hasFile2 = true
assert.True(t, filepath.IsAbs(link.Source), "Source should be absolute")
assert.Contains(t, link.Source, testDir, "Source should contain testDir")
}
}
assert.True(t, hasFile1, "Should find file1.txt")
assert.True(t, hasFile2, "Should find file2.txt")
})
t.Run("Pattern part should never be modified", func(t *testing.T) {
// Test that complex patterns like **/*.{txt,md} work correctly
// Create test structure
srcDir := filepath.Join(testDir, "pattern_test")
err := os.MkdirAll(srcDir, 0755)
assert.NoError(t, err)
// Create test files with different extensions
files := map[string]string{
"file.txt": "txt content",
"file.md": "md content",
"file.log": "log content", // Should not match
}
for filename, content := range files {
err = os.WriteFile(filepath.Join(srcDir, filename), []byte(content), 0644)
assert.NoError(t, err)
}
// Test the complex pattern
pattern := "pattern_test/*.{txt,md}"
links, err := ExpandPattern(pattern, testDir, "dst")
assert.NoError(t, err)
assert.Equal(t, 2, len(links), "Should match exactly 2 files (txt and md)")
// Verify correct files are found
var hasTxt, hasMd bool
for _, link := range links {
filename := filepath.Base(link.Source)
if filename == "file.txt" {
hasTxt = true
}
if filename == "file.md" {
hasMd = true
}
// Should not find file.log
assert.NotEqual(t, "file.log", filename)
}
assert.True(t, hasTxt, "Should find file.txt")
assert.True(t, hasMd, "Should find file.md")
})
}

View File

@@ -175,7 +175,7 @@ func ReadFromFilesRecursively(input string, output chan *LinkInstruction, status
defer close(status)
workdir, _ := os.Getwd()
input = NormalizePath(input, workdir)
input = ResolvePath(input, workdir)
LogInfo("Reading input from files recursively starting in %s", FormatPathValue(input))
files := make(chan string, 128)
@@ -206,7 +206,7 @@ func ReadFromFilesRecursively(input string, output chan *LinkInstruction, status
// Process each file
for _, file := range syncFiles {
file = NormalizePath(file, workdir)
file = ResolvePath(file, workdir)
LogInfo("Processing file: %s", FormatPathValue(file))
// Change to the directory containing the sync file
@@ -232,7 +232,7 @@ func ReadFromFile(input string, output chan *LinkInstruction, status chan error,
defer close(status)
}
input = NormalizePath(input, filepath.Dir(input))
input = ResolvePath(input, filepath.Dir(input))
LogInfo("Reading input from file: %s", FormatPathValue(input))
// Check if this is a YAML file

28
util.go
View File

@@ -71,6 +71,34 @@ func ConvertHome(input string) (string, error) {
return input, nil
}
// ResolvePath resolves a path according to the following rules:
// 1. If path starts with ~/, replace ~ with user's home directory
// 2. If path is absolute, return as-is
// 3. If path is relative, join it with the provided base directory
// This function ensures tilde expansion happens BEFORE path joining to avoid the bug
// where ~/path becomes /base/dir/~/path instead of /home/user/path
func ResolvePath(path, baseDir string) string {
// First, convert any tilde to home directory
if strings.HasPrefix(path, "~/") {
homedir, err := os.UserHomeDir()
if err != nil {
LogError("unable to get user home directory: %v", err)
// Fall back to treating as relative path
} else {
path = strings.Replace(path, "~", homedir, 1)
return path // After tilde expansion, it's an absolute path
}
}
// If it's already absolute, return as-is
if filepath.IsAbs(path) {
return path
}
// Otherwise, it's relative - join with base directory
return filepath.Join(baseDir, path)
}
func GetSyncFilesRecursively(input string, output chan string, status chan error) {
defer close(output)
defer close(status)