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
|
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
|
last open item of that phase. **Result: done.** A signed `.ipa` exists and is committed; the
|
||||||
verified; the signature is blocked on a fact about the Apple account, not on this repo.**
|
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`):
|
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;
|
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.
|
nothing but provisioning is missing.
|
||||||
|
|
||||||
**The blocker, verbatim from Apple:** `Communication with Apple failed: Your team has no devices
|
**The first blocker (since resolved), verbatim from Apple:** `Communication with Apple failed: Your
|
||||||
from which to generate a provisioning profile.` The account authenticates fine (Xcode reached Apple
|
team has no devices from which to generate a provisioning profile.` The account authenticated fine
|
||||||
and got a real answer, not an auth error) — team `RMACS9VLS4`, certificate
|
(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
|
`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
|
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
|
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;
|
- 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.
|
- 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
|
**Interlude, same day: the account was free at that moment.** Xcode's cache said
|
||||||
— `defaults read com.apple.dt.Xcode` → `IDEProvisioningTeamByIdentifier`:
|
`teamName = "Paul Nothaft (Personal Team)"`, **`isFreeProvisioningTeam = 1`** — which meant no
|
||||||
`teamName = "Paul Nothaft (Personal Team)"`, `teamType = "Personal Team"`,
|
portal device management, and a 7-day expiry on profile, App ID and device registration alike. The
|
||||||
**`isFreeProvisioningTeam = 1`**. The note further up this file claiming "Paul has an Apple
|
owner then **bought the Apple Developer Program** and registered the iPhone through the portal.
|
||||||
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:
|
|
||||||
|
|
||||||
- **There is no portal route to register a device.** Certificates, Identifiers & Profiles device
|
**Two things this taught, both worth keeping:**
|
||||||
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.
|
|
||||||
|
|
||||||
**What to do (owner):** connect the iPhone by cable, tap "Trust", then run the script below. That
|
1. **A paid enrolment upgrades the existing team; it does not create a new one.** The Team ID stayed
|
||||||
is the only path with this account type. If the weekly re-install turns out to be intolerable, the
|
`RMACS9VLS4` — the same string that had belonged to the Personal Team. This file briefly claimed
|
||||||
paid Apple Developer Program (99 €/year) is what buys the 1-year signature, portal-side UDID
|
the opposite and the signing script briefly refused to run because of it. Verified by the issued
|
||||||
registration without the phone present, and 100 devices — `UMSETZUNGSPLAN.md` Phase 10 already
|
profile: team `RMACS9VLS4`, valid **2026-08-29 → 2027-08-29** (a year, not seven days).
|
||||||
flagged this as an owner decision, and it is now a decision with a known answer on one side.
|
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`
|
**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
|
— 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
|
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
|
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
|
team ID can survive (`APPLE_TEAM_ID` overrides it). It copies the finished `.ipa` to
|
||||||
covered by the `ios/` ignore, no build artifact enters git). Verified by running it end to end: it
|
`auslieferung/`, which *is* tracked, for the same reason.
|
||||||
passes build, sync and archive setup and stops exactly at Apple's device requirement, nowhere else.
|
|
||||||
|
|
||||||
Deliberately **not** done: committing an unsigned `.ipa` as a stand-in. It is not what was asked
|
Deliberately **not** done: committing an unsigned `.ipa` as a stand-in while signing was still
|
||||||
for, it cannot be installed as-is, and a per-build ~6 MB binary would sit in the git history
|
blocked. It could not have been installed, and it is not what was asked for. Once the signature
|
||||||
forever. The OTA path already covers "new UI without Xcode" (section above) — Xcode is only needed
|
worked, the real 2.4 MB artifact went in.
|
||||||
when native code or the signature itself changes.
|
|
||||||
|
|
||||||
Phase 10 step 7 therefore stays **unchecked** in `UMSETZUNGSPLAN.md`: the app is not on the phone
|
**Do not confuse the two things called "OTA" in this repo.** The older one (section on
|
||||||
yet, and marking it done would be exactly the silent staleness this project keeps designing against.
|
`@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
|
> Offen bleibt allein das Signieren aufs eigene Gerät — das braucht das
|
||||||
> angeschlossene iPhone und die Apple-ID des Besitzers.
|
> angeschlossene iPhone und die Apple-ID des Besitzers.
|
||||||
>
|
>
|
||||||
> **2026-08-29:** Schritt 7 vorbereitet, aber nicht abschließbar. Der Gerätebau
|
> **2026-08-29: Schritt 7 erledigt.** Der Besitzer hat das bezahlte
|
||||||
> (arm64, Release) läuft fehlerfrei durch, das Signieren scheitert
|
> Entwicklerkonto gelöst und das iPhone im Portal registriert; die Team-Kennung
|
||||||
> ausschließlich daran, dass dem Team `RMACS9VLS4` **kein Gerät** bekannt ist:
|
> blieb dabei `RMACS9VLS4` (die bezahlte Mitgliedschaft stuft das bestehende
|
||||||
> Apple erzeugt ein Development-Profil nur für konkrete UDIDs. Der ganze
|
> Team hoch, sie legt kein neues an). Ergebnis: `companion-app/auslieferung/App.ipa`,
|
||||||
> Ablauf steckt jetzt in `companion-app/scripts/ios-signieren.sh` und läuft
|
> 2,4 MB, **Ad-hoc** signiert mit `Apple Distribution: Paul Nothaft`, Profil
|
||||||
> bis genau zu diesem Punkt. Details in `AGENTS.md` Abschnitt AI.
|
> 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
|
**Ziel:** Die App läuft auf dem iPhone. Zwei Stufen — erst PWA (sofort nutzbar), dann Capacitor
|
||||||
(Keychain + QR-Scan).
|
(Keychain + QR-Scan).
|
||||||
@@ -456,21 +460,27 @@ nicht ein Riesencommit.
|
|||||||
(Offline-JS-QR-Bibliothek, keine Netzabfrage) als QR anzeigt. Inhalt des QR: JSON
|
(Offline-JS-QR-Bibliothek, keine Netzabfrage) als QR anzeigt. Inhalt des QR: JSON
|
||||||
`{"url": "...", "token": "..."}`. Wenn das zusammen > 1 Tag Aufwand wird: weglassen —
|
`{"url": "...", "token": "..."}`. Wenn das zusammen > 1 Tag Aufwand wird: weglassen —
|
||||||
manuelles Einfügen ist die beschlossene, ausreichende Lösung.
|
manuelles Einfügen ist die beschlossene, ausreichende Lösung.
|
||||||
7. **iOS-Sideload:** statt Xcode von Hand jetzt `bash companion-app/scripts/ios-signieren.sh`
|
7. ✅ **iOS-Sideload (erledigt 2026-08-29):** `bash companion-app/scripts/ios-signieren.sh`
|
||||||
(baut Webbündel, synchronisiert die Hülle, archiviert signiert, exportiert die `.ipa`).
|
baut Webbündel, synchronisiert die Hülle, archiviert signiert und exportiert die `.ipa`
|
||||||
Die Team-Kennung steht im Skript, weil `ios/` gitignored ist und jede in Xcode geklickte
|
nach `companion-app/auslieferung/`. Die Team-Kennung steht im Skript, weil `ios/` gitignored
|
||||||
Einstellung beim nächsten `npx cap add ios` verschwinden würde.
|
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
|
Der Entscheidungspunkt Kostenlos-vs-Bezahlt ist entschieden: **bezahltes Konto**, damit
|
||||||
Mac hängen und vertraut sein**. Sonst: „Your team has no devices from which to generate a
|
Signatur ein Jahr gültig statt sieben Tage, und Geräteregistrierung über das Portal ohne
|
||||||
provisioning profile". Die UDID stattdessen auf developer.apple.com einzutragen geht **nicht** —
|
angeschlossenes Telefon.
|
||||||
das ist ein Recht der bezahlten Mitgliedschaft, und das Konto ist ein kostenloses Personal Team
|
⚠️ **Falle, die viel Zeit kostet:** beim ersten Signieren fragt der Schlüsselbund per Dialog
|
||||||
(`isFreeProvisioningTeam = 1`, belegt 2026-08-29, siehe `AGENTS.md` Abschnitt AI).
|
um Erlaubnis. Wird der nicht beantwortet, hängt `xcodebuild` wortlos minutenlang und endet
|
||||||
⚠️ Entscheidungspunkt für den Besitzer, jetzt mit bekannter Faktenlage: das Konto **ist**
|
mit `errSecInternalComponent`. Im Dialog **„Immer erlauben"** wählen — ein Archiv signiert
|
||||||
kostenlos, die Signatur läuft also alle **7 Tage** ab (Profil, App-ID und Geräteeintrag
|
über 25 Binärdateien und würde sonst jedes Mal erneut fragen.
|
||||||
gleichermaßen — App wöchentlich neu aufspielen). Ein bezahltes Entwicklerkonto (99 €/Jahr)
|
8. **Übertragung über die Luft** (statt Kabel): `bash companion-app/scripts/ios-luftweg.sh
|
||||||
macht daraus 1 Jahr und erlaubt die Geräteregistrierung ohne angeschlossenes Telefon.
|
https://<host>` erzeugt `manifest.plist`, Installationsseite und Symbole in
|
||||||
Bei 7-Tage-Schmerz ist die PWA-Stufe die Alltagslösung, Capacitor das Extra für Keychain/QR.
|
`ios/build/luftweg/`. Der Ordner muss über **HTTPS mit öffentlich vertrauenswürdigem
|
||||||
8. ✅ Fertig wenn: App startet nativ auf dem iPhone, Token liegt im Keychain (Test: App löschen und
|
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
|
neu installieren → Token weg; Backup/Restore-Verhalten notieren), QR-Einrichtung funktioniert
|
||||||
oder ist dokumentiert entfallen.
|
oder ist dokumentiert entfallen.
|
||||||
9. Committen; `AGENTS.md` Block A Punkte QR/Secure-Storage abhaken.
|
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 —
|
kommt bewusst ohne diese Dateien aus, weil es nach außen hochgeladen wird —
|
||||||
dort niemals Markendateien ablegen.
|
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
|
## Noch offen
|
||||||
|
|
||||||
- Native Hülle selbst: `ios/`/`android/` (von `npx cap add` erzeugt, absichtlich
|
- 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
|
(Keychain/Keystore) ist dagegen bereits vollständig angebunden
|
||||||
(`src/api/ablageNativ.ts`, von `main.tsx` aktiviert) und wird automatisch
|
(`src/api/ablageNativ.ts`, von `main.tsx` aktiviert) und wird automatisch
|
||||||
aktiv, sobald die Hülle existiert
|
aktiv, sobald die Hülle existiert
|
||||||
- Signierte `.ipa`: `scripts/ios-signieren.sh` baut und signiert in einem
|
- Übertragung über die Luft: `scripts/ios-luftweg.sh https://<host>` erzeugt
|
||||||
Durchlauf, **sobald das iPhone per Kabel am Mac hängt und vertraut ist**.
|
Manifest und Installationsseite; es fehlt nur noch ein Host, der den Ordner
|
||||||
Vorher bricht Apple den Profilabruf ab - das ist die einzige verbleibende
|
über **HTTPS mit gültigem Zertifikat** ausliefert (`tailscale serve`
|
||||||
Hürde, der Gerätebau selbst läuft fehlerfrei durch. Das Konto ist ein
|
angedacht, dieser Mac ist dort noch abgemeldet)
|
||||||
kostenloses Personal Team: die UDID lässt sich nicht auf developer.apple.com
|
|
||||||
nachtragen, und die Signatur verfällt alle 7 Tage
|
|
||||||
- QR-Einrichtung als Alternative zum Einfügen des Tokens
|
- QR-Einrichtung als Alternative zum Einfügen des Tokens
|
||||||
- Live-Ansicht der laufenden Fahrt: gebaut, aber über
|
- Live-Ansicht der laufenden Fahrt: gebaut, aber über
|
||||||
`src/funktionen.ts` abgeschaltet, bis der FMM003 echte Werte liefert
|
`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)"
|
HIER="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
APP="$(dirname "$HIER")"
|
APP="$(dirname "$HIER")"
|
||||||
# Absichtlich ohne Vorgabewert. Hier stand bis 2026-08-29 die Kennung des
|
# Dieselbe Kennung wie zu Zeiten des kostenlosen Kontos: die bezahlte
|
||||||
# kostenlosen Personal Teams (RMACS9VLS4). Mit dem Wechsel auf eine bezahlte
|
# Mitgliedschaft hat das bestehende Team hochgestuft, statt ein neues
|
||||||
# Mitgliedschaft entsteht ein *neues* Team mit eigener Kennung - der alte Wert
|
# anzulegen. Zwischenzeitlich stand hier das Gegenteil - widerlegt am
|
||||||
# wuerde also still weiter mit dem kostenlosen Team signieren (7-Tage-Ablauf)
|
# 2026-08-29 durch das ausgestellte Profil (Team RMACS9VLS4, Laufzeit ein
|
||||||
# statt mit dem bezahlten. Lieber ein klarer Abbruch als eine App, die nach
|
# Jahr statt sieben Tage). Ueberschreibbar ueber APPLE_TEAM_ID.
|
||||||
# einer Woche wortlos stirbt.
|
TEAM="${APPLE_TEAM_ID:-RMACS9VLS4}"
|
||||||
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
|
|
||||||
AUSGABE="$APP/ios/build"
|
AUSGABE="$APP/ios/build"
|
||||||
ARCHIV="$AUSGABE/DataMetric360.xcarchive"
|
ARCHIV="$AUSGABE/DataMetric360.xcarchive"
|
||||||
|
|
||||||
@@ -74,15 +66,17 @@ xcodebuild \
|
|||||||
DEVELOPMENT_TEAM="$TEAM" \
|
DEVELOPMENT_TEAM="$TEAM" \
|
||||||
archive
|
archive
|
||||||
|
|
||||||
# Die Exportart heisst seit Xcode 16 offiziell "debugging"; "development" ist
|
# Ad-hoc, nicht development: das ist die von Apple vorgesehene Art fuer die
|
||||||
# weiterhin als Synonym gueltig und funktioniert auch mit aelteren Xcode-
|
# Installation ueber die Luft (siehe ios-luftweg.sh) und deckt den Weg per
|
||||||
# Versionen, deshalb bleibt es hier stehen.
|
# 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
|
cat > "$AUSGABE/ExportOptions.plist" <<PLIST
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?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">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>method</key><string>development</string>
|
<key>method</key><string>ad-hoc</string>
|
||||||
<key>teamID</key><string>$TEAM</string>
|
<key>teamID</key><string>$TEAM</string>
|
||||||
<key>signingStyle</key><string>automatic</string>
|
<key>signingStyle</key><string>automatic</string>
|
||||||
<key>stripSwiftSymbols</key><true/>
|
<key>stripSwiftSymbols</key><true/>
|
||||||
@@ -97,6 +91,12 @@ xcodebuild -exportArchive \
|
|||||||
-exportPath "$AUSGABE" \
|
-exportPath "$AUSGABE" \
|
||||||
-allowProvisioningUpdates
|
-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
|
||||||
echo "Fertig: $AUSGABE/App.ipa"
|
echo "Fertig: $APP/auslieferung/App.ipa"
|
||||||
echo "Aufs Geraet: xcrun devicectl device install app --device <UDID> $AUSGABE/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