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

1import pathlib 

2import re as py_re 

3import typing 

4 

5from archdocs import settings 

6from archdocs.features.kubernetes import const 

7 

8 

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) 

11 

12 

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)) 

18 

19 

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 ) 

31 

32 

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 

40 

41 

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 

50 

51 

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) 

66 

67 

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 )