fix(version): read from manifest.json (closes #784)#785
Conversation
…loses #784) The admin footer had been displaying "Beatify v3.2.0-rc29" since mid-April regardless of which version was actually installed. Cache-busting was a red herring — assets were loading fresh, the backend was just honestly reporting its stale `_VERSION` constant. Root cause is two compounding issues: 1. `server/base.py` hardcoded `_VERSION = "3.2.0-rc29"` as a constant. That constant was supposed to be auto-bumped on every release by `.github/workflows/version-bump.yml`. 2. Commit 6d056b3 (Apr 15) untracked `.github/workflows/` from git and added the path to `.gitignore`. The workflow file still exists on contributors' local disks, but GitHub Actions has no copy to run. Last actual run: v3.0.6-rc.4 on 2026-04-15. Every release since shipped with the wrong footer. This change eliminates the duplicated source of truth. Version is now read from `manifest.json` once at `async_setup_entry` (in an executor job so HA's blocking-I/O detector stays happy on reload) and cached in `hass.data[DOMAIN]['version']`. `_get_version(hass)` reads from there. No GitHub Action dependency, can never drift. `_get_version()` signature changed from no-args to optional `hass`. Only one caller (`StatusView` in `server/views.py`), updated inline. Falls back to "unknown" if hass isn't available — also covers a malformed install where manifest.json can't be read at all. The version-bump.yml workflow is now obsolete. Worth deleting the local-only copy in a follow-up. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumped manifest + sw.js CACHE_VERSION → 3.3.1-rc7. No frontend asset changes — HTML cache-busters unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request resolves issue #784 by establishing manifest.json as the single source of truth for the integration version, replacing a hardcoded constant that frequently drifted. The version is now read during setup via an executor job and cached in the Home Assistant data store. A review comment suggests enhancing the error handling in the manifest reading function to catch potential AttributeErrors if the JSON structure is unexpected.
| try: | ||
| return json.loads(manifest_path.read_text(encoding="utf-8")).get( | ||
| "version", "unknown" | ||
| ) | ||
| except (OSError, ValueError): |
There was a problem hiding this comment.
The _read_manifest_version function should also catch AttributeError. If manifest.json contains a valid JSON value that is not an object (e.g., null or []), json.loads() will return a type that does not have a .get() method, causing an unhandled exception that would fail the integration setup. Adding AttributeError to the exception handler ensures robustness against malformed manifest files, consistent with the defensive approach used in server/base.py.
| try: | |
| return json.loads(manifest_path.read_text(encoding="utf-8")).get( | |
| "version", "unknown" | |
| ) | |
| except (OSError, ValueError): | |
| try: | |
| return json.loads(manifest_path.read_text(encoding="utf-8")).get( | |
| "version", "unknown" | |
| ) | |
| except (OSError, ValueError, AttributeError): |
Closes #784. Cache-busting was a red herring — the admin footer was honestly reporting a stale
_VERSIONconstant that hadn't been bumped since April 15.Root cause
Two compounding issues:
Every release since (3.2.x stable, 3.3.0, all six 3.3.1-rc tags) shipped with the wrong footer.
The fix
Eliminate the duplicated source of truth. Version is now read from `manifest.json` once at `async_setup_entry` time (via `async_add_executor_job` to stay off the event loop, respecting HA 2026.2+'s blocking-I/O detector) and cached in `hass.data[DOMAIN]['version']`. `_get_version(hass)` reads from there, falls back to "unknown" if hass isn't available.
Single source of truth, no GitHub Action dependency, can never drift again.
Test plan
Tradeoffs
Version
🤖 Generated with Claude Code