diff --git a/instruction.go b/instruction.go index 81f7eca..fc0e1d9 100644 --- a/instruction.go +++ b/instruction.go @@ -278,7 +278,9 @@ func ParseYAMLFile(filename, workdir string) ([]LinkInstruction, error) { } // Preprocess instructions: expand globs and from references - processedInstructions, err := preprocessInstructions(instructions, filename, workdir) + // Create a new visited map for this file + visited := make(map[string]bool) + processedInstructions, err := preprocessInstructions(instructions, filename, workdir, visited) if err != nil { return nil, err } @@ -302,7 +304,7 @@ func ParseYAMLFile(filename, workdir string) ([]LinkInstruction, error) { } // preprocessInstructions handles glob expansion and from references -func preprocessInstructions(instructions []LinkInstruction, filename, workdir string) ([]LinkInstruction, error) { +func preprocessInstructions(instructions []LinkInstruction, filename, workdir string, visited map[string]bool) ([]LinkInstruction, error) { var result []LinkInstruction for _, instr := range instructions { @@ -312,7 +314,7 @@ func preprocessInstructions(instructions []LinkInstruction, filename, workdir st if instr.Target == "" { // This is a from reference - load the referenced file - fromInstructions, err := loadFromReference(instr.Source, filename, workdir) + fromInstructions, err := loadFromReference(instr.Source, filename, workdir, visited) if err != nil { return nil, fmt.Errorf("error loading from reference %s: %w", instr.Source, err) } @@ -331,7 +333,7 @@ func preprocessInstructions(instructions []LinkInstruction, filename, workdir st } // loadFromReference loads instructions from a referenced file -func loadFromReference(fromFile, currentFile, workdir string) ([]LinkInstruction, error) { +func loadFromReference(fromFile, currentFile, workdir string, visited map[string]bool) ([]LinkInstruction, error) { // Convert relative paths to absolute paths based on the current file's directory fromPath := fromFile if !filepath.IsAbs(fromPath) { @@ -342,9 +344,9 @@ func loadFromReference(fromFile, currentFile, workdir string) ([]LinkInstruction // Normalize the path fromPath = filepath.Clean(fromPath) - // Recursively parse the referenced file + // Recursively parse the referenced file with cycle detection fromWorkdir := filepath.Dir(fromPath) - return ParseYAMLFileRecursive(fromPath, fromWorkdir) + return parseYAMLFileRecursive(fromPath, fromWorkdir, visited) } // expandGlobs expands glob patterns in a single instruction @@ -389,8 +391,41 @@ func parseYAMLFileRecursive(filename, workdir string, visited map[string]bool) ( visited[normalizedFilename] = true defer delete(visited, normalizedFilename) - // Parse the current file using the new preprocessing approach - return ParseYAMLFile(filename, workdir) + // Parse the current file and preprocess it with cycle detection + data, err := os.ReadFile(filename) + if err != nil { + return nil, fmt.Errorf("error reading YAML file: %w", err) + } + + // Parse as a direct list of instructions + var instructions []LinkInstruction + err = yaml.Unmarshal(data, &instructions) + if err != nil { + return nil, fmt.Errorf("error parsing YAML: %w", err) + } + + // Preprocess instructions: expand globs and from references + processedInstructions, err := preprocessInstructions(instructions, filename, workdir, visited) + if err != nil { + return nil, err + } + + // Final processing: normalize paths and set defaults + 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) + + // If Delete is true, Force must also be true + if link.Delete { + link.Force = true + } + } + + return processedInstructions, nil } func ExpandPattern(source, workdir, target string) (links []LinkInstruction, err error) {