Signierte iOS-App gebaut und Luftweg-Installation vorbereitet
Phase 10 Schritt 7 ist damit erledigt. auslieferung/App.ipa, 2,4 MB, Ad-hoc signiert mit "Apple Distribution: Paul Nothaft", Profil gueltig bis 29.08.2027, genau ein eingetragenes Geraet. Zwei Annahmen von heute frueh waren falsch und sind korrigiert: die bezahlte Mitgliedschaft stuft das bestehende Team hoch, statt ein neues anzulegen (die Kennung bleibt RMACS9VLS4), und der Export als Ad-hoc funktioniert einwandfrei. Neue Falle festgehalten: beim ersten Signieren fragt der Schluesselbund per Dialog um Erlaubnis. Bleibt der unbeantwortet, haengt xcodebuild wortlos und endet mit errSecInternalComponent. Die Diagnose steht in AGENTS.md, weil das Symptom von sich aus nirgendwohin zeigt. Neu: scripts/ios-luftweg.sh erzeugt manifest.plist, Installationsseite und Symbole fuer die Uebertragung ueber die Luft. Es verweigert eine Basis-Adresse ohne https, weil iOS sonst erst auf dem Telefon still scheitert. Offen bleibt der Host, der die Dateien ausliefert.
This commit is contained in:
@@ -4604,11 +4604,13 @@ via log (single `audi_dashboard` loader warning, no traceback).
|
||||
|
||||
---
|
||||
|
||||
## AI. iOS signing prepared, and the one thing that blocks it (2026-08-29)
|
||||
## AI. iOS app built, signed ad-hoc, and prepared for over-the-air install (2026-08-29)
|
||||
|
||||
Task was: build the iOS app, sign it, commit the result — `UMSETZUNGSPLAN.md` Phase 10 step 7, the
|
||||
last open item of that phase. **Result: everything except the signature itself is done and
|
||||
verified; the signature is blocked on a fact about the Apple account, not on this repo.**
|
||||
last open item of that phase. **Result: done.** A signed `.ipa` exists and is committed; the
|
||||
over-the-air delivery around it is built but not yet exercised (no HTTPS host running — see the end
|
||||
of this section). Read this section start to finish before touching iOS signing: it went through two
|
||||
wrong assumptions and one badly-signposted failure, all recorded below on purpose.
|
||||
|
||||
What was verified on this machine (Xcode 26.4, Node 22.20, `main` at `2026.8.28.18`):
|
||||
|
||||
@@ -4619,9 +4621,9 @@ What was verified on this machine (Xcode 26.4, Node 22.20, `main` at `2026.8.28.
|
||||
8.5 MB `App.app`. So the code, the plugins and the Swift-package graph are all fine on device;
|
||||
nothing but provisioning is missing.
|
||||
|
||||
**The blocker, verbatim from Apple:** `Communication with Apple failed: Your team has no devices
|
||||
from which to generate a provisioning profile.` The account authenticates fine (Xcode reached Apple
|
||||
and got a real answer, not an auth error) — team `RMACS9VLS4`, certificate
|
||||
**The first blocker (since resolved), verbatim from Apple:** `Communication with Apple failed: Your
|
||||
team has no devices from which to generate a provisioning profile.` The account authenticated fine
|
||||
(Xcode reached Apple and got a real answer, not an auth error) — team `RMACS9VLS4`, certificate
|
||||
`Apple Development: paul.nothaft@me.com (C9L892Z59P)`, valid until 2027-07-17. Apple issues a
|
||||
development profile only for **named devices**, and this team has none registered. No iPhone is
|
||||
connected (`xcrun devicectl list devices` → none) and no device was ever paired with this Mac (no
|
||||
@@ -4632,47 +4634,74 @@ machine with no phone attached:
|
||||
- an App-Store-method export needs no devices but produces an IPA that iOS refuses to sideload;
|
||||
- a locally `codesign`-ed `.app` without an embedded profile will not install either.
|
||||
|
||||
**Correction, same day: this is a FREE account, not a paid membership.** Xcode's own cache says so
|
||||
— `defaults read com.apple.dt.Xcode` → `IDEProvisioningTeamByIdentifier`:
|
||||
`teamName = "Paul Nothaft (Personal Team)"`, `teamType = "Personal Team"`,
|
||||
**`isFreeProvisioningTeam = 1`**. The note further up this file claiming "Paul has an Apple
|
||||
Developer Program" (section on the 2026-08-17 port) is therefore **wrong** and must not be relied
|
||||
on. Consequences, all confirmed against Apple's membership comparison and the free-provisioning
|
||||
limits:
|
||||
**Interlude, same day: the account was free at that moment.** Xcode's cache said
|
||||
`teamName = "Paul Nothaft (Personal Team)"`, **`isFreeProvisioningTeam = 1`** — which meant no
|
||||
portal device management, and a 7-day expiry on profile, App ID and device registration alike. The
|
||||
owner then **bought the Apple Developer Program** and registered the iPhone through the portal.
|
||||
|
||||
- **There is no portal route to register a device.** Certificates, Identifiers & Profiles device
|
||||
management is a paid-membership feature. A free team registers a device only by having it
|
||||
**physically connected to this Mac and trusted**; Xcode then does it via
|
||||
`-allowProvisioningUpdates`. So "add the UDID on the website" is not an option here.
|
||||
- **Everything expires after 7 days** — provisioning profile, App ID and device registration alike.
|
||||
The app stops launching and has to be rebuilt and reinstalled, forever, every week.
|
||||
- Ceilings: 3 devices per platform, 10 App IDs per 7 days.
|
||||
- Whether `-exportArchive` even yields a usable `.ipa` for a free team is **untested and doubtful**
|
||||
— free provisioning is built around "Run straight onto the connected device" from Xcode, not
|
||||
around exporting a redistributable archive. If the export step fails once a phone is attached,
|
||||
install directly instead of debugging the export.
|
||||
**Two things this taught, both worth keeping:**
|
||||
|
||||
**What to do (owner):** connect the iPhone by cable, tap "Trust", then run the script below. That
|
||||
is the only path with this account type. If the weekly re-install turns out to be intolerable, the
|
||||
paid Apple Developer Program (99 €/year) is what buys the 1-year signature, portal-side UDID
|
||||
registration without the phone present, and 100 devices — `UMSETZUNGSPLAN.md` Phase 10 already
|
||||
flagged this as an owner decision, and it is now a decision with a known answer on one side.
|
||||
1. **A paid enrolment upgrades the existing team; it does not create a new one.** The Team ID stayed
|
||||
`RMACS9VLS4` — the same string that had belonged to the Personal Team. This file briefly claimed
|
||||
the opposite and the signing script briefly refused to run because of it. Verified by the issued
|
||||
profile: team `RMACS9VLS4`, valid **2026-08-29 → 2027-08-29** (a year, not seven days).
|
||||
2. **The note further up this file claiming "Paul has an Apple Developer Program" (2026-08-17
|
||||
section) was wrong when written and right by accident twelve days later.** It is marked in place.
|
||||
|
||||
**`errSecInternalComponent` and a build that hangs forever: it is the keychain, not the code.** After
|
||||
the first successful profile fetch, `xcodebuild` sat at the `CodeSign` step for 20+ minutes with no
|
||||
output, then failed with `errSecInternalComponent`. Diagnosis that settled it, worth repeating
|
||||
because the symptom points nowhere useful on its own: `lsof -p <codesign-pid> -i` showed **no network
|
||||
sockets** (so not a timestamp-server stall, the other classic cause), and `sample <pid>` showed the
|
||||
stack parked in `SecKeyCreateSignature` → `SecCDSAKeyCopyOperationResult` → `mach_msg` — blocked on
|
||||
the security daemon, i.e. an unanswered **"allow codesign to use this key?" dialog** sitting on the
|
||||
GUI. Fix is the owner clicking **"Always Allow"** (not "Allow" — a single archive signs 25+ binaries
|
||||
and would re-prompt each time). Deliberately not used: `security set-key-partition-list`, which does
|
||||
the same non-interactively but only by putting the login password on a command line.
|
||||
|
||||
**Result: it worked.** `App.ipa`, 2.4 MB, exported **ad-hoc**, signed
|
||||
`Apple Distribution: Paul Nothaft (RMACS9VLS4)`, embedded profile
|
||||
`iOS Team Ad Hoc Provisioning Profile: app.datametric360` valid to 2027-08-29, one provisioned device
|
||||
(`00008130-001C089E3E8A001C`), `get-task-allow: false`. Committed to
|
||||
`companion-app/auslieferung/App.ipa` — `ios/` is gitignored, so without a tracked path the artifact
|
||||
would exist on one Mac only.
|
||||
|
||||
**Over-the-air install, built as `companion-app/scripts/ios-luftweg.sh <https-basis>`.** Emits
|
||||
`manifest.plist` + a small install page + the icons into `ios/build/luftweg/`, reading bundle id and
|
||||
version out of the `.ipa` rather than repeating them. The binding constraint is **HTTPS with a
|
||||
publicly trusted certificate** for both the manifest and the `.ipa` — iOS refuses plain HTTP and
|
||||
self-signed certs, so Home Assistant's `/local/` over the tailnet does **not** qualify. The fit for
|
||||
this project is `tailscale serve --bg <ordner>` (real Let's Encrypt cert on `*.ts.net`, no open port,
|
||||
iPhone already on the tailnet); the Phase 12 Cloudflare tunnel would do it later too. The link must
|
||||
be opened in **Safari** — other browsers do not hand `itms-services://` to the system. Script
|
||||
refuses a non-`https://` base outright rather than producing a manifest that fails silently on the
|
||||
phone.
|
||||
|
||||
**Still open at end of session:** this Mac is logged out of Tailscale (`BackendState: NeedsLogin`),
|
||||
so no host was serving yet and the over-the-air install is **built but not yet exercised end to end**.
|
||||
Also untouched: `MARKETING_VERSION` is still `1.0`/`1` from the Capacitor scaffold, so successive
|
||||
`.ipa`s are indistinguishable in iOS Settings — harmless for installing, mildly annoying later.
|
||||
|
||||
**What was built instead of a hand-clicked Xcode signature:** `companion-app/scripts/ios-signieren.sh`
|
||||
— build → `cap sync` → signed archive → `.ipa` export, one command. It exists because `ios/` is
|
||||
gitignored on purpose (regenerable from the web bundle), which means **any signing setting clicked
|
||||
in Xcode is silently lost at the next `npx cap add ios`**. A versioned script is the only place the
|
||||
team ID can survive. `APPLE_TEAM_ID` overrides it; output lands in `ios/build/App.ipa` (already
|
||||
covered by the `ios/` ignore, no build artifact enters git). Verified by running it end to end: it
|
||||
passes build, sync and archive setup and stops exactly at Apple's device requirement, nowhere else.
|
||||
team ID can survive (`APPLE_TEAM_ID` overrides it). It copies the finished `.ipa` to
|
||||
`auslieferung/`, which *is* tracked, for the same reason.
|
||||
|
||||
Deliberately **not** done: committing an unsigned `.ipa` as a stand-in. It is not what was asked
|
||||
for, it cannot be installed as-is, and a per-build ~6 MB binary would sit in the git history
|
||||
forever. The OTA path already covers "new UI without Xcode" (section above) — Xcode is only needed
|
||||
when native code or the signature itself changes.
|
||||
Deliberately **not** done: committing an unsigned `.ipa` as a stand-in while signing was still
|
||||
blocked. It could not have been installed, and it is not what was asked for. Once the signature
|
||||
worked, the real 2.4 MB artifact went in.
|
||||
|
||||
Phase 10 step 7 therefore stays **unchecked** in `UMSETZUNGSPLAN.md`: the app is not on the phone
|
||||
yet, and marking it done would be exactly the silent staleness this project keeps designing against.
|
||||
**Do not confuse the two things called "OTA" in this repo.** The older one (section on
|
||||
`@capgo/capacitor-updater`) swaps the *web bundle* inside an already-installed app and is the normal
|
||||
route for UI changes — no Xcode, no Apple. The new `ios-luftweg.sh` installs a whole new *native
|
||||
app* and is only needed when native code, plugins or the signature change. Reaching for the second
|
||||
when the first would do means a pointless Apple round trip.
|
||||
|
||||
Phase 10 step 7 is **ticked** in `UMSETZUNGSPLAN.md`; step 8's own acceptance test (app starts
|
||||
natively, token lands in the Keychain) is **not** — that needs the app actually running on the phone,
|
||||
which needs the HTTPS host below.
|
||||
|
||||
---
|
||||
|
||||
|
||||
+31
-21
@@ -424,12 +424,16 @@ nicht ein Riesencommit.
|
||||
> Offen bleibt allein das Signieren aufs eigene Gerät — das braucht das
|
||||
> angeschlossene iPhone und die Apple-ID des Besitzers.
|
||||
>
|
||||
> **2026-08-29:** Schritt 7 vorbereitet, aber nicht abschließbar. Der Gerätebau
|
||||
> (arm64, Release) läuft fehlerfrei durch, das Signieren scheitert
|
||||
> ausschließlich daran, dass dem Team `RMACS9VLS4` **kein Gerät** bekannt ist:
|
||||
> Apple erzeugt ein Development-Profil nur für konkrete UDIDs. Der ganze
|
||||
> Ablauf steckt jetzt in `companion-app/scripts/ios-signieren.sh` und läuft
|
||||
> bis genau zu diesem Punkt. Details in `AGENTS.md` Abschnitt AI.
|
||||
> **2026-08-29: Schritt 7 erledigt.** Der Besitzer hat das bezahlte
|
||||
> Entwicklerkonto gelöst und das iPhone im Portal registriert; die Team-Kennung
|
||||
> blieb dabei `RMACS9VLS4` (die bezahlte Mitgliedschaft stuft das bestehende
|
||||
> Team hoch, sie legt kein neues an). Ergebnis: `companion-app/auslieferung/App.ipa`,
|
||||
> 2,4 MB, **Ad-hoc** signiert mit `Apple Distribution: Paul Nothaft`, Profil
|
||||
> gültig bis 29.08.2027. Zwei Skripte tragen das:
|
||||
> `scripts/ios-signieren.sh` (bauen und signieren) und `scripts/ios-luftweg.sh`
|
||||
> (Manifest und Installationsseite für die Übertragung über die Luft).
|
||||
> Offen bleibt allein ein HTTPS-Host, der die Dateien ausliefert — siehe
|
||||
> Schritt 8. Fallstricke und Diagnosen in `AGENTS.md` Abschnitt AI.
|
||||
|
||||
**Ziel:** Die App läuft auf dem iPhone. Zwei Stufen — erst PWA (sofort nutzbar), dann Capacitor
|
||||
(Keychain + QR-Scan).
|
||||
@@ -456,21 +460,27 @@ nicht ein Riesencommit.
|
||||
(Offline-JS-QR-Bibliothek, keine Netzabfrage) als QR anzeigt. Inhalt des QR: JSON
|
||||
`{"url": "...", "token": "..."}`. Wenn das zusammen > 1 Tag Aufwand wird: weglassen —
|
||||
manuelles Einfügen ist die beschlossene, ausreichende Lösung.
|
||||
7. **iOS-Sideload:** statt Xcode von Hand jetzt `bash companion-app/scripts/ios-signieren.sh`
|
||||
(baut Webbündel, synchronisiert die Hülle, archiviert signiert, exportiert die `.ipa`).
|
||||
Die Team-Kennung steht im Skript, weil `ios/` gitignored ist und jede in Xcode geklickte
|
||||
Einstellung beim nächsten `npx cap add ios` verschwinden würde.
|
||||
⚠️ **Voraussetzung, die kein Skript herstellen kann:** das iPhone muss **per Kabel an diesem
|
||||
Mac hängen und vertraut sein**. Sonst: „Your team has no devices from which to generate a
|
||||
provisioning profile". Die UDID stattdessen auf developer.apple.com einzutragen geht **nicht** —
|
||||
das ist ein Recht der bezahlten Mitgliedschaft, und das Konto ist ein kostenloses Personal Team
|
||||
(`isFreeProvisioningTeam = 1`, belegt 2026-08-29, siehe `AGENTS.md` Abschnitt AI).
|
||||
⚠️ Entscheidungspunkt für den Besitzer, jetzt mit bekannter Faktenlage: das Konto **ist**
|
||||
kostenlos, die Signatur läuft also alle **7 Tage** ab (Profil, App-ID und Geräteeintrag
|
||||
gleichermaßen — App wöchentlich neu aufspielen). Ein bezahltes Entwicklerkonto (99 €/Jahr)
|
||||
macht daraus 1 Jahr und erlaubt die Geräteregistrierung ohne angeschlossenes Telefon.
|
||||
Bei 7-Tage-Schmerz ist die PWA-Stufe die Alltagslösung, Capacitor das Extra für Keychain/QR.
|
||||
8. ✅ Fertig wenn: App startet nativ auf dem iPhone, Token liegt im Keychain (Test: App löschen und
|
||||
7. ✅ **iOS-Sideload (erledigt 2026-08-29):** `bash companion-app/scripts/ios-signieren.sh`
|
||||
baut Webbündel, synchronisiert die Hülle, archiviert signiert und exportiert die `.ipa`
|
||||
nach `companion-app/auslieferung/`. Die Team-Kennung steht im Skript, weil `ios/` gitignored
|
||||
ist und jede in Xcode geklickte Einstellung beim nächsten `npx cap add ios` verschwinden würde.
|
||||
Der Entscheidungspunkt Kostenlos-vs-Bezahlt ist entschieden: **bezahltes Konto**, damit
|
||||
Signatur ein Jahr gültig statt sieben Tage, und Geräteregistrierung über das Portal ohne
|
||||
angeschlossenes Telefon.
|
||||
⚠️ **Falle, die viel Zeit kostet:** beim ersten Signieren fragt der Schlüsselbund per Dialog
|
||||
um Erlaubnis. Wird der nicht beantwortet, hängt `xcodebuild` wortlos minutenlang und endet
|
||||
mit `errSecInternalComponent`. Im Dialog **„Immer erlauben"** wählen — ein Archiv signiert
|
||||
über 25 Binärdateien und würde sonst jedes Mal erneut fragen.
|
||||
8. **Übertragung über die Luft** (statt Kabel): `bash companion-app/scripts/ios-luftweg.sh
|
||||
https://<host>` erzeugt `manifest.plist`, Installationsseite und Symbole in
|
||||
`ios/build/luftweg/`. Der Ordner muss über **HTTPS mit öffentlich vertrauenswürdigem
|
||||
Zertifikat** ausgeliefert werden — iOS lehnt einfaches HTTP und selbstsignierte Zertifikate
|
||||
ab, Home Assistant unter `/local/` genügt also **nicht**. Passend: `tailscale serve --bg
|
||||
<ordner>` (echtes Let's-Encrypt-Zertifikat auf `*.ts.net`, kein offener Port, iPhone ohnehin
|
||||
im Tailnet); der Cloudflare-Tunnel aus Phase 12 täte es später ebenso. Link auf dem iPhone in
|
||||
**Safari** öffnen — andere Browser reichen `itms-services://` nicht ans System weiter.
|
||||
⏳ Offen: dieser Mac ist bei Tailscale abgemeldet, es läuft also noch kein Host.
|
||||
9. ✅ Fertig wenn: App startet nativ auf dem iPhone, Token liegt im Keychain (Test: App löschen und
|
||||
neu installieren → Token weg; Backup/Restore-Verhalten notieren), QR-Einrichtung funktioniert
|
||||
oder ist dokumentiert entfallen.
|
||||
9. Committen; `AGENTS.md` Block A Punkte QR/Secure-Storage abhaken.
|
||||
|
||||
+15
-6
@@ -68,6 +68,17 @@ veröffentlicht**, und dieses Repository bleibt privat. `../design-system/`
|
||||
kommt bewusst ohne diese Dateien aus, weil es nach außen hochgeladen wird —
|
||||
dort niemals Markendateien ablegen.
|
||||
|
||||
## Die signierte App
|
||||
|
||||
`auslieferung/App.ipa` ist der fertige, **Ad-hoc signierte** Stand (Team
|
||||
`RMACS9VLS4`, Profil gültig bis 29.08.2027, nur für das eingetragene iPhone).
|
||||
Neu bauen: `bash scripts/ios-signieren.sh` - baut Webbündel, synchronisiert die
|
||||
Hülle, signiert und legt die `.ipa` wieder hier ab.
|
||||
|
||||
Beim ersten Signieren fragt der Schlüsselbund um Erlaubnis. Dort **„Immer
|
||||
erlauben"** wählen, sonst hängt `xcodebuild` wortlos und endet nach Minuten mit
|
||||
`errSecInternalComponent`.
|
||||
|
||||
## Noch offen
|
||||
|
||||
- Native Hülle selbst: `ios/`/`android/` (von `npx cap add` erzeugt, absichtlich
|
||||
@@ -76,12 +87,10 @@ dort niemals Markendateien ablegen.
|
||||
(Keychain/Keystore) ist dagegen bereits vollständig angebunden
|
||||
(`src/api/ablageNativ.ts`, von `main.tsx` aktiviert) und wird automatisch
|
||||
aktiv, sobald die Hülle existiert
|
||||
- Signierte `.ipa`: `scripts/ios-signieren.sh` baut und signiert in einem
|
||||
Durchlauf, **sobald das iPhone per Kabel am Mac hängt und vertraut ist**.
|
||||
Vorher bricht Apple den Profilabruf ab - das ist die einzige verbleibende
|
||||
Hürde, der Gerätebau selbst läuft fehlerfrei durch. Das Konto ist ein
|
||||
kostenloses Personal Team: die UDID lässt sich nicht auf developer.apple.com
|
||||
nachtragen, und die Signatur verfällt alle 7 Tage
|
||||
- Übertragung über die Luft: `scripts/ios-luftweg.sh https://<host>` erzeugt
|
||||
Manifest und Installationsseite; es fehlt nur noch ein Host, der den Ordner
|
||||
über **HTTPS mit gültigem Zertifikat** ausliefert (`tailscale serve`
|
||||
angedacht, dieser Mac ist dort noch abgemeldet)
|
||||
- QR-Einrichtung als Alternative zum Einfügen des Tokens
|
||||
- Live-Ansicht der laufenden Fahrt: gebaut, aber über
|
||||
`src/funktionen.ts` abgeschaltet, bis der FMM003 echte Werte liefert
|
||||
|
||||
Binary file not shown.
Executable
+124
@@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env bash
|
||||
# Bereitet die Installation ueber die Luft vor: legt neben der signierten .ipa
|
||||
# die manifest.plist und eine kleine Installationsseite ab.
|
||||
#
|
||||
# bash scripts/ios-luftweg.sh https://mein-mac.tailnetname.ts.net
|
||||
#
|
||||
# Warum das noetig ist: iOS installiert eine App nur dann ueber die Luft, wenn
|
||||
# ein itms-services-Link auf eine manifest.plist zeigt, die wiederum die
|
||||
# Adresse der .ipa enthaelt. Beide Adressen muessen ueber **HTTPS mit einem
|
||||
# oeffentlich vertrauenswuerdigen Zertifikat** erreichbar sein - einfaches HTTP
|
||||
# und selbstsignierte Zertifikate lehnt iOS kommentarlos ab. Home Assistant
|
||||
# unter /local/ genuegt dafuer nicht, das laeuft im Tailnet unverschluesselt.
|
||||
#
|
||||
# Passend dazu und ohne offenen Port: `tailscale serve` liefert ein echtes
|
||||
# Let's-Encrypt-Zertifikat auf <maschine>.<tailnet>.ts.net aus. Voraussetzung
|
||||
# ist, dass in der Tailscale-Verwaltung HTTPS-Zertifikate aktiviert sind.
|
||||
#
|
||||
# tailscale serve --bg ios/build/luftweg
|
||||
#
|
||||
# Danach den ausgegebenen Link auf dem iPhone in **Safari** oeffnen (andere
|
||||
# Browser reichen itms-services nicht an das System weiter).
|
||||
set -euo pipefail
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "Aufruf: bash scripts/ios-luftweg.sh <basis-url>" >&2
|
||||
echo "Beispiel: bash scripts/ios-luftweg.sh https://mein-mac.tailnetname.ts.net" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BASIS="${1%/}"
|
||||
if [ "${BASIS#https://}" = "$BASIS" ]; then
|
||||
echo "FEHLER: Die Basis-Adresse muss mit https:// beginnen." >&2
|
||||
echo "iOS verweigert die Installation ueber die Luft ueber unverschluesseltes HTTP." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
HIER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
APP="$(dirname "$HIER")"
|
||||
IPA="$APP/auslieferung/App.ipa"
|
||||
ZIEL="$APP/ios/build/luftweg"
|
||||
|
||||
[ -f "$IPA" ] || { echo "FEHLER: $IPA fehlt - erst scripts/ios-signieren.sh laufen lassen." >&2; exit 1; }
|
||||
|
||||
# Version aus der .ipa selbst lesen statt sie hier zu wiederholen: zwei Quellen
|
||||
# fuer dieselbe Angabe waeren eine zu viel.
|
||||
AUSPACK="$(mktemp -d)"
|
||||
trap 'rm -rf "$AUSPACK"' EXIT
|
||||
unzip -q "$IPA" -d "$AUSPACK"
|
||||
PLIST="$(find "$AUSPACK/Payload" -maxdepth 2 -name Info.plist | head -1)"
|
||||
KENNUNG="$(/usr/libexec/PlistBuddy -c "Print :CFBundleIdentifier" "$PLIST")"
|
||||
VERSION="$(/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString" "$PLIST")"
|
||||
|
||||
rm -rf "$ZIEL"
|
||||
mkdir -p "$ZIEL"
|
||||
cp "$IPA" "$ZIEL/App.ipa"
|
||||
cp "$APP/public/symbol-192.png" "$ZIEL/symbol-192.png"
|
||||
cp "$APP/public/symbol-512.png" "$ZIEL/symbol-512.png"
|
||||
|
||||
cat > "$ZIEL/manifest.plist" <<PLISTENDE
|
||||
<?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>items</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>assets</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>kind</key><string>software-package</string>
|
||||
<key>url</key><string>$BASIS/App.ipa</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>display-image</string>
|
||||
<key>url</key><string>$BASIS/symbol-192.png</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>kind</key><string>full-size-image</string>
|
||||
<key>url</key><string>$BASIS/symbol-512.png</string>
|
||||
</dict>
|
||||
</array>
|
||||
<key>metadata</key>
|
||||
<dict>
|
||||
<key>bundle-identifier</key><string>$KENNUNG</string>
|
||||
<key>bundle-version</key><string>$VERSION</string>
|
||||
<key>kind</key><string>software</string>
|
||||
<key>title</key><string>DataMetric360</string>
|
||||
</dict>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
PLISTENDE
|
||||
|
||||
# Das kaufmaennische Und muss in HTML maskiert werden, sonst schneidet Safari
|
||||
# den Link hinter dem url-Parameter ab.
|
||||
cat > "$ZIEL/index.html" <<HTMLENDE
|
||||
<!doctype html>
|
||||
<html lang="de">
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>DataMetric360 installieren</title>
|
||||
<style>
|
||||
body { font-family: -apple-system, sans-serif; background: #161b23; color: #eef1f6;
|
||||
margin: 0; display: grid; place-items: center; min-height: 100vh; }
|
||||
main { text-align: center; padding: 2rem; }
|
||||
h1 { font-weight: 300; font-size: 1.5rem; }
|
||||
a { display: inline-block; margin-top: 1.5rem; padding: 0.9rem 2rem; border-radius: 999px;
|
||||
background: #eef1f6; color: #161b23; text-decoration: none; font-size: 1.1rem; }
|
||||
p { color: #8a94a3; font-size: 0.9rem; line-height: 1.5; }
|
||||
</style>
|
||||
<main>
|
||||
<h1>DataMetric360</h1>
|
||||
<p>Version $VERSION</p>
|
||||
<a href="itms-services://?action=download-manifest&url=$BASIS/manifest.plist">Installieren</a>
|
||||
<p>In Safari oeffnen. Nach dem Antippen fragt iOS einmal nach,<br>danach erscheint die App auf dem Startbildschirm.</p>
|
||||
</main>
|
||||
</html>
|
||||
HTMLENDE
|
||||
|
||||
echo "Ordner fertig: $ZIEL"
|
||||
echo
|
||||
echo "Ausliefern: tailscale serve --bg $ZIEL"
|
||||
echo "Auf dem iPhone in Safari oeffnen: $BASIS/"
|
||||
@@ -32,20 +32,12 @@ set -euo pipefail
|
||||
|
||||
HIER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
APP="$(dirname "$HIER")"
|
||||
# Absichtlich ohne Vorgabewert. Hier stand bis 2026-08-29 die Kennung des
|
||||
# kostenlosen Personal Teams (RMACS9VLS4). Mit dem Wechsel auf eine bezahlte
|
||||
# Mitgliedschaft entsteht ein *neues* Team mit eigener Kennung - der alte Wert
|
||||
# wuerde also still weiter mit dem kostenlosen Team signieren (7-Tage-Ablauf)
|
||||
# statt mit dem bezahlten. Lieber ein klarer Abbruch als eine App, die nach
|
||||
# einer Woche wortlos stirbt.
|
||||
TEAM="${APPLE_TEAM_ID:-}"
|
||||
if [ -z "$TEAM" ]; then
|
||||
echo "FEHLER: APPLE_TEAM_ID ist nicht gesetzt." >&2
|
||||
echo "Die Team-Kennung steht auf developer.apple.com/account unter" >&2
|
||||
echo "'Membership details' (10 Zeichen, z. B. A1B2C3D4E5). Dann:" >&2
|
||||
echo " APPLE_TEAM_ID=XXXXXXXXXX bash scripts/ios-signieren.sh" >&2
|
||||
exit 1
|
||||
fi
|
||||
# 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"
|
||||
|
||||
@@ -74,15 +66,17 @@ xcodebuild \
|
||||
DEVELOPMENT_TEAM="$TEAM" \
|
||||
archive
|
||||
|
||||
# Die Exportart heisst seit Xcode 16 offiziell "debugging"; "development" ist
|
||||
# weiterhin als Synonym gueltig und funktioniert auch mit aelteren Xcode-
|
||||
# Versionen, deshalb bleibt es hier stehen.
|
||||
# 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>development</string>
|
||||
<key>method</key><string>ad-hoc</string>
|
||||
<key>teamID</key><string>$TEAM</string>
|
||||
<key>signingStyle</key><string>automatic</string>
|
||||
<key>stripSwiftSymbols</key><true/>
|
||||
@@ -97,6 +91,12 @@ xcodebuild -exportArchive \
|
||||
-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"
|
||||
|
||||
echo
|
||||
echo "Fertig: $AUSGABE/App.ipa"
|
||||
echo "Aufs Geraet: xcrun devicectl device install app --device <UDID> $AUSGABE/App.ipa"
|
||||
echo "Fertig: $APP/auslieferung/App.ipa"
|
||||
echo "Ueber die Luft: bash scripts/ios-luftweg.sh https://<host>"
|
||||
echo "Per Kabel: xcrun devicectl device install app --device <UDID> $APP/auslieferung/App.ipa"
|
||||
|
||||
Reference in New Issue
Block a user