---
name: publish
description: "APK publizieren: Version bumpen, APK bauen, auf VPS hochladen, Server-Version aktualisieren, QR-Code/Download-Seite verifizieren. Verwende diesen Skill immer wenn eine neue App-Version veröffentlicht werden soll – auch wenn der User nur 'publish', 'release', 'veröffentlichen', 'neue Version', 'APK hochladen', 'APK deployen', 'Update bereitstellen' oder 'QR-Code aktualisieren' sagt. Trigger-Phrasen: 'publish', 'release', 'veröffentlichen', 'APK hochladen', 'neue Version', 'Update ausrollen', 'QR-Code'."
---

# Skill: Publish (APK auf VPS veröffentlichen)

Dieser Skill deckt den vollständigen Workflow ab, um eine neue App-Version auf dem VPS bereitzustellen, sodass:

1. Die **Homepage** (QR-Code + Download-Link) die aktuelle APK anbietet
2. Die **Update-Prüfung in der App** die neue Version erkennt und zum Download anbietet

---

## Architektur-Überblick

```
App (lokal)                        VPS (bollwerk.online)
─────────────                      ─────────────────────
app/build.gradle.kts               /opt/bollwerk/
  versionCode / versionName          ├── docker-compose.yml  (BOLLWERK_APP_VERSION_CODE/NAME)
                                     ├── data/app-latest.apk (statisch ausgeliefert)
./gradlew assembleDebug              └── Server-Container (Ktor)
  → app-debug.apk                         ├── GET /           → Homepage mit QR-Code
                                           ├── GET /api/version → JSON {versionCode, versionName, apkUrl}
scp → /opt/bollwerk/data/                 └── GET /static/*   → Dateien aus data/
```

### Wie die Update-Prüfung funktioniert

1. App ruft `GET {serverUrl}/api/version` auf → erhält `VersionInfo(versionCode, versionName, apkUrl)`
2. `CheckForUpdateUseCase` vergleicht `versionInfo.versionCode > BuildConfig.VERSION_CODE`
3. Falls neuer → zeigt Download-Button, lädt APK herunter, installiert via `ApkInstaller`

### Wie die Version gesetzt wird

- `POST /api/admin/version` mit `Authorization: Bearer <BOLLWERK_ADMIN_TOKEN>` Header
- Server speichert `versionCode` + `versionName` persistent in `data/version.json`
- `GET /api/version` liest aus `VersionStore` (Fallback auf Env-Vars, falls Datei fehlt)
- **Kein Container-Neustart nötig!**

### Wie die Homepage funktioniert

- `GET /` liefert HTML mit QR-Code (via qrcodejs) + Download-Link auf `/static/app-latest.apk`
- Version wird aus `VersionStore` gelesen (persistent in `data/version.json`)

---

## Publish-Workflow (Schritte)

### Schritt 1 – Version bumpen

In `app/build.gradle.kts`:

- `versionCode` um 1 erhöhen
- `versionName` **immer strikt im Format `x.y.z`** führen (z.B. `1.7.13`)
- Das dritte Segment (`z`) muss dem `versionCode` entsprechen

### Schritt 2 – Build & Test

```powershell
.\gradlew.bat assembleDebug test
```

Die APK liegt danach unter: `app/build/outputs/apk/debug/app-debug.apk`

### Schritt 3 – APK auf VPS hochladen

```powershell
scp app/build/outputs/apk/debug/app-debug.apk root@195.246.231.210:/opt/bollwerk/data/app-latest.apk
```

**Voraussetzung:** SSH-Agent muss laufen und Key geladen sein (siehe vps-deploy Skill).

### Schritt 4 – Server über neue Version benachrichtigen

Die Version wird per API-Call gesetzt (kein Container-Neustart nötig):

```powershell
$body = @{ versionCode = $CODE; versionName = $NAME } | ConvertTo-Json -Compress
Invoke-WebRequest -Uri "https://bollwerk.online/api/admin/version" `
    -Method POST `
    -Headers @{ "Authorization" = "Bearer $env:BOLLWERK_ADMIN_TOKEN"; "Content-Type" = "application/json" } `
    -Body $body `
    -UseBasicParsing
```

Alternativ mit curl:

```bash
curl -s -X POST https://bollwerk.online/api/admin/version \
  -H "Authorization: Bearer $BOLLWERK_ADMIN_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"versionCode\": $CODE, \"versionName\": \"$NAME\"}"
```

**Voraussetzung:** Die Umgebungsvariable `BOLLWERK_ADMIN_TOKEN` muss gesetzt sein. Das Token muss mit dem `BOLLWERK_ADMIN_TOKEN` auf dem VPS übereinstimmen.

### Schritt 5 – Verifizieren

```powershell
# Version-API prüfen
Invoke-WebRequest -Uri "https://bollwerk.online/api/version" -UseBasicParsing | Select-Object -ExpandProperty Content

# Homepage prüfen (QR-Code-Seite)
Invoke-WebRequest -Uri "https://bollwerk.online/" -UseBasicParsing | Select-Object StatusCode, StatusDescription
```

---

## Automatisiertes Skript

Das Skript `publish-apk.py` in diesem Skill-Ordner automatisiert den **gesamten** Release-Workflow (Version bumpen, bauen, hochladen, API-Call, verifizieren, committen):

```powershell
# Alles automatisch (versionCode +1, versionName wird auf x.y.<versionCode> gesetzt):
python ".github/skills/publish/publish-apk.py"

# Mit neuem versionName:
python ".github/skills/publish/publish-apk.py" --version-name "2.0.42"

# Nur hochladen (APK bereits gebaut, kein Push):
python ".github/skills/publish/publish-apk.py" --skip-build --skip-push
```

**Voraussetzungen:**

- SSH-Agent muss laufen (für APK-Upload per SCP)
- `$env:BOLLWERK_ADMIN_TOKEN` muss gesetzt sein (für Version-Notify API-Call)

**Parameter:**

| Parameter        | Pflicht | Default     | Beschreibung                                      |
| ---------------- | ------- | ----------- | ------------------------------------------------- |
| `--version-name` | nein    | aus current major.minor + code | Neuer versionName im Format `x.y.z` (z.B. "2.0.42") |
| `--version-code` | nein    | aktuell + 1 | Expliziter versionCode; sonst automatisch +1      |
| `--apk-path`     | nein    | debug-APK   | Pfad zur APK-Datei                                |
| `--skip-build`   | nein    | false       | Gradle-Build überspringen                         |
| `--skip-verify`  | nein    | false       | Verifizierung nach dem Deploy überspringen        |
| `--skip-push`    | nein    | false       | Git-Push überspringen (nur lokaler Commit)        |

---

## Wichtige Dateien

| Datei                                                    | Beschreibung                               |
| -------------------------------------------------------- | ------------------------------------------ |
| `app/build.gradle.kts` (L18-19)                          | `versionCode` / `versionName`              |
| `server/src/main/resources/application.conf`             | Server-Version-Defaults + Env-Var-Override |
| `server/src/main/kotlin/.../store/VersionStore.kt`       | Persistente Version in `data/version.json` |
| `server/src/main/kotlin/.../routes/VersionRoutes.kt`     | Homepage + `/api/version` + POST-Endpoint  |
| `server/src/main/kotlin/.../plugins/Routing.kt`          | `staticFiles("/static", File("data"))`     |
| `app/src/main/java/.../usecase/CheckForUpdateUseCase.kt` | Update-Prüfung in der App                  |
| `app/src/main/java/.../ui/update/UpdateViewModel.kt`     | Update-UI + Download-Logik                 |

---

## Environment-Variablen (VPS)

| Variable                    | Pflicht | Beschreibung                                                 |
| --------------------------- | ------- | ------------------------------------------------------------ |
| `BOLLWERK_ADMIN_TOKEN`      | ja      | Bearer-Token für `POST /api/admin/version` (min. 32 Zeichen) |
| `BOLLWERK_APP_VERSION_CODE` | nein    | Fallback-VersionCode (nur wenn `data/version.json` fehlt)    |
| `BOLLWERK_APP_VERSION_NAME` | nein    | Fallback-VersionName (nur wenn `data/version.json` fehlt)    |

---

## Bekannte Lücken / TODOs

- **Kein Release-Build:** Aktuell wird `assembleDebug` verwendet. Für Produktion → Signing-Config + `assembleRelease` einrichten.
- **Env-Vars optional:** `BOLLWERK_APP_VERSION_CODE` / `BOLLWERK_APP_VERSION_NAME` können aus der docker-compose.yml entfernt werden, sobald `data/version.json` initial gesetzt ist.
