Coverage for src / rhiza / bundle_resolver.py: 100%

26 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-12 20:13 +0000

1"""Bundle resolution logic for template configuration. 

2 

3This module provides functions to load and resolve bundle configurations 

4from the template repository's template-bundles.yml file. 

5""" 

6 

7from pathlib import Path 

8 

9from rhiza.models import RhizaBundles, RhizaTemplate 

10 

11 

12def load_bundles_from_clone(tmp_dir: Path) -> RhizaBundles | None: 

13 """Load .rhiza/template-bundles.yml from cloned template repo. 

14 

15 Args: 

16 tmp_dir: Path to the cloned template repository. 

17 

18 Returns: 

19 RhizaBundles if template-bundles.yml exists, None otherwise. 

20 

21 Raises: 

22 yaml.YAMLError: If template-bundles.yml is malformed. 

23 ValueError: If template-bundles.yml is invalid. 

24 """ 

25 bundles_file = tmp_dir / ".rhiza" / "template-bundles.yml" 

26 if not bundles_file.exists(): 

27 return None 

28 return RhizaBundles.from_yaml(bundles_file) 

29 

30 

31def resolve_include_paths( 

32 template: RhizaTemplate, 

33 bundles_config: RhizaBundles | None, 

34) -> list[str]: 

35 """Resolve template configuration to file paths. 

36 

37 Supports: 

38 - Template-based mode (templates field) 

39 - Path-based mode (include field) 

40 - Hybrid mode (both templates and include) 

41 

42 Args: 

43 template: The template configuration. 

44 bundles_config: The loaded bundles configuration, or None if not available. 

45 

46 Returns: 

47 List of file paths to materialize. 

48 

49 Raises: 

50 ValueError: If configuration is invalid or bundles.yml is missing. 

51 """ 

52 paths = [] 

53 

54 # Resolve templates to paths if specified 

55 if template.templates: 

56 if not bundles_config: 

57 msg = "Template uses templates but template-bundles.yml not found in template repository" 

58 raise ValueError(msg) 

59 paths.extend(bundles_config.resolve_to_paths(template.templates)) 

60 

61 # Add include paths if specified 

62 if template.include: 

63 paths.extend(template.include) 

64 

65 # At least one must be specified 

66 if not paths: 

67 msg = "Template configuration must specify either 'templates' or 'include'" 

68 raise ValueError(msg) 

69 

70 # Deduplicate while preserving order 

71 seen = set() 

72 deduplicated = [] 

73 for path in paths: 

74 if path not in seen: 

75 deduplicated.append(path) 

76 seen.add(path) 

77 

78 return deduplicated