Files
audi-app/APPLE_DEV_BACKLOG.md
T
tobias 185a044c5e Signierskript: ein Klon von vor dem Rewrite darf nicht pullen
Am 07.09.2026 sind drei Commits am Ende von main entfernt und der Branch per
Force-Push zurueckgesetzt worden. Ein aelterer Klon - der Mac, auf dem
signiert wird - steht damit VORAUS statt zurueck. Der Waechter sagte dort
"Erst 'git pull' ausfuehren", und genau das ist die Falle: der Merge holt die
entfernten Commits zurueck in die Historie, der naechste Push stellt sie auf
dem Server wieder her, und zwar ohne --force.

Git kann diese Lage nicht von echter ungepushter Arbeit unterscheiden. Das
Skript entscheidet deshalb nicht, sondern listet die nur lokal vorhandenen
Commits auf, nennt beide Moeglichkeiten samt richtigem Befehl und bricht ab.
Nur der schlichte Rueckstand empfiehlt weiterhin 'git pull'.

Beide Zweige gegen Wegwerf-Repos in der jeweiligen Form geprueft, nicht nur
geparst - die erste Fassung stufte die Lage als "auseinandergelaufen" ein und
haette dem Mac zum Pushen geraten.

APPLE_DEV_BACKLOG.md nennt den einmaligen Schritt im Abschnitt "Vorher".

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 20:58:05 +02:00

9.1 KiB

Apple Dev Backlog (DM360)

Alles, was einen Xcode-Lauf oder das Apple Developer Portal braucht, wird hier gesammelt - und nur hier.

Warum: ein Xcode-Lauf lohnt sich nicht für eine einzelne native Änderung. Er wird erst gestartet, wenn eine nennenswerte Menge zusammengekommen ist; die Entscheidung darüber trifft der Eigentümer, nicht eine Sitzung.

Arbeitsregel (verbindlich)

  • Jede Aufgabe, die Xcode oder das Apple-Portal braucht, kommt in diese Datei, in derselben Sitzung, in der sie auffällt - nicht in eine Notiz im Bericht und nicht nur in AGENTS.md.
  • Nichts davon wird von sich aus gestartet. Ein Xcode-Lauf ist eine Handlung des Eigentümers am Mac; eine Sitzung bereitet ihn vor und meldet, wenn sich genug angesammelt hat.
  • Was ohne Xcode geht, gehört nicht hierher: Änderungen an der Oberfläche der App reisen im OTA-Bündel (@capgo/capacitor-updater), Änderungen am Panel und am Backend über die Selbstaktualisierung der Integration. Nur nativer Code, Plugins, Berechtigungen, Signatur und Auslieferungsweg brauchen einen Lauf.
  • Erledigtes wird nicht gelöscht, sondern nach unten unter „Erledigt" verschoben - mit der Fassung, in der es ausgeliefert wurde. Sonst wird dieselbe Frage in einem halben Jahr wieder aufgemacht.

Stand der ausgelieferten App

Gemessen am Artefakt companion-app/auslieferung/App.ipa, nicht aus dem Gedächtnis (06.09.2026):

Fassung 2026.9.4.17 (Build 1), App und Erweiterung gleich
Bundle-ID app.datametric360, Erweiterung app.datametric360.teilen
Profile beide Ad Hoc, 2 Geräte, gültig bis 2027-08-29
Berechtigungen Standort, Kalender (beide Schlüssel), Kamera - alle vorhanden
packageClassList 6 Klassen - beide eigenen Plugins fehlen

Zum Vergleich: das Repository steht auf 2026.9.6.5. Der Abstand ist kein Rückstand - alles dazwischen war Oberfläche und Backend und reist über OTA. Was wirklich auf einen Lauf wartet, steht unten.


Wie ein Lauf abläuft

Diese Datei ist die Übergabe. Wer am Mac sitzt - mit oder ohne Claude am Xcode - braucht keine weitere: hier steht der Stand, der eine Befehl, und was danach zu prüfen ist. AGENTS.md ist nur für das Warum nötig.

Vorher

Nichts vorzubereiten außer einem aktuellen Stand:

git pull

Einmalig, für jeden Klon von vor dem 07.09.2026: an diesem Tag sind drei Commits am Ende von main entfernt und der Branch per Force-Push zurückgesetzt worden. Ein älterer Klon steht damit vor origin/main und git pull ist dort das Falsche — es würde die entfernten Commits per Merge zurückholen, und der nächste Push stellte sie auf dem Server wieder her (er ginge sogar ohne --force durch). Richtig ist einmalig:

git fetch origin && git reset --hard origin/main

Das Skript erkennt die Lage selbst, listet die betroffenen Commits auf und bricht ab, statt zu raten — es entscheidet aber nicht für dich, weil dieselbe Lage auch echte ungepushte Arbeit sein kann.

Das Skript prüft das selbst und bricht ab, wenn der Arbeitsbaum nicht sauber oder HEAD nicht gleich origin/main ist - sonst signierte es einen veralteten Stand. Ebenso bricht es ab, wenn das Zielgerät nicht im Team eingetragen ist („Your team has no devices"); das ist dann eine Portal-Sache, kein Projektfehler.

Der Lauf

bash companion-app/scripts/ios-signieren.sh

Ein Befehl, von jedem Verzeichnis im Repository aus. Er wechselt selbst nach companion-app/ und erledigt der Reihe nach: Git-Stand prüfen, Webbündel bauen, native Hülle synchronisieren, Standort-/Kalender-/Kamera-Berechtigung eintragen, Share-Erweiterung einrichten, App-Symbol einspielen, Versionsnummern setzen, Archiv bauen und signieren, .ipa exportieren, die fertige .ipa gegenlesen und die Auslieferung über die Luft vorbereiten.

Die Fassung kommt aus custom_components/audi_dashboard/manifest.json - es gibt nichts von Hand hochzuzählen. Beim Schreiben dieser Zeilen wäre das 2026.9.6.5.

Einmalig beim ersten Signieren: der Schlüsselbund fragt, ob codesign den Schlüssel benutzen darf. „Immer erlauben" wählen, nicht „Erlauben" - ein Archiv signiert über 25 Programmteile und fragte sonst jedes Mal erneut. Bleibt der Lauf minutenlang beim Signieren stehen, wartet er auf genau diesen Dialog.

Danach

git add companion-app/auslieferung && git commit && git push

Das Telefon lädt die .ipa aus dem Repository - ohne diesen Push ändert sich dort nichts. Danach in Safari auf dem Telefon öffnen (andere Browser reichen itms-services:// nicht an das System weiter):

itms-services://?action=download-manifest&url=https://gitea.nothaft.cloud/paul/audi-app/raw/branch/main/companion-app/auslieferung/manifest.plist

Die Adresse trägt kein ?v= - ein zweites Fragezeichen zerlegt den Parameter, und weil iOS die Rückfrage erst aus dem geladenen Manifest baut, passiert dann sichtbar gar nichts (Abschnitt CK).

Zum Schluss die eine Prüfung, die kein Skript abnehmen kann - siehe Abschnitt C unten.


A. Wartet auf einen Xcode-Lauf

A1. Zwei eigene Plugins sind auf dem Telefon tot

Auswirkung: Zwischenablage und Kalendereintrag funktionieren nicht. Das ist die einzige Position mit sichtbarem Ausfall.

Capacitor 8 registriert ausschließlich, was in packageClassList der capacitor.config.json im Bündel steht (CapacitorBridge.swift, registerPlugins()). Diese Liste erzeugt cap sync aus den installierten npm-Paketen - app-eigene Plugins sind keine. Beide Klassen liegen nachweislich im Programm der ausgelieferten .ipa und werden trotzdem nie registriert.

Korrektur liegt im Repo (a99ae43): ios-teilen-einrichten.mjs trägt die Klassen nach jedem cap sync nach und liest ihre Namen aus den Swift-Dateien selbst, damit sie nicht an zwei Stellen gepflegt werden. ios-signieren.sh bricht ab, wenn eine davon im fertigen Bündel fehlt.

Hintergrund vollständig: AGENTS.md, Abschnitt DH und CM.

ios-luftweg.sh hängte den Cache-Brecher ?v= auch an die Manifest-Adresse innerhalb des itms-services-Links - damit steht dort ein zweites ?, der Abruf scheitert, und weil iOS die Rückfrage erst aus dem geladenen Manifest baut, passiert sichtbar gar nichts.

Korrektur liegt im Repo (6365136). Die bereits ausgelieferte auslieferung/index.html wurde von Hand nachgezogen; das Skript zieht beim nächsten Lauf nach. Hintergrund: AGENTS.md, Abschnitt CK.


B. Wartet auf das Apple Developer Portal

Derzeit nichts offen. App-Gruppe, beide App-IDs und die Gerätefreigabe sind eingerichtet (Abschnitt AK), die Profile laufen bis 2027-08-29.

Vorzumerken, aber noch lange hin:

  • 2027-07-17 - das Entwicklerzertifikat läuft ab.
  • 2027-08-29 - beide Bereitstellungsprofile laufen ab.
  • Ein neues Gerät braucht eine Registrierung im Portal und frisch ausgestellte Profile. Ein bereits ausgestelltes Profil erfährt von einem neuen Gerät nichts - deshalb räumt ios-signieren.sh die zwischengespeicherten Profile vor dem Bauen weg (Abschnitt AI).

C. Nach dem Lauf zu prüfen

Am fertigen Bündel, nicht am Projekt. Die Begründung steht in Abschnitt AK: von fünf Fehlern der nativen Hülle haben vier einen erfolgreichen Archivlauf erzeugt und sind erst am entpackten .ipa aufgefallen.

Das Skript prüft selbst und bricht ab, wenn etwas fehlt:

  1. Die Erweiterung hat ein echtes Programm.
  2. App und Erweiterung tragen dieselbe CFBundleShortVersionString und eine brauchbare CFBundleVersion (ohne die lehnt iOS die Installation der ganzen App ab - Abschnitt CD).
  3. Die App-Gruppe steht in den Entitlements beider Ziele.
  4. Beide eigenen Plugin-Klassen stehen in packageClassList.

Von Hand bleibt genau eine Prüfung - auf dem Gerät: einen Beleg über das Teilen-Blatt schicken, ein PDF aus der Zwischenablage einfügen, einen Kalendereintrag anlegen. Ob die Plugins registriert sind, sagt Punkt 4; ob sie tun, was sie sollen, sagt nur das Telefon.


Was der Lauf von selbst erledigt

Damit es niemand ein zweites Mal von Hand macht: ios-signieren.sh trägt bei jedem Lauf Standort-, Kalender- und Kamera-Berechtigung ein, richtet die Share-Erweiterung ein, spielt das App-Symbol ein und setzt die Versionsnummern in beide Ziele. Das muss sein, weil companion-app/ios/ gitignored ist und von npx cap add ios jederzeit neu erzeugt wird - alles, was man in Xcode anklickt, wäre danach weg (Abschnitt AI).

Unvermeidlich von Hand bleiben: die einmalige Portal-Einrichtung, das Schlüsselbund-„Immer erlauben" beim ersten Signieren, und git push der fertigen .ipa, weil das Telefon sie aus dem Repository lädt.


Erledigt

Fassung Was
2026.9.4.17 Kamera-Berechtigung für den QR-Scanner
2026.9.4.7 CFBundleVersion in beiden Bündeln - die App war vorher nicht installierbar
2026.9.4.5 Versionsnummern von App und Erweiterung angeglichen
2026.9.2.7 Share-Erweiterung, Zwischenablage- und Kalender-Plugin gebaut und signiert
2026.8.30.8 Erste signierte .ipa, Luftweg über Gitea eingerichtet