Skip to content

check_template_bundles

rhiza_hooks.check_template_bundles

Validate template-bundles.yml structure and consistency.

This script validates the template bundles configuration file to ensure: 1. Valid YAML syntax 2. Required fields are present 3. Bundle dependencies reference existing bundles 4. File paths follow expected patterns 5. Examples reference valid bundles

The script reads .rhiza/template.yml to find the template repository, then fetches template-bundles.yml from that remote repository.

Exit codes

0 - Validation passed 1 - Validation failed

find_repo_root()

Find the repository root directory.

Returns:

Type Description
Path

Path to the repository root

Source code in src/rhiza_hooks/check_template_bundles.py
def find_repo_root() -> Path:
    """Find the repository root directory.

    Returns:
        Path to the repository root
    """
    current = Path.cwd()
    while current != current.parent:
        if (current / ".git").exists():
            return current
        current = current.parent
    return Path.cwd()

main(argv=None)

Main entry point.

Source code in src/rhiza_hooks/check_template_bundles.py
def main(argv: list[str] | None = None) -> int:
    """Main entry point."""
    if isinstance(sys.stdout, io.TextIOWrapper):
        sys.stdout.reconfigure(encoding="utf-8", errors="replace")
    parser = argparse.ArgumentParser(description="Validate template-bundles.yml from remote template repository")
    parser.add_argument(
        "filenames",
        nargs="*",
        help="Filenames to check (should be .rhiza/template.yml)",
    )
    args = parser.parse_args(argv)

    # Get configuration path
    config_path = _get_config_path(args)

    # Load and validate configuration
    config, templates_set = _load_and_validate_config(config_path)
    if config is None or templates_set is None:
        return 0

    # Get template repository and branch
    template_repo = config.get("template-repository")
    template_branch = config.get("template-branch")

    if not template_repo or not template_branch:
        print(f"Missing template-repository or template-branch in {config_path}")
        return 1

    # Fetch and validate remote bundles
    data, _fetch_errors = _validate_remote_bundles(template_repo, template_branch, templates_set, config_path)
    if data is None:
        return 1

    # Validate templates
    bundles = data.get("bundles", {})
    errors = _validate_templates_in_bundles(templates_set, bundles, config_path)

    if errors:
        print("\n✗ Template bundles validation failed:")
        for error in errors:
            print(f"  - {error}")
        return 1

    print("✓ Template bundles validation passed!")
    return 0

validate_template_bundles(bundles_path, templates_to_check=None)

Validate template bundles configuration.

Parameters:

Name Type Description Default
bundles_path Path

Path to template-bundles.yml

required
templates_to_check set[str] | None

Optional set of template names to validate. If None, validate all.

None

Returns:

Type Description
tuple[bool, list[str]]

Tuple of (success, error_messages)

Source code in src/rhiza_hooks/check_template_bundles.py
def validate_template_bundles(bundles_path: Path, templates_to_check: set[str] | None = None) -> tuple[bool, list[str]]:
    """Validate template bundles configuration.

    Args:
        bundles_path: Path to template-bundles.yml
        templates_to_check: Optional set of template names to validate. If None, validate all.

    Returns:
        Tuple of (success, error_messages)
    """
    # Load YAML file
    success, data_or_errors = _load_yaml_file(bundles_path)
    if not success:
        # Type narrowing: when success is False, data_or_errors is list[str]
        return False, cast(list[str], data_or_errors)

    # Type narrowing: when success is True, data_or_errors is dict[Any, Any]
    data = cast(dict[Any, Any], data_or_errors)

    # Validate top-level fields
    errors = _validate_top_level_fields(data)
    if errors:
        return False, errors

    # Validate bundles section
    bundles = data.get("bundles", {})
    if not isinstance(bundles, dict):
        return False, ["'bundles' must be a dictionary"]

    bundle_names = cast(set[str], set(bundles.keys()))

    # If templates_to_check is specified, verify they exist
    if templates_to_check is not None:
        for template in templates_to_check:
            if template not in bundle_names:
                errors.append(f"Template '{template}' specified in .rhiza/template.yml not found in bundles")

    # Determine which bundles to validate
    bundles_to_validate = templates_to_check if templates_to_check is not None else bundle_names

    # Validate each bundle
    for bundle_name in bundles_to_validate:
        if bundle_name in bundles:
            bundle_config = bundles[bundle_name]
            errors.extend(_validate_bundle_structure(bundle_name, bundle_config, bundle_names))

    # Validate examples section (only if validating all bundles)
    if templates_to_check is None and "examples" in data:
        errors.extend(_validate_examples(data["examples"], bundle_names))

    # Validate metadata if present (only if validating all bundles)
    if templates_to_check is None and "metadata" in data:
        errors.extend(_validate_metadata(data["metadata"], bundles))

    return len(errors) == 0, errors