6016053a92
- Kalendertermine gehen nativ ueber EventKit statt ueber das Teilen-Blatt:
Apples Kalender meldet sich beim System gar nicht als Teilen-Ziel an, ein
anderes Dateiformat haette also nie geholfen (Abschnitt BS).
- Der Rueckblick stoesst jetzt selbst das Screening an, wenn er Fahrten
angelegt hat - sonst blieb eine importierte Fahrt ohne Ort liegen, solange
das Fahrzeug steht (Abschnitt BT).
- Fahrzeugfotos werden vor dem Upload im Browser auf 2000 px/WebP gebracht.
Gemessen: 3.099.022 -> 325.994 Bytes. Damit ist die Nachrichtengrenze der
WebSocket-Verbindung kein Thema mehr ("connection lost" auf der realen
Instanz). Formate, die der Browser weder umwandeln noch anzeigen kann,
werden mit klarer Meldung abgelehnt statt roh gespeichert.
- Zwei Wechsel des Fahrtsignals in derselben Sekunde kosteten eine ganze
Fahrt: das "on" ueberholte den noch laufenden Beende-Vorgang und fiel durch
beide Zweige. Neue asyncio-Sperre plus Regressionstest, der ohne sie
nachweislich rot ist (Abschnitt BU).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
282 lines
13 KiB
Bash
Executable File
282 lines
13 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 vergessenes `git pull` waere
|
|
# damit ein stiller Veraltungsfehler wie schon mehrfach an anderer Stelle in
|
|
# diesem Projekt (Cache-Busting, OTA-Buendel-Abgleich, VERSIONIERUNG.md) - hier
|
|
# stattdessen ein lauter Abbruch statt eines optisch/inhaltlich alten Builds.
|
|
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)"
|
|
if [ "$LOKAL" != "$FERN" ]; then
|
|
echo "FEHLER: Lokaler Stand ($LOKAL) weicht von origin/main ($FERN) ab." >&2
|
|
echo "Erst 'git pull' ausfuehren - sonst signiert dieses Skript einen veralteten Stand." >&2
|
|
exit 1
|
|
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
|
|
|
|
# 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
|
|
echo " $VERSION"
|
|
|
|
# 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
|
|
|
|
# Die fertige .ipa liegt versioniert unter auslieferung/, weil ios/ gitignored
|
|
# ist und das Ergebnis sonst nur auf diesem einen Mac existierte.
|
|
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"
|