Files
audi-app/companion-app/scripts/ios-signieren.sh
T
tobias 5b840ea510 Signierskript richtet den Klon selbst aus - kein Handgriff mehr noetig
Der Waechter brach bisher ab und legte dem Menschen einen Befehl vor. Jetzt
bringt er den Klon selbst auf origin/main, solange das nachweislich verlustfrei
ist: reiner Rueckstand wird vorgespult; Commits, die nur lokal liegen, deren
Tree aber dem des gemeinsamen Vorfahren gleicht, tragen inhaltlich nichts bei
und werden verworfen (Reflog haelt sie 90 Tage). Nur Commits, die auch den
Inhalt aendern, fuehren weiterhin zum Abbruch mit beiden Wegen zur Wahl.

Damit entfaellt die Vorbereitung vor einem Signierlauf vollstaendig - wichtig,
weil die bisher dokumentierte Vorbereitung ausgerechnet `git pull` war, also
genau der Befehl, der auf einem Klon von vor dem Rewrite die entfernten Commits
zurueckgeholt haette.

Fuenf Lagen gegen Wegwerf-Repositories geprueft: gleich, zurueck, inhaltslos
voraus, echte ungepushte Arbeit, echte Divergenz.

APPLE_DEV_BACKLOG.md, OFFEN.md und AGENTS.md (Abschnitt AJ) nachgezogen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-07 22:01:32 +02:00

425 lines
20 KiB
Bash
Executable File

#!/usr/bin/env bash
# Baut die native iOS-Huelle und signiert sie zu einer installierbaren .ipa.
# Nur auf einem Mac mit Xcode lauffaehig. Siehe ../../UMSETZUNGSPLAN.md Phase 10.
#
# Warum es dieses Skript gibt: `ios/` ist absichtlich gitignored (aus dem
# Webbuendel jederzeit wiederherstellbar). Damit verschwindet aber auch jede
# Signatureinstellung, die man in Xcode von Hand setzt, beim naechsten
# `npx cap add ios` wieder. Die Team-Kennung gehoert deshalb hierher - in eine
# versionierte Datei - und wird dem Build von aussen mitgegeben.
#
# Voraussetzung, die dieses Skript nicht herstellen kann: das Zielgeraet muss
# im Team eingetragen sein. Das Ad-hoc-Profil traegt eine feste Liste von
# Geraetekennungen, ein unbekanntes Telefon lehnt die fertige .ipa ab. Ein
# neues Geraet wird einmalig angemeldet - per Kabel an diesem Mac, dann
# erledigt es -allowProvisioningUpdates selbst, oder von Hand auf
# developer.apple.com. Bricht der Archivschritt mit "Your team has no devices"
# ab, fehlt genau dieser Eintrag; das ist kein Fehler im Projekt.
#
# Stand des Kontos, am 2026-08-29 am ausgestellten Profil nachgesehen statt
# vermutet: bezahlte Mitgliedschaft, Team RMACS9VLS4, Laufzeit ein Jahr. Die
# frueher hier vermerkten sieben Tage galten dem kostenlosen Personal Team und
# gelten nicht mehr; ebensowenig das "keine Geraeteverwaltung auf
# developer.apple.com", das daran hing.
#
# Der Export ueber -exportArchive ist erprobt und kein offener Punkt mehr: die
# ausgelieferten .ipa vom 2026-08-29 und 2026-08-30 sind auf genau diesem Weg
# entstanden und wurden ueber die Luft installiert (siehe ios-luftweg.sh).
# Scheitert er doch einmal, bleibt der direkte Weg aufs angeschlossene Geraet:
# xcodebuild -project ios/App/App.xcodeproj -scheme App \
# -destination 'id=<UDID>' -allowProvisioningUpdates \
# DEVELOPMENT_TEAM=$TEAM install
set -euo pipefail
HIER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
APP="$(dirname "$HIER")"
# Dieselbe Kennung wie zu Zeiten des kostenlosen Kontos: die bezahlte
# Mitgliedschaft hat das bestehende Team hochgestuft, statt ein neues
# anzulegen. Zwischenzeitlich stand hier das Gegenteil - widerlegt am
# 2026-08-29 durch das ausgestellte Profil (Team RMACS9VLS4, Laufzeit ein
# Jahr statt sieben Tage). Ueberschreibbar ueber APPLE_TEAM_ID.
TEAM="${APPLE_TEAM_ID:-RMACS9VLS4}"
AUSGABE="$APP/ios/build"
ARCHIV="$AUSGABE/DataMetric360.xcarchive"
cd "$APP"
# Ohne diese Pruefung baut/signiert das Skript zuverlaessig - aber nur den
# Stand, der gerade lokal ausgecheckt ist. Ein vergessener Abgleich mit dem
# Server waere damit ein stiller Veraltungsfehler wie schon mehrfach an anderer
# Stelle in diesem Projekt (Cache-Busting, OTA-Buendel-Abgleich,
# VERSIONIERUNG.md). Deshalb holt das Skript den Serverstand selbst und rueckt
# den Klon zurecht, wo das verlustfrei ist - und bricht sonst laut ab, statt
# einen optisch/inhaltlich alten Build auszuliefern.
echo "==> Git-Stand pruefen"
if [ -n "$(git status --porcelain)" ]; then
echo "FEHLER: Uncommittete Aenderungen im Arbeitsverzeichnis." >&2
git status --short >&2
echo "Erst committen oder stashen, dann erneut versuchen." >&2
exit 1
fi
git fetch origin
LOKAL="$(git rev-parse HEAD)"
FERN="$(git rev-parse origin/main)"
# Ziel dieses Blocks: der Klon steht danach auf origin/main - oder das Skript
# bricht ab. Zurechtruecken darf es sich selbst, solange dabei nachweislich
# nichts verlorengeht; der saubere Arbeitsbaum ist oben bereits Bedingung.
if [ "$LOKAL" != "$FERN" ]; then
BASIS="$(git merge-base "$LOKAL" "$FERN")"
if git merge-base --is-ancestor "$LOKAL" "$FERN"; then
echo " Klon war zurueck - wird vorgespult."
git merge --ff-only "$FERN" >/dev/null
LOKAL="$(git rev-parse HEAD)"
elif git diff --quiet "$BASIS" "$LOKAL"; then
# Hier liegen Commits, die origin/main nicht hat - aber ihr Tree ist derselbe
# wie der des gemeinsamen Vorfahren, sie tragen inhaltlich also NICHTS bei.
# Genau so sieht ein Klon aus, der noch vor einem Rewrite der Gegenseite steht
# (am 07.09.2026 einmal geschehen). Ein 'git pull' waere hier falsch: der Merge
# holt die entfernten Commits zurueck, und der naechste Push stellt sie auf dem
# Server wieder her - sogar ohne --force. Verwerfen kostet dagegen keine einzige
# Datei; das Reflog haelt sie noch 90 Tage.
echo " Nur hier vorhanden, inhaltlich ohne Wirkung - wird verworfen:"
git log --oneline "$BASIS..$LOKAL" | sed "s/^/ /"
git reset --hard "$FERN" >/dev/null
LOKAL="$(git rev-parse HEAD)"
echo " Zurueckgesetzt auf origin/main. Rueckholbar ueber: git reflog"
else
echo "FEHLER: Lokaler Stand ($LOKAL) weicht von origin/main ($FERN) ab." >&2
echo "" >&2
echo "Diese Commits liegen nur hier und aendern auch den Inhalt:" >&2
git log --oneline "$BASIS..$LOKAL" >&2
echo "" >&2
echo "Das sieht nach eigener, noch ungepushter Arbeit aus - deshalb entscheidet" >&2
echo "das Skript hier nichts. Entweder pushen:" >&2
echo " git push" >&2
echo "oder verwerfen, falls die Gegenseite sie absichtlich entfernt hat:" >&2
echo " git fetch origin && git reset --hard origin/main" >&2
echo "" >&2
echo "Kein 'git pull': ein Merge haengt die Commits wieder in die Historie." >&2
exit 1
fi
fi
echo " HEAD == origin/main ($LOKAL), aktuell."
echo "==> Webbuendel bauen"
npm run build
echo "==> Native Huelle synchronisieren"
if [ ! -d ios ]; then
echo "==> ios/ fehlt, wird neu erzeugt"
npx cap add ios
fi
npx cap sync ios
# Standort-Berechtigung ins Info.plist eintragen.
#
# Ohne diesen Schluessel zeigt iOS beim Aufruf von navigator.geolocation in
# der WebView (src/screens/Standort.tsx) ueberhaupt keine Rueckfrage: der
# Aufruf scheitert sofort, die App meldet "GPS-Freigabe fehlt", und unter
# Einstellungen > DataMetric360 > Ort fehlt die Auswahl "Beim Verwenden der
# App" - es bleiben nur "Nie" und "Beim Teilen". Nachgewiesen am 2026-08-31
# an der ausgelieferten App.ipa: kein einziger Location-Schluessel darin.
#
# Warum hier und nicht in Xcode: ios/ ist gitignored und wird von
# `npx cap add ios` aus Capacitors Vorlage neu erzeugt - die kennt keine
# Standort-Schluessel. Ein Eintrag von Hand in Xcode waere beim naechsten
# Erzeugen wieder verschwunden, genau wie es der Team-Kennung oben ergangen
# waere. Deshalb steht er versioniert hier und wird nach jedem `cap sync`
# neu gesetzt.
#
# Nur "WhenInUse": berechnet wird der Abstand zum geparkten Fahrzeug, waehrend
# jemand die Standort-Seite ansieht. Hintergrundortung braucht die App nicht,
# und was sie nicht braucht, soll sie auch nicht erfragen.
echo "==> Standort-Berechtigung eintragen"
PLIST="ios/App/App/Info.plist"
GRUND="Zeigt auf der Standort-Karte, wie weit du vom geparkten Fahrzeug entfernt bist."
if ! /usr/libexec/PlistBuddy -c "Set :NSLocationWhenInUseUsageDescription $GRUND" "$PLIST" 2>/dev/null; then
/usr/libexec/PlistBuddy -c "Add :NSLocationWhenInUseUsageDescription string $GRUND" "$PLIST"
fi
/usr/libexec/PlistBuddy -c "Print :NSLocationWhenInUseUsageDescription" "$PLIST" >/dev/null || {
echo "FEHLER: NSLocationWhenInUseUsageDescription liess sich nicht setzen." >&2
exit 1
}
# Kalender-Berechtigung, aus demselben Grund an derselben Stelle.
#
# Warum ZWEI Schluessel: seit iOS 17 gibt es einen eigenen Nur-Schreiben-Zugriff
# (NSCalendarsWriteOnlyAccessUsageDescription), aeltere Fassungen kennen nur den
# alten Sammelschluessel. Der System-Editor (native/KalenderTermin.swift) fragt
# auf iOS 17+ von sich aus gar nicht - die Schluessel sind die Absicherung fuer
# den Fall, dass EventKit doch einmal fragt. Fehlt der passende, beendet iOS die
# App wortlos, statt eine Rueckfrage zu zeigen.
echo "==> Kalender-Berechtigung eintragen"
KALENDERGRUND="Traegt Werkstatt- und Wechseltermine in deinen Kalender ein."
for SCHLUESSEL in NSCalendarsWriteOnlyAccessUsageDescription NSCalendarsUsageDescription; do
if ! /usr/libexec/PlistBuddy -c "Set :$SCHLUESSEL $KALENDERGRUND" "$PLIST" 2>/dev/null; then
/usr/libexec/PlistBuddy -c "Add :$SCHLUESSEL string $KALENDERGRUND" "$PLIST"
fi
/usr/libexec/PlistBuddy -c "Print :$SCHLUESSEL" "$PLIST" >/dev/null || {
echo "FEHLER: $SCHLUESSEL liess sich nicht setzen." >&2
exit 1
}
done
# Kamera, fuer den QR-Scanner der Ersteinrichtung.
#
# Gelesen wird der Code in der WebView (jsQR ueber getUserMedia), nicht von
# einem nativen Plugin - siehe den Kopf von src/screens/QrScanner.tsx, warum
# nicht GoogleMLKit. Dieser Schluessel ist trotzdem noetig: ohne ihn beendet
# iOS die App in dem Moment, in dem sie die Kamera anfordert.
echo "==> Kamera-Berechtigung eintragen"
KAMERAGRUND="Liest den QR-Code deines Zugangs-Tokens bei der Ersteinrichtung."
if ! /usr/libexec/PlistBuddy -c "Set :NSCameraUsageDescription $KAMERAGRUND" "$PLIST" 2>/dev/null; then
/usr/libexec/PlistBuddy -c "Add :NSCameraUsageDescription string $KAMERAGRUND" "$PLIST"
fi
/usr/libexec/PlistBuddy -c "Print :NSCameraUsageDescription" "$PLIST" >/dev/null || {
echo "FEHLER: NSCameraUsageDescription liess sich nicht setzen." >&2
exit 1
}
# Share-Erweiterung ins Xcode-Projekt haengen.
#
# Aus demselben Grund wie die Team-Kennung und der Standort-Schluessel oben:
# ios/ ist gitignored, also verschwindet jedes von Hand angelegte Ziel beim
# naechsten `npx cap add ios`. Die Quellen liegen versioniert unter native/,
# das Skript spielt sie bei jedem Bau wieder ein. Es ist idempotent - ein
# zweiter Lauf ueber ein fertiges Projekt aendert nichts.
#
# Schlaegt es fehl, bricht der ganze Bau ab: eine App ohne den Eintrag im
# Teilen-Blatt sieht fertig aus und ist es nicht, und der Unterschied faellt
# erst auf dem Geraet auf.
echo "==> Share-Erweiterung einrichten"
node scripts/ios-teilen-einrichten.mjs
# App-Symbol einspielen.
#
# Capacitors Vorlage bringt ein eigenes Platzhalter-Symbol mit, und ios/ ist
# gitignored - ein in Xcode eingesetztes Symbol waere beim naechsten
# `npx cap add ios` wieder weg. Die Vorlage liegt deshalb versioniert unter
# native/AppIcon.appiconset und wird hier ueber die von Capacitor erzeugte
# gelegt.
#
# Drei Ausfuehrungen, wie iOS 18 sie erwartet: hell mit eigenem Grund, dunkel
# und getoent ohne - dort legt das System seinen eigenen Grund darunter, und
# ein mitgeliefertes Schwarz bliebe darauf als Kasten sichtbar.
echo "==> App-Symbol einspielen"
SYMBOLZIEL="ios/App/App/Assets.xcassets/AppIcon.appiconset"
rm -rf "$SYMBOLZIEL"
mkdir -p "$SYMBOLZIEL"
cp -R native/AppIcon.appiconset/. "$SYMBOLZIEL"/
[ -f "$SYMBOLZIEL/Contents.json" ] || { echo "FEHLER: App-Symbol nicht eingespielt." >&2; exit 1; }
echo " hell, dunkel, getoent"
# Versionsnummer aus der manifest.json in die App schreiben.
#
# Capacitors Vorlage setzt 1.0/1 und laesst es dabei - zwei nacheinander
# aufgespielte .ipa sind auf dem Geraet dadurch nicht zu unterscheiden, und
# man weiss nicht, welche gerade drauf ist. Dieselbe Zahl, die schon Panel,
# OTA-Buendel und __APP_VERSION__ tragen (siehe VERSIONIERUNG.md): eine
# Quelle, kein zweiter Ort zum Vergessen.
echo "==> Versionsnummer eintragen"
VERSION="$(node -p "require('../custom_components/audi_dashboard/manifest.json').version")"
[ -n "$VERSION" ] || { echo "FEHLER: keine Version in der manifest.json." >&2; exit 1; }
if ! /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$PLIST" 2>/dev/null; then
/usr/libexec/PlistBuddy -c "Add :CFBundleShortVersionString string $VERSION" "$PLIST"
fi
BUILDNR="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$PLIST" 2>/dev/null || echo 1)"
# In der Info.plist der App steht dort KEINE Zahl, sondern der Platzhalter
# einer Build-Einstellung ($(CURRENT_PROJECT_VERSION), aus dem Capacitor-
# Geruest). Fuer das App-Ziel loest Xcode ihn zu 1 auf - das Ziel der
# Erweiterung kennt die Einstellung aber gar nicht, dort wurde daraus LEER,
# und ein Buendel ohne CFBundleVersion lehnt iOS beim Installieren ab
# ("kann nicht installiert werden"). Genau so ist die .ipa vom 04.09.2026
# unbrauchbar geworden - an der ausgelieferten Datei nachgemessen: App
# CFBundleVersion 1, Erweiterung gar keine. Deshalb hier nur eine echte Zahl
# weiterreichen, sonst die 1.
case "$BUILDNR" in
""|*'$('*|*[!0-9.]*) BUILDNR=1 ;;
esac
# Die Share-Erweiterung MUSS dieselben beiden Nummern tragen wie die App.
#
# Apples App Extension Programming Guide verlangt das ausdruecklich, und iOS
# haelt sich daran: stimmen CFBundleShortVersionString und CFBundleVersion
# nicht ueberein, registriert das System die Erweiterung nicht - sie taucht im
# Teilen-Blatt dann gar nicht erst auf, ohne jede Fehlermeldung. Genau das war
# in der .ipa vom 03.09.2026 der Fall: App 2026.9.3.3, Erweiterung 1.0 (an der
# ausgelieferten Datei nachgemessen, nicht vermutet - native/teilen/Info.plist
# traegt die 1.0 fest, und dieser Schritt schrieb bis dahin nur $PLIST).
ERW_PLIST="ios/App/DataMetric360Share/Info.plist"
if [ -f "$ERW_PLIST" ]; then
/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $VERSION" "$ERW_PLIST"
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILDNR" "$ERW_PLIST"
echo " $VERSION ($BUILDNR), App und Erweiterung"
else
echo " $VERSION ($BUILDNR) - Erweiterung nicht gefunden, nur die App"
fi
# Zwischengespeicherte Bereitstellungsprofile dieser App wegraeumen.
#
# `-allowProvisioningUpdates` zieht ein neues Profil nur, wenn kein passendes
# herumliegt - ein vorhandenes wird stillschweigend weiterbenutzt, auch wenn
# sich seither die Berechtigungen geaendert haben. Genau daran ist am
# 2026-08-29 eine Installation gescheitert ("Integritaet konnte nicht
# geprueft werden"): das eingebettete Profil kannte ein Geraet, das Team
# hatte inzwischen zwei. Mit der App-Gruppe der Share-Erweiterung ist
# derselbe Fall erneut eingetreten - jedes vor dem 2026-08-31 gezogene
# Profil kennt sie nicht.
#
# Weggeraeumt wird nur, was zu dieser App gehoert (Bundle-ID im entpackten
# Profil gesucht), nicht der ganze Ordner - dort liegen auch die Profile
# anderer Projekte. Mit --profile-behalten laesst sich der Schritt
# ueberspringen.
if [ "${1:-}" != "--profile-behalten" ]; then
echo "==> Alte Bereitstellungsprofile wegraeumen"
PROFILE="$HOME/Library/Developer/Xcode/UserData/Provisioning Profiles"
if [ -d "$PROFILE" ]; then
ANZAHL=0
for datei in "$PROFILE"/*.mobileprovision; do
[ -e "$datei" ] || continue
if security cms -D -i "$datei" 2>/dev/null | grep -qF "app.datametric360"; then
rm -f "$datei"
ANZAHL=$((ANZAHL + 1))
fi
done
echo " $ANZAHL entfernt - xcodebuild zieht sie gleich neu"
else
echo " keine vorhanden"
fi
else
echo "==> Bereitstellungsprofile bleiben (--profile-behalten)"
fi
echo "==> Archiv bauen und signieren (Team $TEAM)"
rm -rf "$AUSGABE"
mkdir -p "$AUSGABE"
xcodebuild \
-project ios/App/App.xcodeproj \
-scheme App \
-configuration Release \
-destination 'generic/platform=iOS' \
-archivePath "$ARCHIV" \
-allowProvisioningUpdates \
DEVELOPMENT_TEAM="$TEAM" \
archive
# Ad-hoc, nicht development: das ist die von Apple vorgesehene Art fuer die
# Installation ueber die Luft (siehe ios-luftweg.sh) und deckt den Weg per
# Kabel gleich mit ab. Sie verlangt ein Apple-Distribution-Zertifikat, das
# -allowProvisioningUpdates bei Bedarf selbst anlegt - moeglich erst seit der
# bezahlten Mitgliedschaft. Die Signatur laeuft damit ein Jahr.
cat > "$AUSGABE/ExportOptions.plist" <<PLIST
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key><string>ad-hoc</string>
<key>teamID</key><string>$TEAM</string>
<key>signingStyle</key><string>automatic</string>
<key>stripSwiftSymbols</key><true/>
</dict>
</plist>
PLIST
echo "==> .ipa exportieren"
xcodebuild -exportArchive \
-archivePath "$ARCHIV" \
-exportOptionsPlist "$AUSGABE/ExportOptions.plist" \
-exportPath "$AUSGABE" \
-allowProvisioningUpdates
# Am FERTIGEN Buendel gegenlesen, nicht am Projekt.
#
# Abschnitt AK der AGENTS.md: von fuenf Fehlern in der nativen Huelle haben
# vier einen erfolgreichen Archivlauf erzeugt und sind erst am entpackten
# .ipa aufgefallen. Geprueft wird hier genau das, was still schiefgehen kann:
# eine Erweiterung ohne Programm, ohne App-Gruppe, oder mit abweichender
# Versionsnummer (dann registriert iOS sie nicht und sie fehlt im
# Teilen-Blatt).
echo "==> Fertige .ipa gegenlesen"
PRUEF="$(mktemp -d)"
if unzip -q "$AUSGABE/App.ipa" -d "$PRUEF"; then
ERW="$PRUEF/Payload/App.app/PlugIns/DataMetric360Share.appex"
if [ ! -x "$ERW/DataMetric360Share" ] && [ ! -f "$ERW/DataMetric360Share" ]; then
echo " WARNUNG: die Erweiterung hat kein Programm - sie wird nichts tun."
fi
ERW_V="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$ERW/Info.plist" 2>/dev/null || echo '?')"
APP_V="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleShortVersionString' "$PRUEF/Payload/App.app/Info.plist" 2>/dev/null || echo '?')"
if [ "$ERW_V" != "$APP_V" ]; then
echo " WARNUNG: Erweiterung $ERW_V, App $APP_V - iOS registriert die"
echo " Erweiterung dann nicht, sie fehlt im Teilen-Blatt."
else
echo " Erweiterung und App tragen beide $APP_V"
fi
# CFBundleVersion ist Pflicht. Fehlt sie in EINEM der beiden Buendel,
# verweigert iOS die Installation der ganzen App - ohne Hinweis darauf,
# woran es liegt. Deshalb hart pruefen, nicht nur warnen.
for ZIEL in "$PRUEF/Payload/App.app" "$ERW"; do
BV="$(/usr/libexec/PlistBuddy -c 'Print :CFBundleVersion' "$ZIEL/Info.plist" 2>/dev/null || echo '')"
case "$BV" in
""|*'$('*)
echo " FEHLER: $(basename "$ZIEL") hat keine brauchbare CFBundleVersion ('$BV')." >&2
echo " iOS wuerde die Installation ablehnen - Bau abgebrochen." >&2
rm -rf "$PRUEF"
exit 1 ;;
esac
done
for ZIEL in "$PRUEF/Payload/App.app" "$ERW"; do
if ! codesign -d --entitlements :- "$ZIEL" 2>/dev/null | grep -q "group.app.datametric360"; then
echo " WARNUNG: $(basename "$ZIEL") ohne App-Gruppe - Teilen kann nichts uebergeben."
fi
done
# Eigene Plugins muessen in packageClassList stehen. Capacitor 8 sucht
# Plugin-Klassen NICHT im Programm (registerPlugins() in
# CapacitorBridge.swift liest nur diese Liste) - eine einkompilierte
# Klasse, die dort fehlt, existiert fuer die Bruecke nicht. Genau so kam
# die .ipa vom 04.09.2026 heraus: Zwischenablage und Kalendertermin waren
# im Programm und trotzdem tot.
KONF="$PRUEF/Payload/App.app/capacitor.config.json"
for KLASSE in $(grep -ho "@objc([A-Za-z0-9_]*)" "$APP/native"/*.swift | sed "s/@objc(//;s/)//"); do
if ! grep -q "$KLASSE" "$KONF" 2>/dev/null; then
echo " FEHLER: $KLASSE fehlt in packageClassList - das Plugin bliebe stumm." >&2
rm -rf "$PRUEF"
exit 1
fi
done
echo " packageClassList: eigene Plugins eingetragen"
fi
rm -rf "$PRUEF"
# Erst JETZT ablegen. auslieferung/ ist versioniert und wird ausgeliefert -
# eine Datei, die die Pruefung oben nicht besteht, darf dort nicht landen und
# schon gar nicht committet werden. (ios/ ist gitignored, sonst existierte das
# Ergebnis nur auf diesem einen Mac.)
mkdir -p "$APP/auslieferung"
cp "$AUSGABE/App.ipa" "$APP/auslieferung/App.ipa"
# Auslieferung ueber die Luft gleich mitschreiben.
#
# manifest.plist und die Installationsseite hingen bisher an einem zweiten,
# von Hand aufzurufenden Skript - und ohne sie ist die frische .ipa auf dem
# Telefon nicht installierbar, obwohl sie fertig im Repo liegt. Ein Schritt,
# den man vergessen kann, gehoert in den Bau.
#
# Die Basis-Adresse ist die des Gitea-Repos: es liefert die Dateien bereits
# ueber HTTPS mit oeffentlich vertrauenswuerdigem Zertifikat aus, was iOS
# fuer diesen Weg zwingend verlangt (siehe ios-luftweg.sh). Ueberschreibbar
# ueber LUFTWEG_BASIS, falls der Weg einmal ueber einen eigenen Host geht.
echo "==> Auslieferung ueber die Luft vorbereiten"
LUFTWEG_BASIS="${LUFTWEG_BASIS:-https://gitea.nothaft.cloud/paul/audi-app/raw/branch/main/companion-app/auslieferung}"
bash "$HIER/ios-luftweg.sh" "$LUFTWEG_BASIS" auslieferung
echo
echo "Fertig: $APP/auslieferung/App.ipa"
echo "Version: $VERSION"
echo
echo "Noch zu tun:"
echo " 1. git add companion-app/auslieferung && git commit && git push"
echo " (die .ipa und manifest.plist muessen im Repo liegen, sonst kann"
echo " das Telefon sie nicht laden)"
echo " 2. Auf dem iPhone in Safari oeffnen:"
echo " itms-services://?action=download-manifest&url=$LUFTWEG_BASIS/manifest.plist"
echo
echo "Per Kabel stattdessen: xcrun devicectl device install app --device <UDID> $APP/auslieferung/App.ipa"