Coverage for archdocs/features/kubernetes/manifest_files.py: 100%
38 statements
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-03 00:00 +0000
« prev ^ index » next coverage.py v7.15.3, created at 2026-08-03 00:00 +0000
1import pathlib
2import re as py_re
3import typing
5from archdocs import settings
6from archdocs.features.kubernetes import const
9_API_VERSION_PATTERN: typing.Final = py_re.compile(r"^apiVersion:", flags=settings.TYPICAL_RE_FLAGS)
10_KIND_PATTERN: typing.Final = py_re.compile(r"^kind:", flags=settings.TYPICAL_RE_FLAGS)
13def _is_manifest_file(one_file_path: pathlib.Path, /) -> bool:
14 if one_file_path.stem == const.VALUES_FILE_STEM:
15 return True
16 file_source: typing.Final = one_file_path.read_text(errors="ignore")
17 return bool(_API_VERSION_PATTERN.search(file_source) and _KIND_PATTERN.search(file_source))
20def _find_manifest_files(search_dir: pathlib.Path, /) -> list[pathlib.Path]:
21 all_found_files: typing.Final = (
22 one_found_file
23 for one_file_suffix in const.MANIFEST_FILE_SUFFIXES
24 for one_found_file in search_dir.rglob(f"*{one_file_suffix}")
25 if settings.SKIPPED_DIR_NAMES.isdisjoint(one_found_file.relative_to(search_dir).parts)
26 )
27 return sorted(
28 (one_found_file for one_found_file in all_found_files if _is_manifest_file(one_found_file)),
29 key=lambda one_found_file: (len(one_found_file.parts), one_found_file),
30 )
33def _iter_search_roots(root_path: pathlib.Path, /) -> typing.Iterator[pathlib.Path]:
34 # Charts live next to the sources at least as often as inside them, so the search climbs a
35 # couple of levels up — but never out of the repository the sources belong to.
36 for one_search_root in (root_path, *list(root_path.parents)[: const.PARENT_SEARCH_DEPTH]):
37 yield one_search_root
38 if (one_search_root / const.REPOSITORY_MARKER_NAME).exists():
39 return
42def _find_manifest_dir(search_dir: pathlib.Path, /) -> pathlib.Path | None:
43 all_manifest_files: typing.Final = _find_manifest_files(search_dir)
44 if not all_manifest_files:
45 return None
46 closest_manifest_dir: typing.Final = all_manifest_files[0].parent
47 if closest_manifest_dir.name == const.TEMPLATES_DIR_NAME:
48 return closest_manifest_dir.parent
49 return closest_manifest_dir
52def _resolve_manifest_dir(root_path: pathlib.Path, configured_dir: str | pathlib.Path | None, /) -> pathlib.Path | None:
53 if configured_dir is None:
54 return next(
55 (
56 one_found_dir
57 for one_found_dir in map(_find_manifest_dir, _iter_search_roots(root_path))
58 if one_found_dir is not None
59 ),
60 None,
61 )
62 all_candidate_dirs: typing.Final = (
63 (one_search_root / configured_dir).resolve() for one_search_root in _iter_search_roots(root_path)
64 )
65 return next((one_candidate_dir for one_candidate_dir in all_candidate_dirs if one_candidate_dir.is_dir()), None)
68def read_kubernetes_manifests(root_path: pathlib.Path, configured_dir: str | pathlib.Path | None, /) -> str:
69 manifest_dir: typing.Final = _resolve_manifest_dir(root_path, configured_dir)
70 if manifest_dir is None:
71 return ""
72 return "".join(
73 f"{one_manifest_file.read_text(errors='ignore')}\n" for one_manifest_file in _find_manifest_files(manifest_dir)
74 )