Files
RelayTV-HA/scripts/validate-repository.py
mark a1da517b4e
Hassfest / hassfest (push) Failing after 48s
Tests / test (push) Successful in 3m15s
Validate / validate-repository (push) Successful in 3s
ci: migrate workflows and releases to Gitea
2026-07-23 22:21:41 -05:00

70 lines
2.7 KiB
Python

#!/usr/bin/env python3
"""Validate the local repository structure used to package the integration."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
REPOSITORY_ROOT = Path(__file__).resolve().parent.parent
CUSTOM_COMPONENTS = REPOSITORY_ROOT / "custom_components"
def load_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as error:
raise ValueError(f"Could not read valid JSON from {path.relative_to(REPOSITORY_ROOT)}: {error}") from error
if not isinstance(value, dict):
raise ValueError(f"Expected a JSON object in {path.relative_to(REPOSITORY_ROOT)}")
return value
def main() -> None:
integration_directories = sorted(
path for path in CUSTOM_COMPONENTS.iterdir() if path.is_dir() and path.name != "__pycache__"
)
if len(integration_directories) != 1:
names = ", ".join(path.name for path in integration_directories) or "none"
raise ValueError(f"Expected exactly one integration under custom_components; found: {names}")
integration_directory = integration_directories[0]
manifest = load_json(integration_directory / "manifest.json")
required_manifest_fields = {
"codeowners",
"documentation",
"domain",
"issue_tracker",
"name",
"version",
}
missing_fields = sorted(required_manifest_fields - manifest.keys())
if missing_fields:
raise ValueError(f"manifest.json is missing required fields: {', '.join(missing_fields)}")
if manifest["domain"] != integration_directory.name:
raise ValueError("manifest.json domain must match its custom_components directory")
if not isinstance(manifest["codeowners"], list) or not manifest["codeowners"]:
raise ValueError("manifest.json codeowners must be a non-empty list")
if not (integration_directory / "brand" / "icon.png").is_file():
raise ValueError("The integration must include brand/icon.png")
hacs_manifest = load_json(REPOSITORY_ROOT / "hacs.json")
if not isinstance(hacs_manifest.get("name"), str) or not hacs_manifest["name"].strip():
raise ValueError("hacs.json must include a non-empty name")
if hacs_manifest.get("zip_release") is True:
filename = hacs_manifest.get("filename")
if not isinstance(filename, str) or not filename.endswith(".zip"):
raise ValueError("hacs.json zip_release requires a .zip filename")
if not (REPOSITORY_ROOT / "README.md").is_file():
raise ValueError("The repository must include README.md")
print(f"Repository structure is valid for integration {manifest['domain']} {manifest['version']}.")
if __name__ == "__main__":
main()