HA: git config-sync sidecar (auto-deploy /config from main) #87

Merged
k.renner merged 2 commits from worktree-ha-config-sync-sidecar into master 2026-06-29 19:37:47 +00:00
Owner

What

Adds a config-sync sidecar to the home-assistant pod so merges to main of the homeassistant repo auto-deploy into the running instance. Closes the gap found while verifying PR #8 (notifications): HA's /config is an RWO PVC git checkout with no automated pull, so merged config sat undeployed.

How it works

A small alpine/git:2.54.0 sidecar shares the config PVC. Loop (30s):

  1. git fetch origin main
  2. on change, force-sync only the HA config paths — automations.yaml + automations/, packages/, scripts.yaml, templates.yaml, configuration.yaml, notify.yaml, groups.yaml, powercalc.yaml, blueprints/ — via git checkout origin/main -- <paths>, then advance the branch ref without touching the working tree
  3. POST /api/services/homeassistant/reload_all (no restart)

Left untouched on purpose: www/ (HACS rewrites those tracked files at runtime) and untracked runtime state (.storage/, *.db, secrets.yaml). git auth reuses the PAT already embedded in the PVC's remote URL; reload auth uses a long-lived token in a sops secret (config-sync-token.sops.yaml, same age recipient as the other HA secrets).

Script lives in config-sync.sh (mounted via configMapGenerator, hash suffix disabled like the nginx one — editing it needs a manual rollout restart).

⚠️ Behavioral consequence — git is now source of truth

Uncommitted edits to a synced path made directly in code-server, or via HA's UI automation editor (which writes automations.yaml), are reverted within ~30s. Commit + push to persist. Helpers/integrations configured through the HA UI live in .storage/ (untracked) and are unaffected.

🔑 Follow-up — rotate the token

The reload token currently reuses the existing HA long-lived token, to get this live with zero extra steps. Recommend minting a dedicated config-sync LLAT (HA → Profile → Security) and replacing the secret, so it's revocable independently of the personal token.

Notes / limits

  • First sync deploys everything merged-but-not-yet-pulled (currently PR #7 Solcast + PR #8 notifications) and reloads.
  • reload_all covers automations/scripts/templates/packages; a change to the notify: rest platform or a new integration would still need a restart (not the case here).
  • File deletions of a synced path aren't propagated by checkout -- <path> (edits/adds are); rare for config, can harden later.
  • Validated: kubectl kustomize builds (32 objects), all refs resolve, token sealed (sops, 0 plaintext leak). Not yet applied — Flux deploys on merge.

🤖 Generated with Claude Code

## What Adds a `config-sync` sidecar to the home-assistant pod so merges to `main` of the `homeassistant` repo auto-deploy into the running instance. Closes the gap found while verifying PR #8 (notifications): HA's `/config` is an RWO PVC git checkout with **no** automated pull, so merged config sat undeployed. ## How it works A small `alpine/git:2.54.0` sidecar shares the `config` PVC. Loop (30s): 1. `git fetch origin main` 2. on change, **force-sync only the HA config paths** — `automations.yaml` + `automations/`, `packages/`, `scripts.yaml`, `templates.yaml`, `configuration.yaml`, `notify.yaml`, `groups.yaml`, `powercalc.yaml`, `blueprints/` — via `git checkout origin/main -- <paths>`, then advance the branch ref without touching the working tree 3. `POST /api/services/homeassistant/reload_all` (no restart) **Left untouched on purpose:** `www/` (HACS rewrites those tracked files at runtime) and untracked runtime state (`.storage/`, `*.db`, `secrets.yaml`). git auth reuses the PAT already embedded in the PVC's remote URL; reload auth uses a long-lived token in a sops secret (`config-sync-token.sops.yaml`, same age recipient as the other HA secrets). Script lives in `config-sync.sh` (mounted via `configMapGenerator`, hash suffix disabled like the nginx one — editing it needs a manual `rollout restart`). ## ⚠️ Behavioral consequence — git is now source of truth Uncommitted edits to a **synced path** made directly in code-server, **or via HA's UI automation editor** (which writes `automations.yaml`), are reverted within ~30s. Commit + push to persist. Helpers/integrations configured through the HA UI live in `.storage/` (untracked) and are unaffected. ## 🔑 Follow-up — rotate the token The reload token currently **reuses the existing HA long-lived token**, to get this live with zero extra steps. Recommend minting a dedicated `config-sync` LLAT (HA → Profile → Security) and replacing the secret, so it's revocable independently of the personal token. ## Notes / limits - First sync deploys everything merged-but-not-yet-pulled (currently PR #7 Solcast + PR #8 notifications) and reloads. - `reload_all` covers automations/scripts/templates/packages; a change to the `notify:` rest platform or a new integration would still need a restart (not the case here). - File deletions of a synced path aren't propagated by `checkout -- <path>` (edits/adds are); rare for config, can harden later. - Validated: `kubectl kustomize` builds (32 objects), all refs resolve, token sealed (sops, 0 plaintext leak). **Not yet applied** — Flux deploys on merge. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
k.renner added 1 commit 2026-06-29 19:24:02 +00:00
HA's /config is an RWO PVC that is a git checkout of the homeassistant repo,
edited via the code-server sidecar. There was no automated path from a merge to
the running instance, so merged config (e.g. the notification-queue work) sat
undeployed until a manual pull+reload.

Adds a `config-sync` sidecar (alpine/git) sharing the config PVC. Every 30s it
fetches origin/main and, on change, force-syncs ONLY the HA YAML config paths
(automations(.yaml+dir), packages/, scripts.yaml, templates.yaml,
configuration.yaml, notify.yaml, groups.yaml, powercalc.yaml, blueprints) into
the working tree, advances the branch ref without touching the working tree, and
calls homeassistant.reload_all so the change goes live with no restart.

Deliberately left untouched: www/ (HACS rewrites those tracked files at runtime)
and untracked runtime state (.storage/, *.db, secrets.yaml). git auth reuses the
PAT already embedded in the PVC's remote URL; reload auth uses a dedicated
long-lived token in a sops secret.

Consequence (git becomes source of truth): uncommitted edits to a synced path
made directly in code-server OR via HA's UI automation editor are reverted on the
next sync. Commit + push to persist.

NOTE: the reload token currently reuses the existing HA long-lived token; rotate
it to a dedicated `config-sync` token (revocable independently) — see PR.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgbyKgrN1yYWrkAA32rJE5
k.renner added 1 commit 2026-06-29 19:36:13 +00:00
The previous loop reloaded on any advance of origin/main, including commits that
touch only unsynced paths (www/, shelly_scripts/, docs). Now the reload trigger
is `git diff --quiet <remote> -- <config paths>`: HA is reloaded only when the
synced config paths differ. Non-config commits just advance the branch ref with
no reload. As a side effect the loop now also reverts local drift on synced
paths continuously (reloading only when something actually changed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GgbyKgrN1yYWrkAA32rJE5
k.renner merged commit d636266bd1 into master 2026-06-29 19:37:47 +00:00
Sign in to join this conversation.