homelab-codex-ws/scripts/ha/lib/normalize.py
oskar ee48319a86 feat(ha): skeleton for Home Assistant configs-as-code subproject
Structure only, no deploy path: DESIGN.md decision registry (phasing,
per-instance adapter, bidirectional sync with drift-abort on deploy,
reload-vs-restart gating, token handling), instances.yaml (ken/piha via
docker-exec, chelsty-ha via api), config/storage-export/fixtures dirs,
and a read-only scripts/ha/import.sh (docker-exec adapter only) with a
canonical YAML normalize+split library and an offline determinism test.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-21 15:29:39 +02:00

64 lines
1.6 KiB
Python
Executable file

#!/usr/bin/env python3
"""Canonical YAML normalization used by every HA import path.
One function, one style, used everywhere data lands in the repo (see
services/home-assistant/DESIGN.md, "Canonical format"): block collections,
indent width 2, unlimited line width, sorted keys, UTF-8.
"""
import json
import sys
import yaml
class _CanonicalDumper(yaml.SafeDumper):
pass
def _represent_none(dumper, _value):
return dumper.represent_scalar("tag:yaml.org,2002:null", "")
_CanonicalDumper.add_representer(type(None), _represent_none)
def dump_canonical_yaml(data):
"""Render `data` as canonical YAML text."""
return yaml.dump(
data,
Dumper=_CanonicalDumper,
default_flow_style=False,
sort_keys=True,
allow_unicode=True,
width=1 << 31,
indent=2,
)
def normalize_yaml_text(text):
"""Parse YAML text then re-dump it through the canonical formatter."""
data = yaml.safe_load(text)
return dump_canonical_yaml(data)
def normalize_json_text(text):
"""Parse JSON text (e.g. a `.storage/*` file) and dump as canonical YAML."""
data = json.loads(text)
return dump_canonical_yaml(data)
def main(argv):
if len(argv) != 3 or argv[1] not in ("yaml", "json"):
print("usage: normalize.py <yaml|json> <input-path>", file=sys.stderr)
return 2
mode, path = argv[1], argv[2]
with open(path, "r", encoding="utf-8") as f:
text = f.read()
out = normalize_yaml_text(text) if mode == "yaml" else normalize_json_text(text)
sys.stdout.write(out)
return 0
if __name__ == "__main__":
raise SystemExit(main(sys.argv))