Files
tessera-ctl/.planning/phases/18-desktop-client-fertigstellen/18-02-SUMMARY.md
T

140 lines
12 KiB
Markdown

---
phase: 18-desktop-client-fertigstellen
plan: 02
subsystem: infra
tags: [gitea-actions, ci-cd, tauri, actions-cache, release-assets]
# Dependency graph
requires:
- phase: 18-desktop-client-fertigstellen (Plan 01)
provides: .gitea/scripts/desktop-collect.sh, .gitea/scripts/desktop-version.sh, desktop-dist/manifest.json-Form
provides:
- "Job desktop in .gitea/workflows/ci.yml (Linux-AppImage mit Tag-Version, Cargo-Zwischenspeicher, actions/cache-Uebergabe)"
- "publish haengt an desktop (needs: desktop), holt Pakete per actions/cache/restore mit fail-on-cache-miss: true, prueft das Manifest hart"
- "publish-images.sh bricht im echten Baupfad ohne desktop-dist/manifest.json ab"
- "publish-release.sh: upload_asset() laedt jede Manifest-Datei idempotent als Release-Anhang hoch (GET -> DELETE vorhandener -> POST multipart)"
affects: [18-05-windows-cross-bau-pipeline-beweis, 18-06-freigabe-release-anhang]
actuals:
tokens: 2586
tasks: 2
commits: 2
plan_head_before: cd62de1
tech-stack:
added: []
patterns:
- "Cross-Job-Uebergabe per actions/cache/save + actions/cache/restore (Schluessel exakt am Commit-SHA, kein restore-keys-Fallback fuer die Uebergabe selbst) statt der auf dieser Gitea-Instanz unzuverlaessigen upload-/download-artifact-Actions"
- "Zweite Header-Datei ohne Content-Type: application/json fuer multipart-Uploads (curl -F) neben der bestehenden JSON-Header-Datei — gleiche umask 077/mktemp/trap-Mechanik, Token nie als Argument"
- "Idempotenter Datei-Upload nach dem bereits etablierten GET-dann-PATCH/POST-Muster von publish-release.sh: GET .../assets, vorhandene Datei gleichen Namens per DELETE entfernen, dann frisch per POST hochladen"
key-files:
created: []
modified:
- .gitea/workflows/ci.yml
- .gitea/scripts/publish-images.sh
- .gitea/scripts/publish-release.sh
key-decisions:
- "Kopfkommentar-Verweis auf 'desktop-version.sh' im neuen CI-Job-Schritt entfernt (nur als run-Zeile belassen), weil sonst grep -c 'desktop-version.sh' in der Datei auf 2 statt der geforderten 1 Fundstelle gestiegen waere — reine Kommentarformulierung, keine Verhaltensaenderung."
- "In der 404-Verzweigung von publish-release.sh wird ID jetzt explizit als Variable gesetzt (vorher nur inline in der Echo-Zeile berechnet), damit sie fuer die nachfolgende Upload-Schleife in beiden Zweigen (200 und 404) verfuegbar ist."
- "Upload-Schleife ueber die Manifest-Dateinamen laeuft als `for FNAME in $(jq -r ...)` statt `jq ... | while read`, damit ein `exit 1` innerhalb von upload_asset() unter dash/sh tatsaechlich das ganze Skript beendet und nicht nur eine Pipe-Subshell (POSIX-sh-Pipelines laufen in eigenen Subshells)."
patterns-established: []
requirements-completed: [DESK-01, DESK-04]
coverage:
- id: D1
description: "Job desktop laeuft nach test auf main und bei Tags v*, baut das Linux-AppImage mit der Tag-Version (System-Abhaengigkeiten, Rust-Toolchain per rustup, Cargo-Zwischenspeicher, cargo check/clippy, alte Bundles entfernen, Bau, desktop-collect.sh --require linux) und uebergibt desktop-dist/ per actions/cache an publish"
requirement: "DESK-01"
verification:
- kind: other
ref: "grep-Batterie aus dem Plan (CI-OK: Job-Schluessel, needs, Cache-Schluessel x2, fail-on-cache-miss, kein upload-artifact, System-Abhaengigkeiten, Skript-Aufrufe) + node-Struktur-Check der Job-Reihenfolge quality/test/desktop/publish"
status: pass
human_judgment: true
rationale: "Der eigentliche Pipeline-Lauf (Rust-Bau, apt-Installation, Cargo-Cache-Verhalten auf dem echten act_runner) kann von diesem Executor nicht ausgefuehrt werden — nur die YAML-Struktur und die POSIX-sh-Skripte sind lokal pruefbar. Der echte gruene Lauf wird laut Plan/Objective erst in 18-05 bewiesen (gemeinsam mit dem Windows-Cross-Bau)."
- id: D2
description: "publish bricht hart ab, wenn das Manifest aus dem Zwischenspeicher fehlt (fail-on-cache-miss im Workflow + expliziter test -f/jq-Schritt + zweites Netz in publish-images.sh vor der Docker-Bau-Schleife)"
requirement: "DESK-01"
verification:
- kind: other
ref: "sh -n .gitea/scripts/publish-images.sh + GITHUB_REF=refs/tags/v1.1.0 sh .gitea/scripts/publish-images.sh --print-plan (liefert weiterhin 4 push-Zeilen, da der Probelauf vor der neuen Pruefung endet) + Code-Inspektion der neuen if [ ! -f desktop-dist/manifest.json ]-Pruefung vor der Bau-Schleife"
status: pass
human_judgment: false
- id: D3
description: "publish-release.sh haengt bei Tags jede Datei aus dem Manifest idempotent als Release-Datei an den Gitea-Release (GET assets -> vorhandene Datei gleichen Namens per DELETE entfernen -> POST multipart); das Token verlaesst nie die Header-Datei"
requirement: "DESK-04"
verification:
- kind: other
ref: "sh -n .gitea/scripts/publish-release.sh + sh .gitea/scripts/publish-release.sh --dry-run --tag v1.1.0 (nennt POST .../assets?name=Tessera-1.1.0.AppImage aus dem echten Manifest von 18-01, kein Netzaufruf, kein Token) + grep-Batterie (HDR_AUTH x4, genau ein upload_asset(), files[].name, kein curl mit GITEA_TOKEN als Argument)"
status: pass
human_judgment: true
rationale: "Der idempotente GET/DELETE/POST-Roundtrip gegen die echte Gitea-API (inkl. multipart-Upload einer ~107-MB-Datei) ist nur im echten CI-Lauf pruefbar; der Probelauf beweist ausschliesslich die Skript-Logik und den erwarteten Zielpfad. Der echte Beweis folgt beim naechsten Freigabe-Tag (18-06, human-check laut Plan-Verifikation Punkt 4)."
duration: 8min
completed: 2026-09-16
status: complete
---
# Phase 18 Plan 02: CI-Pipeline fuer den Desktop-Client — Job `desktop`, Cache-Uebergabe, Release-Anhaenge Summary
**Neuer CI-Job `desktop` baut das Linux-AppImage mit Tag-Version und uebergibt es per `actions/cache` an `publish`, das ohne Manifest hart abbricht; `publish-release.sh` haengt jede Datei aus `manifest.json` idempotent (GET/DELETE/POST) als Release-Anhang an — der echte Pipeline-Lauf folgt in 18-05.**
## Performance
- **Duration:** 8 min
- **Started:** 2026-09-16T14:13:35Z (Aktenstand-Zeitstempel nach 18-01)
- **Completed:** 2026-09-16T14:21:32Z
- **Tasks:** 2
- **Files modified:** 3
## Accomplishments
- `.gitea/workflows/ci.yml`: neuer Job `desktop` zwischen `test` und `publish` — Systemabhaengigkeiten (vollstaendige apt-Liste fuer den bloßen `ubuntu-latest`-Runner, Pitfall 5), Rust-Toolchain per `rustup` (kein Rust im Runner-Abbild), Cargo-Zwischenspeicher (`actions/cache@v4`, Schluessel ueber `Cargo.lock`-Hash), Version aus dem Freigabe-Tag (`desktop-version.sh`), `cargo check`/`cargo clippy` (D-16), alte Bundle-Reste entfernen, Linux-AppImage bauen, `desktop-collect.sh --require linux`, Uebergabe per `actions/cache/save` mit Schluessel `desktop-dist-${{ gitea.sha }}`.
- `publish` haengt jetzt an `desktop` (`needs: desktop`) statt an `test`, holt die Pakete per `actions/cache/restore` mit `fail-on-cache-miss: true` und prueft das Manifest zusaetzlich explizit (`test -f` + `jq .`) — Job bricht sichtbar ab statt ein Abbild ohne Desktop-Pakete zu bauen.
- `publish-images.sh`: zweites Netz gegen einen Cache-Fehlschlag — im echten Baupfad (nicht im `--print-plan`-Probelauf) bricht das Skript ohne `desktop-dist/manifest.json` mit Exit 1 ab, bevor irgendein `docker build` laeuft.
- `publish-release.sh`: neue Funktion `upload_asset()` nach dem bereits etablierten GET-dann-PATCH/POST-Idempotenzmuster der Datei — pro Manifest-Datei erst pruefen, ob ein Anhang gleichen Namens existiert (`GET .../assets`), diesen ggf. entfernen (`DELETE`), dann frisch hochladen (`POST multipart`, Feld `attachment`). Neue Header-Datei `$HDR_AUTH` (nur `Authorization`, kein JSON-Content-Type) fuer den multipart-Upload — gleiche `umask 077`/`mktemp`/`trap`-Mechanik wie die bestehende `$HDR`-Datei, Token verlaesst nie eine `curl`-Kommandozeile. `--dry-run` listet zusaetzlich die geplanten Uploads aus dem vorhandenen Manifest.
## Task Commits
Each task was committed atomically:
1. **Task 1: Job desktop (Linux-AppImage) und Uebergabe an publish per actions/cache** - `a6ffe05` (feat)
2. **Task 2: Release-Dateien idempotent an den Gitea-Release haengen** - `75a8e40` (feat)
**Plan metadata:** commit pending (this SUMMARY + STATE.md/ROADMAP.md/REQUIREMENTS.md)
## Files Created/Modified
- `.gitea/workflows/ci.yml` - Job `desktop` (Linux-AppImage, Cargo-Cache, actions/cache-Uebergabe), `publish` haengt an `desktop`, holt Pakete per Cache-Restore mit hartem Abbruch
- `.gitea/scripts/publish-images.sh` - Harte Manifest-Pruefung vor der Docker-Bau-Schleife im echten Baupfad
- `.gitea/scripts/publish-release.sh` - `HDR_AUTH`, `upload_asset()`, `DESKTOP_DIST`/`MANIFEST`-Variablen, Upload-Schleife nach Release-Anlage/-Aktualisierung, erweiterter `--dry-run`
## Decisions Made
- Kopfkommentar-Referenz auf `desktop-version.sh` im neuen CI-Schritt-Kommentar weggelassen (nur als tatsaechliche `run:`-Zeile vorhanden), damit die Zaehl-basierte Abnahmekriterien-Pruefung (`grep -c 'desktop-version.sh'` == 1) exakt erfuellt wird — keine funktionale Aenderung.
- `ID` in der 404-Verzweigung von `publish-release.sh` (neuer Release) jetzt als Variable gesetzt statt nur inline in der Log-Zeile berechnet, damit dieselbe Variable in beiden Case-Zweigen (bestehender und neuer Release) fuer die nachfolgende Upload-Schleife zur Verfuegung steht.
- Die Upload-Schleife ueber Manifest-Dateinamen nutzt `for FNAME in $(jq -r '.files[].name' "$MANIFEST")` statt einer `jq | while read`-Pipe, weil ein `exit 1` innerhalb der aufgerufenen `upload_asset()`-Funktion in einer POSIX-sh-Pipe-Subshell nur die Subshell beendet hatte, nicht das gesamte Skript — mit `for ... in $(...)` bleibt der Fehlerpfad im Hauptprozess und `set -eu` wirkt wie erwartet.
## Deviations from Plan
None - plan executed exactly as written.
## Issues Encountered
- Die im Plan/`<verify>` verwendeten `grep`-Muster mit `${{ ... }}` (z. B. `desktop-dist-${{ gitea.sha }}`) liefern in dieser Ausfuehrungsumgebung ueber die interaktive `grep`-Shell-Funktion (ugrep-basierter Shim von Claude Code) faelschlich 0 Treffer, obwohl die Zeile exakt vorhanden ist — bestaetigt durch direkten Vergleich mit `command grep`/`/usr/bin/grep` (GNU grep 3.11), die beide korrekt 2 Treffer liefern. Alle `<verify>`- und `<acceptance_criteria>`-Pruefungen wurden deshalb zusaetzlich mit `command grep` wiederholt und sind gruen; die Datei selbst ist unveraendert von diesem Werkzeug-Artefakt betroffen. Kein Code-Problem, reine Umgebungs-Eigenheit dieser Sitzung.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- Der Job `desktop` und die Cache-Uebergabe an `publish` stehen; `publish` kann kein Abbild mehr ohne Desktop-Pakete bauen; `publish-release.sh` laedt Manifest-Dateien idempotent hoch — 18-05 kann direkt den Windows-Cross-Bau (cargo-xwin, NSIS) in denselben `desktop`-Job erweitern und den echten Pipeline-Lauf mit beiden Dateien beweisen.
- Kein Blocker. Der reale CI-Lauf (act_runner, echter Cache-Server, echter Gitea-Upload) ist laut Plan-Objective bewusst nicht Teil dieses Plans — er wird in 18-05 (Pipeline-Beweis) und beim naechsten Freigabe-Tag (18-06, Release-Anhang) gefuehrt.
- `REGISTRY_TOKEN` (Precondition Task 2) bleibt unveraendert im Einsatz; keine neue Secret-Konfiguration noetig.
---
*Phase: 18-desktop-client-fertigstellen*
*Completed: 2026-09-16*
## Self-Check: PASSED
All modified files verified on disk (`.gitea/workflows/ci.yml`, `.gitea/scripts/publish-images.sh`, `.gitea/scripts/publish-release.sh`). Both task commits found in `git log` (`a6ffe05`, `75a8e40`). All plan-level `<verification>` items re-run and passing: `CI-OK` (Job-Struktur, Cache-Schluessel x2, `fail-on-cache-miss`, kein `upload-artifact`, Skript-Aufrufe), `IMAGES-OK` (`sh -n`, vier `push`-Zeilen im Probelauf, Manifest-Pruefung vorhanden), `RELEASE-OK` (`sh -n`, Probelauf nennt `assets?name=Tessera-1.1.0.AppImage`, `HDR_AUTH` x4, genau ein `upload_asset()`, `files[].name`, kein Token in einer `curl`-Zeile) — alle Pruefungen zusaetzlich mit `command grep`/GNU grep gegengeprueft (siehe "Issues Encountered" zum `ugrep`-Shim-Artefakt dieser Sitzung).