from dataclasses import dataclass from pathlib import Path from typing import List, Optional @dataclass class ConfigFile: path: Path format: str name: str class ConfigDiscovery: PATTERNS = { "json": [ "package.json", "tsconfig*.json", ".eslintrc*.json", ".prettierrc*.json", ".babelrc*.json", "webpack.config.js", "rollup.config.js", "nest-cli.json", "angular.json", "pyproject.toml", "poetry.toml", "mypy.ini", "pytest.ini", "setup.cfg", ".coveragerc", "tox.ini", ], "yaml": [ ".gitlab-ci.yml", ".github/**/*.yml", ".github/**/*.yaml", "docker-compose.yml", "*.k8s.yaml", "*.k8s.yml", ".ansible.yml", ".ansible/**/*.yml", ], "toml": [ "pyproject.toml", "poetry.toml", "Cargo.toml", "PDM.toml", ], } def __init__(self, max_depth: int = 3): self.max_depth = max_depth def discover(self, path: Path) -> List[ConfigFile]: config_files = [] for format_patterns in self.PATTERNS.values(): for pattern in format_patterns: matches = self._find_matches(path, pattern) for match in matches: format_type = self._detect_format(match) if format_type: config_files.append(ConfigFile( path=match, format=format_type, name=match.name )) return config_files def _find_matches(self, base_path: Path, pattern: str) -> List[Path]: matches = [] if "**" in pattern: parts = pattern.split("**") if len(parts) > 1: search_pattern = parts[-1].lstrip("/") else: search_pattern = "*" for p in base_path.rglob(search_pattern): if p.is_file() and self._matches_pattern(p.name, search_pattern): matches.append(p) elif "*" in pattern: for p in base_path.rglob(pattern): if p.is_file(): matches.append(p) else: full_path = base_path / pattern if full_path.exists(): matches.append(full_path) return list(set(matches)) def _matches_pattern(self, filename: str, pattern: str) -> bool: from fnmatch import fnmatch return fnmatch(filename, pattern) def _detect_format(self, path: Path) -> Optional[str]: suffix = path.suffix.lower() name = path.name.lower() if suffix == ".json" or "eslintrc" in name or "babelrc" in name: return "json" elif suffix in (".yaml", ".yml"): return "yaml" elif suffix == ".toml" or name == "pyproject.toml": return "toml" if path.exists(): try: content = path.read_text() if content.strip().startswith("{") or content.strip().startswith("["): return "json" elif content.strip().startswith("---") or ":" in content.split("\n")[0]: return "yaml" except Exception: pass return None