Files
audi-app/VERSIONIERUNG.md
T
tobias 2ea01ce2ce install.ps1 warnt vor einem veralteten OTA-Bündel
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>
2026-08-24 09:32:36 +02:00

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.