127 lines
8.2 KiB
Markdown
127 lines
8.2 KiB
Markdown
---
|
||
name: mobile-apk-flutter-build
|
||
description: "Flutter-APK auf headless VPS bauen: Setup + AGP-Pitfalls."
|
||
version: 1.0.0
|
||
author: mehlzoerwer
|
||
platforms: [linux]
|
||
---
|
||
|
||
# Mobile APK bauen (Flutter, headless Linux-VPS)
|
||
|
||
Klasse: Eine Flutter-App als Android-APK auf einem headless Linux-VPS bauen und zum Download bereitstellen. Verifiziert 2026-08-07 für PersoEnt (VPS netcup, Ubuntu 24.04).
|
||
|
||
Hardware-Notiz: `flutter doctor` meldet "Android Studio not installed" und fehlendes Chrome/Linux-Toolchain — **beide sind für APK-Build nicht nötig**. Android toolchain ✓ reicht.
|
||
|
||
## 1. Flutter SDK installieren
|
||
|
||
```bash
|
||
cd /opt
|
||
curl -sL -o /tmp/flutter.tar.xz "https://storage.googleapis.com/flutter_infra_release/releases/stable/linux/flutter_linux_3.24.5-stable.tar.xz"
|
||
tar xf /tmp/flutter.tar.xz # → /opt/flutter
|
||
echo 'export PATH="$PATH:/opt/flutter/bin"' >> /root/.bashrc
|
||
export PATH="$PATH:/opt/flutter/bin"
|
||
flutter --version # prüft Dart-Version (3.5.x)
|
||
```
|
||
|
||
## 2. 🔥 git safe.directory — DER "0.0.0-unknown"-Fix (PITFALL)
|
||
|
||
Wenn `flutter --version` / `flutter pub` meldet **"Flutter SDK version 0.0.0-unknown"** oder `fatal: detected dubious ownership in repository at '/opt/flutter'`, dann ist Flutters git-Versionserkennung gebrochen. Fix:
|
||
```bash
|
||
git config --global --add safe.directory /opt/flutter
|
||
```
|
||
Ohne diesen Schritt schlägt **jeder** `pub get` an Flutters eigener Versionsprüfung fehl ("The current Flutter SDK version is 0.0.0-unknown ... version solving failed"). Das ist NICHT ein echtes Versionsproblem — nur ownership. Nach dem Fix zeigt `flutter --version` wieder `3.24.5 • stable`.
|
||
|
||
## 3. Android SDK + Java
|
||
|
||
```bash
|
||
apt-get install -y openjdk-17-jdk unzip
|
||
|
||
# cmdline-tools:
|
||
mkdir -p /opt/android-sdk/cmdline-tools
|
||
curl -sL -o /tmp/cmdtools.zip "https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip"
|
||
unzip -q cmdtools.zip -d /opt/android-sdk/cmdline-tools/
|
||
mv /opt/android-sdk/cmdline-tools/cmdline-tools /opt/android-sdk/cmdline-tools/latest # 'latest' zwingend!
|
||
|
||
export ANDROID_HOME=/opt/android-sdk
|
||
export PATH="$PATH:/opt/android-sdk/cmdline-tools/latest/bin"
|
||
yes | sdkmanager --licenses
|
||
sdkmanager "platforms;android-34" "build-tools;34.0.0" "platform-tools"
|
||
```
|
||
Gradle lädt fehlende Build-Tools (z.B. 33.0.1) automatisch nach. Android-Studio NICHT nötig.
|
||
|
||
## 4. Projekt anlegen + Dependencies
|
||
|
||
```bash
|
||
flutter create --platforms=android --org de.persoent --project-name persoent_app persoent_app
|
||
cd persoent_app
|
||
```
|
||
Statt manuelle Versionen in pubspec zu erraten → `flutter pub add <pkg>` (löst passende Version selbst, inkl. `:^0.0.0` bei 0.x-Paketen):
|
||
```bash
|
||
flutter pub add pocketbase google_sign_in http shared_preferences
|
||
```
|
||
|
||
## 5. 🔥 AGP `namespace`-Konflikt mit älteren Plugins (PITFALL)
|
||
|
||
Symptom: `flutter build apk` → *"Could not create ... LibraryVariantBuilderImpl. > Namespace not specified. Specify a namespace in the module's build file."* (im Plugin, z.B. `:health_connect`).
|
||
|
||
Ursache: AGP 8.1 (im Projekt, `android/settings.gradle`) **erzwingt `namespace`**, aber ältere Plugin-Pakete (z.B. `health_connect ^0.x`) haben keinen. Optionen:
|
||
- **Einfachster robuster Weg:** das brechende Plugin `flutter pub remove <pkg>` **vorerst entfernen**, das Grundgerüst (Login+Dashboard) bauen → **erst funktionierendes APK, dann Health-Integration gezielt lösen**.
|
||
- Oder AGP 8.1 → 7.3 zurückstufen (matcht Flutters Gradle), riskant.
|
||
|
||
**Bau-Strategie (empfohlen):** erst minimales APK, das kompiliert; native Plugins (Health Connect usw.) danach einzeln, um Fehlerursachen zu isolieren.
|
||
|
||
## 5b. 🔥 `health_connect` auf pub.dev ist ein LEERES 0.0.0-Paket (PITFALL)
|
||
|
||
Symptom: `flutter pub add health_connect` liefert `health_connect-0.0.0` (im `~/.pub-cache/.../health_connect-0.0.0` nur ein leeres Verzeichnis), und der Build bricht mit dem AGP-`namespace`-Fehler.
|
||
|
||
Fakt: **Es existiert KEIN offizielles, funktionsfähiges `health_connect`-Flutter-Paket auf pub.dev.** Googles Health-Connect-Integration ist ein **GitHub-Beispiel**, das das Android-SDK nativ bundelt — nicht als veröffentlichtes pub-Paket gepflegt. `health_connect`/`health_connect_android` auf pub.dev sind leere Platzhalter.
|
||
|
||
Konsequenz für PersoEnt: Google-Gesundheitsdaten sind NICHT über ein magisches pub.dev-Paket abrufbar. Der echte Weg erfordert nativen Android-Health-Connect-Code (SDK-Binding) — das skizzierte der Nutzer als langfristige "PersoEnt als App"-Entscheidung; vorerst liefert die APK Login+Dashboard, Health-Import ist separat zu bauen. Siehe auch `references/persoent-google-health-strategy.md` (Google-Fit-REST deprecated, Health Connect nur Android-nativ, kein Server-REST-Sync).
|
||
|
||
## 6. Gate + Build
|
||
|
||
```bash
|
||
flutter analyze # vorher: fängt unbenutzte Imports / falsche Klassennamen
|
||
flutter build apk --release
|
||
```
|
||
- APK: `build/app/outputs/flutter-apk/app-release.apk` (~22MB)
|
||
- Erster Gradle-Build dauert lange (Downloads). Nutze langen Timeout.
|
||
- `test/widget_test.dart` muss zum neuen Code passen (Default referenziert `MyApp`).
|
||
|
||
## 7. Zum Download bereitstellen (VPS)
|
||
|
||
```bash
|
||
mkdir -p /var/www/<projekt>/html/app
|
||
cp build/app/outputs/flutter-apk/app-release.apk /var/www/<projekt>/html/app/<Name>-Android.apk
|
||
chown -R www-data:www-data /var/www/<projekt>/html/app/
|
||
# Test:
|
||
curl -s -o /dev/null -w "%{http_code}" "https://<domain>/app/<Name>-Android.apk" # 200
|
||
```
|
||
|
||
## 8. 🔥 Google-Sign-In Fehlercode 10 (PITFALL)
|
||
|
||
Symptom in der App (Nutzerscreenshot): `PlatformException(sign_in_failed, [google_sign_in] 10: null, null)`. Fehlercode **10 = OAuth-Client-Konfiguration fehlt/unverifiziert**:
|
||
- Dem Google-Cloud-Projekt fehlt der **Android-OAuth-Client**, oder
|
||
- der **SHA-1-Fingerprint** der App ist dort nicht hinterlegt.
|
||
|
||
Fix (Nutzer-Einbindung nötig, nur deren Konto kann das):
|
||
1. SHA-1 aus der gebauten APK extrahieren und eintragen:
|
||
```bash
|
||
keytool -printcert -jarfile app-release.apk | grep SHA1
|
||
```
|
||
2. Google-Cloud → Credentials → OAuth-Client "Android" mit diesem SHA-1 anlegen.
|
||
3. Die generierte **Client-ID** in `android/app/src/main/AndroidManifest.xml` als `<meta-data android:name="com.google.android.gms.auth.API_KEY" .../>`-Block eintragen, APK neu bauen.
|
||
|
||
## 9. 📸 Nutzer-Screenshots auswerten (Handy-Foto) ohne vision-Tool
|
||
|
||
Wenn der Nutzer einen Screenshot schickt und **kein vision_analyze-Tool** verfügbar ist, lässt sich der Text (Fehlermeldungen) per **Tesseract-OCR** auslesen:
|
||
```bash
|
||
tesseract /root/.hermes/cache/images/<img>.jpg - 2>/dev/null # Text auf stdout
|
||
```
|
||
Weg: `browser_get_images` liefert keine lokalen Dateien → stattdessen `file <pfad>` (Maße bestätigen) + `tesseract` zum Extrahieren von Fehlertext. Liefert z.B. `PlatformException(...)`-Zeilen — genug, um die Diagnose (Code 10, etc.) zu stellen. Hinweis: tesseract ist auf Ubuntu oft vorinstalliert (`/usr/bin/tesseract`).
|
||
|
||
## Referenzen
|
||
- `references/persoent-apk-2026-08-07.md` — Session-Transcript: voller Verlauf, Versionen, Fehlerbilder
|
||
- `references/persoent-google-health-strategy.md` — PersoEnt: Google-Gesundheitsdaten-Strategie (Fit-REST deprecated, Health-Connect nur nativ; Detektiv-Korrelations-Analytik)
|
||
- `references/persoent-cost-tracking.md` — **PersoEnt/Mesh-Kosten-Tracking + Budget-BREMSE (OpenRouter)**: mesh-cost-track.sh (stündlich, je Agent) + mesh-cost-break.py (kill_switches bei Deckel) + Agent-Poller-Guard (PAUSED → keine Task-Übernahme), E2E-getestet. PITFALLs: DREI OpenRouter-Kostenquellen unterscheiden (`/auth/key usage`≠Credits, `/credits total_usage`=USD verbindlich, Body-`usage.cost`); Usage KUMULATIV → **tag-übergreifendes Delta** (letzter-heute − letzter-gestern, max(0,·) gegen Reset), sonst endlos PAUSED; DeepSeek V4 Pro = 64% (Bremsen auf Modell-Ebene via `hermes config set model.providers.openrouter.blocked_models`, Singular `model.`); Master-exempt von PAUSED. **FINALER NUTZER-COST-MODEL (ab 08-08 ersetzt Monats-Cap): 1$/Tag/Agent hart, solange Credits da, KEIN Team-Monats-Stopp, Guthaben-0→Stopp.**
|
||
- `references/hermes-config-ops.md` — **Hermes-System-Operation (VPS/Master)**: Config NIE via patch — immer `hermes config set`; `approvals.mode=auto` (allowlist bleibt hart); **BACKUP-ONLY-Regel** (`.bak` vor jeder Änderung → keine extra Freigabe); Memory-Limits (4400/2750, verdoppelt); **„Wir, nicht du" Team-Pflicht** bei Kernsystemen. |