Compare commits

...
Author SHA1 Message Date
atlas-botandClaude Opus 5.5 61407808d1 feat(home-assistant): config-sync publishes hacs-versions.json to live (REN-266)
ci/woodpecker/pr/ci-scripts-test Pipeline was successful
ci/woodpecker/pr/ci-manifests-test Pipeline was successful
ci/woodpecker/pr/pr-review Pipeline was successful
The sidecar now writes hacs-versions.json (domain, name, installed_version
per custom_components/*/manifest.json) and commits it to the `live` branch,
so the weekly HACS dependency check can read versions from git without an
HA token. Only those three fields, charset-filtered; nothing from .storage.
Deterministic output, so an unchanged install makes no commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 18:03:10 +00:00
+37 -1
View File
@@ -85,6 +85,37 @@ reload_lovelace() {
fi
}
# Publish installed HACS integration versions to hacs-versions.json (REN-266), so
# the weekly HACS dependency check can read them from git without an HA token.
# Source: custom_components/*/manifest.json only. Emits domain, name and version,
# each reduced to [A-Za-z0-9 ._+-] — nothing else from the manifest, nothing from
# .storage. No timestamp and a sorted glob, so an unchanged install rewrites
# nothing and makes no commit. The file is replaced only when its content changes.
HACS_FILE="hacs-versions.json"
manifest_field() {
tr -d '\n' < "$1" | grep -o "\"$2\"[[:space:]]*:[[:space:]]*\"[^\"]*\"" | head -n 1 \
| sed 's/.*:[[:space:]]*"\([^"]*\)"$/\1/' | tr -cd 'A-Za-z0-9 ._+-'
}
write_hacs_versions() {
[ -d custom_components ] || return 0
tmp="${HACS_FILE}.tmp"
sep=""
{
printf '{\n "source": "custom_components/*/manifest.json",\n "components": ['
for m in custom_components/*/manifest.json; do
[ -f "$m" ] || continue
dir="${m#custom_components/}"; dir="${dir%/manifest.json}"
domain="$(manifest_field "$m" domain)"
printf '%s\n {"domain": "%s", "name": "%s", "installed_version": "%s"}' \
"$sep" "${domain:-$(printf '%s' "$dir" | tr -cd 'A-Za-z0-9._+-')}" \
"$(manifest_field "$m" name)" "$(manifest_field "$m" version)"
sep=","
done
printf '\n ]\n}\n'
} > "$tmp"
if cmp -s "$tmp" "${HACS_FILE}"; then rm -f "$tmp"; else mv "$tmp" "${HACS_FILE}"; fi
}
# Open a PR live -> main if one isn't already open. gitea returns 422 when a PR
# for this head already exists (or there's no diff); ignore it.
ensure_pr() {
@@ -127,10 +158,15 @@ git symbolic-ref HEAD "refs/heads/${LIVE}" 2>/dev/null || true
FIRST=1
while true; do
# 1. PUSH: capture live edits to the tracked config paths onto `live`.
# hacs-versions.json is regenerated first and added with -f, so a .gitignore
# in the HA repo cannot hide it. Best-effort: a failure never stops the loop.
write_hacs_versions 2>/dev/null || log "WARN could not write ${HACS_FILE}"
# shellcheck disable=SC2086
if [ -n "$(git status --porcelain -- ${PATHS} 2>/dev/null)" ]; then
if [ -n "$(git status --porcelain -- ${PATHS} 2>/dev/null)" ] \
|| [ -n "$(git status --porcelain --ignored=matching -- "${HACS_FILE}" 2>/dev/null)" ]; then
# shellcheck disable=SC2086
git add -A -- ${PATHS} 2>/dev/null || true
[ -f "${HACS_FILE}" ] && git add -f -- "${HACS_FILE}" 2>/dev/null || true
if git commit -q -m "chore(live): HA config change $(date -Iseconds)" 2>/dev/null; then
log "committed live config change(s)"
if git push -q origin "${LIVE}" 2>/tmp/push.err; then