PKG-09: refuse a deploy that would be a no-op, or that the add-on would reject
checks / unit (pull_request) Successful in 6m11s
checks / unit (pull_request) Successful in 6m11s
Two ways a deploy of this add-on has gone wrong, and the Supervisor reports both as success. THE SILENT NO-OP. The Supervisor keys the built image by `version:`, so `ha apps update` against an unchanged version installs nothing and says it worked. PKG-06 recorded this on 2026-09-05 with four merges behind an unbumped version. PKG-07 recorded it again on 2026-09-06 with six. Both times a human noticed. Nothing else was going to. THE REFUSED CONFIG. A config the shipped validator rejects is SystemExit(78) - the add-on does not start. ⚠️ AND THE INGRESS PANEL GOES WITH IT, because the add-on serving it is not running, so recovery means the Supervisor UI. SAFETY-39's rule 10 made this reachable from a config that started fine on the previous version: 0.7.0's own default `slew_w: 1000` exceeds the 555.6 W ceiling on a 5000 W rail. ⚠️ WHY THIS IS NOT A CI CHECK, WHICH IS THE OBVIOUS IMPLEMENTATION. "A pull request that touches runtime code must bump the version" is the wrong shape for this project. It deliberately batches many merges into one deploy, so a per-PR bump would put every open branch in conflict over one line of config.yaml, and it still would not prevent the actual failure - which happens at DEPLOY, not at merge. The check belongs where the mistake is made, so it is a script the operator runs, not a gate on a pull request. WHAT IT DOES NOT DO. It does not read the live box: the options blob needs the Supervisor API and a supervisor token, and neither the HA REST API nor any entity exposes it. The operator exports the options and passes --options. Without that flag the validator half SKIPS - loudly, exit 2 - and never pretends. `app/validate.py` imports its siblings and those need aiohttp, so on a bare host that half skips too and says which container to re-run it in. EXIT CODES: 0 cleared, 1 REFUSED, 2 NOT CLEARED. ⚠️ 2 IS NOT A PASS. ⚠️ THE TEST'S FIRST DRAFT REPORTED FOUR SILENT SKIPS AS PASSES. It copied the gate into each throwaway fixture, so the script's sibling `app/` was the stub the fixture had built and the validator import failed - every validator assertion skipped, and four of them were written to expect a clearance. Fixed by invoking the gate WHERE IT SHIPS and pointing --tree/--repo at the fixture, which is also the behaviour that matters: the gate must check a config against the validator it ships beside. That is MUTATION.md habit 4 again - a check whose data cannot reach the failure it names - caught this time by the harness positive control rather than in review. Every refusal in test_version_gate.py is DRIVEN: a repository is built in which the failure is really present and the check is required to say so. There is a positive control that the no-op detector does NOT fire on a docs-only change, and a pin that omitting --options can never exit 0. This change needs no version bump, and the gate agrees: `tools/` and `test_*.py` are outside its runtime set, because demanding a bump for a typo fix trains people to bump without reading - the habit it exists to prevent. Suite: 24 files, all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BwRLw7eKYj2byVdwB6kbjU
This commit is contained in:
co-authored by
Claude Opus 5
parent
3368231048
commit
09b84e30b0
@@ -1452,3 +1452,46 @@ If an MQTT broker is available the add-on publishes setpoint, grid power,
|
||||
battery power, circulating power, state of charge, maintenance phase and
|
||||
controller status by MQTT discovery. This is observability only — the controller works fine without a
|
||||
broker, and MQTT problems can never affect control.
|
||||
|
||||
## Before you deploy: `tools/predeploy_check.py` (PKG-09)
|
||||
|
||||
Two ways a deploy has gone wrong on this system, both of which the Supervisor
|
||||
reports as success:
|
||||
|
||||
**The silent no-op.** The Supervisor keys the built image by `version:` in
|
||||
`config.yaml`. `ha apps update` against an unchanged version installs nothing
|
||||
and says it worked. This happened twice: PKG-06 (four merges behind an unbumped
|
||||
version) and PKG-07 (six). Both times a human noticed. Nothing else would have.
|
||||
|
||||
**The refused config.** A config the shipped validator rejects is
|
||||
`SystemExit(78)` — the add-on does not start. ⚠️ **And the ingress panel goes
|
||||
with it**, because the add-on serving it is not running, so recovery means the
|
||||
Supervisor UI. SAFETY-39's rule 10 made this reachable from a config that
|
||||
started fine on the previous version: 0.7.0's own default `slew_w: 1000`
|
||||
exceeds the 555.6 W ceiling on a 5000 W rail.
|
||||
|
||||
Run it inside the container, so the validator half actually runs:
|
||||
|
||||
docker run --rm -v <repo>:/w:ro goodwe-addon-checks:1 \
|
||||
python3 /w/goodwe_controller/tools/predeploy_check.py \
|
||||
--deployed-version 0.7.0 --options /w/live-options.json
|
||||
|
||||
Export `live-options.json` from the Supervisor first — the options blob is not
|
||||
reachable through the Home Assistant REST API, and no entity carries it.
|
||||
|
||||
**Exit codes.** `0` cleared · `1` REFUSED, do not deploy · `2` NOT CLEARED,
|
||||
meaning a check did not run. ⚠️ **2 is not a pass.** On a bare host without the
|
||||
add-on's dependencies the validator half skips and returns 2 rather than
|
||||
pretending; `test_version_gate.py` pins that a skipped check can never exit 0,
|
||||
because the first draft of that test reported four silent skips as passes.
|
||||
|
||||
⚠️ **Why this is not a CI check.** The obvious gate — "a pull request touching
|
||||
runtime code must bump the version" — is the wrong shape here. This project
|
||||
deliberately batches many merges into one deploy, so a per-PR bump would put
|
||||
every branch in conflict over one line and still not prevent the failure, which
|
||||
happens at deploy and not at merge. The check belongs where the mistake is made.
|
||||
|
||||
⚠️ This file's own change does **not** need a version bump, and the gate agrees:
|
||||
`tools/` and `test_*.py` are not in its runtime set, because demanding a bump
|
||||
for a typo fix trains people to bump without reading — the habit the check
|
||||
exists to prevent.
|
||||
|
||||
Reference in New Issue
Block a user