diff --git a/scripts/deploy-stack.sh b/scripts/deploy-stack.sh index d50156b..2085c65 100755 --- a/scripts/deploy-stack.sh +++ b/scripts/deploy-stack.sh @@ -163,13 +163,17 @@ for entry in "${PAIRS[@]}"; do tmp_out=$(mktemp) tmp_err=$(mktemp) rc=0 - # --no-o --no-g: the deploy tree is root:docker 2775 (setgid) since the + # --no-o --no-g --omit-dir-times: the deploy tree is root:docker 2775 (setgid) since the # 2026-09-14 fleet normalisation, and plain -a makes rsync try to chgrp the # destination as the deploy identity, which is not root. It fails with # "chgrp ... Operation not permitted" and exits 23 AFTER transferring the # content — a loud error on a deploy that actually succeeded. The setgid bit # already assigns the right group, so rsync should not be fighting it. - rsync -az --no-o --no-g --delete --dry-run \ + # --omit-dir-times is the same problem one layer along: a non-root identity + # cannot utime() a root-owned directory either, which produced the identical + # "exit 23 after a successful transfer" on the next deploy. Both halves are + # needed; fixing only the group half looks fixed until the next run. + rsync -az --no-o --no-g --omit-dir-times --delete --dry-run \ "${RSYNC_REMOTE[@]}" \ --out-format='%i %n' \ "${EXCLUDES[@]}" "${extra[@]}" \ @@ -291,7 +295,7 @@ for entry in "${PAIRS[@]}"; do extra=() [ "$kind" = compose ] && extra+=(--exclude='conf/') printf 'pushing %s → %s\n' "$src" "$dest" - rsync -az --no-o --no-g --delete \ + rsync -az --no-o --no-g --omit-dir-times --delete \ "${RSYNC_REMOTE[@]}" \ "${EXCLUDES[@]}" "${extra[@]}" \ "$src" "$dest" diff --git a/stacks/esphome/compose.yaml b/stacks/esphome/compose.yaml index dcb7769..135cb5f 100644 --- a/stacks/esphome/compose.yaml +++ b/stacks/esphome/compose.yaml @@ -39,6 +39,23 @@ services: # the 2025.8.2 -> 2026.8.2 upgrade, 2026-09-14. - ESPHOME_USERNAME=${ESPHOME_USERNAME} - ESPHOME_PASSWORD=${ESPHOME_PASSWORD} + # + # Device Builder 1.0.0 added a remote-build peer-link receiver that binds + # 0.0.0.0:6055 and advertises itself over mDNS as _esphomebuilder._tcp. + # It is ON BY DEFAULT — controllers/remote_build/_state.py declares + # `remote_builds_enabled: bool = True` — so nobody enabled it; it arrived + # with the rewrite. There is exactly one Device Builder here and builds + # run locally in this container, so the feature has no role: it is + # unrequested LAN reachability on a privileged host-network container + # whose job is writing firmware to devices. + # + # ⚠ This binds it to loopback; it does NOT disable it. The real off + # switch is a Settings toggle in the UI, and its enabled flag has no + # on-disk representation until it is changed, so there is nothing to set + # from a playbook without inventing a schema. Flip it in Settings and + # this line can go. (ha-dev's stated preference, correctly, is not + # enabling a feature over restricting its bind address.) + - ESPHOME_REMOTE_BUILD_HOST=127.0.0.1 labels: - homepage.group=Apps - homepage.name=ESPHome