package toolingplan import ( "bufio" "fmt" "os" "path/filepath" "sort" "strings" toml "github.com/pelletier/go-toml" ) func repoManagedTools(repoRoot string) (map[string]struct{}, []SkipNote) { tools := make(map[string]struct{}) skips := make([]SkipNote, 0) if err := collectToolVersions(filepath.Join(repoRoot, ".tool-versions"), tools); err != nil { skips = append(skips, SkipNote{ Target: "repo mise declarations", Reason: fmt.Sprintf("could not read .tool-versions: %v", err), }) } if err := collectMiseToml(filepath.Join(repoRoot, ".mise.toml"), tools); err != nil { skips = append(skips, SkipNote{ Target: "repo mise declarations", Reason: fmt.Sprintf("could not parse .mise.toml: %v", err), }) } return tools, skips } func collectToolVersions(path string, tools map[string]struct{}) error { file, err := os.Open(path) if err != nil { if os.IsNotExist(err) { return nil } return err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) if line == "" || strings.HasPrefix(line, "#") { continue } fields := strings.Fields(line) if len(fields) == 0 { continue } tools[fields[0]] = struct{}{} } return scanner.Err() } func collectMiseToml(path string, tools map[string]struct{}) error { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil } return err } tree, err := toml.LoadBytes(data) if err != nil { return err } value := tree.Get("tools") if value == nil { return nil } switch typed := value.(type) { case *toml.Tree: for _, key := range typed.Keys() { tools[key] = struct{}{} } case map[string]interface{}: keys := make([]string, 0, len(typed)) for key := range typed { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { tools[key] = struct{}{} } } return nil }