2ea01ce2ce
Beim Prüfen, ob die GitHub-Entfernung eine Versionserhöhung brauchte, fiel eine echte Lücke auf: nichts hielt frontend/app/bundle.json auf demselben Stand wie manifest.json. Bei der nächsten echten Versionserhöhung ohne "npm run ota" hätte das zu einem stillen Widerspruch geführt - die Hinweisleiste hätte "veraltet" gemeldet (liest die Version live), die Update-Seite "aktuell" (vergleicht gegen das eingefrorene, dann falsche Bündel) -, ohne dass ein Update über OTA erreichbar gewesen wäre, bis jemand den Widerspruch bemerkt. install.ps1 liest jetzt direkt nach der Manifest-Version auch frontend/app/bundle.json und vergleicht. Bei Abweichung: deutliche Warnung plus erster Eintrag in der Restliste. Fehlt das Bündel ganz, passiert nichts - OTA ist optional, das ist der normale Zustand. Beide Pfade an einem Mock-Zielordner geprüft: passende Versionen melden "OTA-Bündel passt zur Integration" ohne Restliste-Eintrag; eine testweise erhöhte Manifest-Version (danach byte-genau zurückgesetzt, gegen HEAD gegengeprüft) erzeugt die Warnung und landet als Restliste-Punkt 1. VERSIONIERUNG.md: "Was du tun musst" nennt den OTA-Neubau jetzt als eigenen nummerierten Schritt, statt ihn ganz auszulassen. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
155 lines
7.7 KiB
Markdown
155 lines
7.7 KiB
Markdown
# Versionierung — eine Zahl, drei Verbraucher
|
|
|
|
Kurzfassung für den Alltag: **Wenn du etwas änderst, erhöhe `version` in
|
|
`custom_components/audi_dashboard/manifest.json`.** Alles Weitere ergibt sich
|
|
daraus von selbst.
|
|
|
|
---
|
|
|
|
## Warum es diese Zahl braucht
|
|
|
|
Das Panel und die iOS-App werden völlig unterschiedlich ausgeliefert:
|
|
|
|
- **Panel:** gehört zur Integration und wird mit ihr zusammen installiert. Es
|
|
kann gar nicht hinterherhinken — ein Update der Integration ist automatisch
|
|
ein Update des Panels.
|
|
- **iOS-App:** eine Capacitor-Hülle mit **fest gebündelten** Dateien. Sie
|
|
bleibt auf dem Stand, der beim Signieren in Xcode eingebaut wurde —
|
|
unbegrenzt.
|
|
|
|
Die Paritätsregel in `AGENTS.md` sichert, dass beide Codebasen in derselben
|
|
Sitzung geändert werden. Über das, was *läuft*, sagte lange nichts etwas: die
|
|
iOS-App konnte wochenlang zurückliegen, ohne dass es irgendwo sichtbar wurde.
|
|
|
|
Die Versionszahl schließt genau diese Lücke — nicht, indem sie die Abweichung
|
|
verhindert (das kann keine Zahl), sondern indem sie sie **sichtbar** macht.
|
|
|
|
## Wo sie steht
|
|
|
|
In `custom_components/audi_dashboard/manifest.json`, Feld `version`. Format
|
|
`2026.8.23.2` (Datum + laufende Nummer).
|
|
|
|
Genau dort und nirgends sonst. Home Assistant verlangt das Feld für jede
|
|
benutzerdefinierte Integration — eine zweite Quelle für dieselbe Angabe wäre
|
|
eine zu viel. Sie hätten irgendwann auseinandergelegen, und dann wäre der
|
|
Vergleich still falsch geworden statt laut.
|
|
|
|
> HACS würde dieses Feld ebenfalls lesen und danach entscheiden, ob ein
|
|
> Update bereitliegt — spielt hier aber keine Rolle: HACS kann laut eigener
|
|
> Dokumentation grundsätzlich nicht mit privaten GitHub-Repositories
|
|
> arbeiten, und das Repository ist privat (lizenzierte Audi-Assets). Das
|
|
> Manifest-Feld bleibt trotzdem die einzig richtige Stelle - Home Assistant
|
|
> selbst braucht es unabhängig von HACS.
|
|
|
|
> Bis 2026-08-23 gab es dafür eine eigene Datei `VERSION` in der Repo-Wurzel,
|
|
> daneben eine Unix-Sekundenzahl in `www/audi-dashboard-version.json` als
|
|
> Cache-Brecher. Beide sind mit dem Umbau zur Integration entfallen: die
|
|
> Version steht jetzt im Manifest, und die Oberflächen-Dateien tragen sie als
|
|
> `?v=…` in ihrer URL — die Integration hängt sie beim Anmelden des Panels an.
|
|
> Ein Cache-Brecher, der sich nur bei echten Änderungen ändert, ist der
|
|
> bessere: er lädt nichts unnötig neu.
|
|
|
|
## Wie sie durchs System läuft
|
|
|
|
```
|
|
manifest.json { "version": "2026.8.23.2" }
|
|
│
|
|
├─► Home Assistant führt sie als Version der Integration
|
|
│ └─► HACS vergleicht sie gegen das Repository und meldet Updates
|
|
│
|
|
├─► die Integration veröffentlicht sie als
|
|
│ sensor.audi_dashboard_app_version
|
|
│ └─► die iOS-App vergleicht sie mit ihrer eigenen, einkompilierten
|
|
│ Zahl und zeigt bei Abweichung einen Hinweis in der
|
|
│ Hinweisleiste
|
|
│
|
|
├─► sie hängt als ?v=… an der Panel-URL
|
|
│ └─► Browser laden Panel, CSS und Badges nur dann neu, wenn sich
|
|
│ wirklich etwas geändert hat
|
|
│
|
|
└─► companion-app: vite liest das Manifest beim Bauen und setzt sie als
|
|
__APP_VERSION__ ein (bricht ab, wenn das Feld fehlt)
|
|
```
|
|
|
|
Der Vergleich läuft in eine Richtung: **die Integration sagt, welcher Stand
|
|
installiert ist; die App sagt, welchen sie hat.** Stimmen sie nicht überein,
|
|
ist die App zu alt (oder, seltener, die Integration).
|
|
|
|
Bewusst **nur auf Gleichheit**, nie größer/kleiner: die Version ist eine
|
|
Kennung, keine Zahl. Ein Sortierversuch wäre scheingenau und würde bei einem
|
|
Formatwechsel still falsche Antworten geben. Fehlt eine der beiden Seiten, wird
|
|
gar nicht verglichen und nichts gemeldet — ein älteres Backend oder ein Start
|
|
ohne Netz darf keinen Fehlalarm auslösen.
|
|
|
|
## Was du tun musst
|
|
|
|
**Bei einer Änderung an der Oberfläche oder am Backend:**
|
|
|
|
1. `version` in `manifest.json` erhöhen — bei mehreren Änderungen am selben Tag
|
|
die laufende Nummer: `2026.8.23.2` → `2026.8.23.3`, am nächsten Tag
|
|
`2026.8.24.1`.
|
|
2. **OTA-Bündel neu bauen: `npm run ota`** (in `companion-app/`). Baut die
|
|
Oberfläche und packt sie mit der neuen Versionsnummer — siehe
|
|
[OTA-Updates](#ota-updates-seit-2026-08-24) unten. Ohne diesen Schritt
|
|
trägt `frontend/app/bundle.json` noch die alte Nummer: die Hinweisleiste
|
|
sagt dann „du bist veraltet" (liest die Version live), die Update-Seite
|
|
sagt „du bist aktuell" (vergleicht gegen das eingefrorene, jetzt falsche
|
|
Bündel) — ein Widerspruch, aus dem es ohne diesen Schritt keinen Ausweg
|
|
gibt. `install.ps1` prüft das seit 2026-08-24 selbst und warnt, falls
|
|
vergessen — aber besser, es passiert gar nicht erst.
|
|
3. Integration ausliefern: `homeassistant\installationspaket\Installieren.cmd`
|
|
(HACS scheidet aus — siehe Kasten oben, das Repository ist privat).
|
|
4. iOS-App für Xcode neu bauen (`npm run build`), damit eine über App Store
|
|
Connect / Sideload signierte Fassung dieselbe Zahl einkompiliert bekommt.
|
|
Für alle, die die App schon installiert haben, erledigt Schritt 2 das
|
|
automatisch über OTA — dieser Schritt ist nur für die nächste
|
|
Neuinstallation oder Signatur-Erneuerung nötig.
|
|
|
|
Vergisst du Schritt 4, ist das kein stiller Fehler mehr: die App meldet selbst,
|
|
dass sie älter ist als der Server. Vergisst du Schritt 2, bleibt es leider
|
|
still — deshalb die Prüfung in `install.ps1`.
|
|
|
|
**Bei einer reinen Neuinstallation** ohne Codeänderung: nichts tun.
|
|
|
|
## OTA-Updates (seit 2026-08-24)
|
|
|
|
`@capgo/capacitor-updater` ist eingebaut. Für reine Oberflächen-Änderungen
|
|
(JavaScript, CSS, Bilder) entfällt Schritt 3 oben — die App lädt sich den
|
|
neuen Stand selbst, auf einen Tastendruck des Nutzers in Einstellungen →
|
|
App-Update. Xcode wird nur noch für echte native Änderungen gebraucht
|
|
(neue Plugins, Berechtigungen, `capacitor.config.ts`).
|
|
|
|
**Woher das Bündel kommt.** `companion-app/scripts/ota-paket.ps1` packt
|
|
`dist/` (ohne Sourcemaps) in
|
|
`custom_components/audi_dashboard/frontend/app/bundle.zip`, daneben
|
|
`bundle.json` mit Version, SHA-256 und Größe. Weil das im Integrationsordner
|
|
liegt, reist es bei jeder Auslieferung automatisch mit — kein zweiter Weg, den
|
|
man vergessen könnte. Ausgeliefert wird es unter
|
|
`/audi_dashboard_static/app/bundle.zip`, gemeldet über dasselbe Feld, das
|
|
schon den Versionsvergleich trägt: `sensor.audi_dashboard_app_version`,
|
|
Attribut `daten.buendel`.
|
|
|
|
**Was die App damit macht** (`companion-app/src/daten/ota.ts`): erscheint ein
|
|
Bündel, dessen Version von der eigenen abweicht, zeigt Einstellungen →
|
|
App-Update einen Knopf. Ein Tastendruck lädt die Zip, prüft sie clientseitig
|
|
gegen die mitgelieferte SHA-256 (das Plugin selbst, nicht diese App) und
|
|
tauscht die Oberfläche aus — die App startet dabei neu.
|
|
|
|
**Die Rückfallebene.** `notifyAppReady()` läuft in `App.tsx`, sobald React
|
|
tatsächlich gerendert hat. Bleibt diese Meldung aus, weil das neue Bündel
|
|
die App zerlegt hat, rollt das Plugin nach der eingestellten Frist
|
|
(`appReadyTimeout` in `capacitor.config.ts`) von selbst auf das vorherige
|
|
Bündel zurück — ein kaputtes Update kann das Gerät deshalb nicht dauerhaft
|
|
unbrauchbar machen.
|
|
|
|
**Warum kein Selbstlauf.** `autoUpdate: false` — die App lädt nur auf
|
|
ausdrücklichen Tastendruck, nie im Hintergrund. Ein Update, das während einer
|
|
Fahrteintragung ungefragt die Oberfläche austauscht, wäre die falsche Sorte
|
|
Hilfsbereitschaft.
|
|
|
|
**HACS spielt hier keine Rolle und wird es auch nicht.** Das Update-Bündel
|
|
reist mit `install.ps1` (dem einzigen Installationsweg, siehe oben) genauso
|
|
mit wie mit einem hypothetischen HACS-Download — der Mechanismus hängt nicht
|
|
daran, wie die Integration selbst auf die Instanz kommt, nur daran, dass das
|
|
Bündel im Integrationsordner liegt.
|