From 44154a4697c2b015ccacbb09bb6f5702d90e90f3 Mon Sep 17 00:00:00 2001 From: Schalli Date: Thu, 25 Jun 2026 09:25:24 +0200 Subject: [PATCH] docs(06-desktop-client-ci-cd): create phase plan Co-Authored-By: Claude Sonnet 4.6 --- .planning/ROADMAP.md | 13 +- .../06-desktop-client-ci-cd/06-01-PLAN.md | 246 +++++++++++++++ .../06-desktop-client-ci-cd/06-02-PLAN.md | 284 ++++++++++++++++++ .../06-desktop-client-ci-cd/06-03-PLAN.md | 258 ++++++++++++++++ 4 files changed, 799 insertions(+), 2 deletions(-) create mode 100644 .planning/phases/06-desktop-client-ci-cd/06-01-PLAN.md create mode 100644 .planning/phases/06-desktop-client-ci-cd/06-02-PLAN.md create mode 100644 .planning/phases/06-desktop-client-ci-cd/06-03-PLAN.md diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 31abf9b..c8b811d 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -197,7 +197,16 @@ Decimal phases appear between their surrounding integers in numeric order. 2. Desktop app connects to the existing web backend (no standalone server) 3. Code changes are automatically committed and pushed to Gitea with minimal manual intervention -**Plans**: 0/TBD +**Plans**: 3 plans + +**Wave 1** *(parallel — disjoint files)* + +- [ ] 06-01-PLAN.md -- Desktop foundation: Tauri toolchain, apps/desktop scaffold, URL-loading WebView + first-run server URL setup (DESK-01/02) +- [ ] 06-03-PLAN.md -- CI/CD: Gitea remote, act_runner, multi-stage Gitea Actions pipeline (lint+type-check -> tests -> docker build+deploy) (INFRA-04) + +**Wave 2** *(blocked on 06-01)* + +- [ ] 06-02-PLAN.md -- Desktop native: tray + close-to-tray, window-state, autostart, notifications + version check, branded icon, AppImage+NSIS bundles, /health/version API (DESK-01/02) ## Progress @@ -211,4 +220,4 @@ Phases execute in numeric order: 1 -> 2 -> 3 -> 4 -> 5 -> 6 | 3. Module System & Domaincheck | 3/4 | In Progress| | | 4. Marketplace & Portal Navigation | 0/4 | Not started | - | | 5. Dashboard & Calendar | 5/5 | Complete | 2026-06-24 | -| 6. Desktop Client & CI/CD | 0/TBD | Not started | - | +| 6. Desktop Client & CI/CD | 0/3 | Not started | - | diff --git a/.planning/phases/06-desktop-client-ci-cd/06-01-PLAN.md b/.planning/phases/06-desktop-client-ci-cd/06-01-PLAN.md new file mode 100644 index 0000000..5e07db9 --- /dev/null +++ b/.planning/phases/06-desktop-client-ci-cd/06-01-PLAN.md @@ -0,0 +1,246 @@ +--- +phase: 06-desktop-client-ci-cd +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - apps/desktop/package.json + - apps/desktop/src-tauri/Cargo.toml + - apps/desktop/src-tauri/tauri.conf.json + - apps/desktop/src-tauri/build.rs + - apps/desktop/src-tauri/src/main.rs + - apps/desktop/src-tauri/src/lib.rs + - apps/desktop/src-tauri/capabilities/default.json + - apps/desktop/src/setup.html +autonomous: false +requirements: + - DESK-01 + - DESK-02 +user_setup: [] + +must_haves: + truths: + - "Tauri toolchain prerequisites (Rust + WebKitGTK dev headers) are installed and verifiable" + - "apps/desktop is a Tauri 2.x project registered in the pnpm workspace as @tessera/desktop" + - "On first launch the app shows a local setup page where the user enters a server URL" + - "The entered server URL is persisted via tauri-plugin-store and reused on subsequent launches" + - "After a URL is configured, the WebView loads the configured Tessera server (no bundled frontend assets)" + artifacts: + - path: "apps/desktop/src-tauri/tauri.conf.json" + provides: "Tauri window + bundle config, frontendDist pointing at local setup page" + contains: "productName" + - path: "apps/desktop/src-tauri/src/lib.rs" + provides: "Rust entry: plugin registration, store read, navigate-to-server-URL logic" + min_lines: 25 + - path: "apps/desktop/src/setup.html" + provides: "First-run server URL configuration page using tauri-plugin-store" + contains: "server_url" + - path: "apps/desktop/src-tauri/capabilities/default.json" + provides: "Plugin permission grants for store" + contains: "store:default" + key_links: + - from: "apps/desktop/src/setup.html" + to: "tauri-plugin-store (config.json)" + via: "load() + store.set('server_url', url)" + pattern: "server_url" + - from: "apps/desktop/src-tauri/src/lib.rs" + to: "main WebView window" + via: "read stored server_url, navigate window to it on startup" + pattern: "server_url" +--- + + +Establish the Tauri 2.x desktop wrapper foundation as a thin URL-loading shell. This is the +first vertical slice for DESK-01/DESK-02: install the missing toolchain, scaffold `apps/desktop` +in the monorepo, and deliver an app that launches, prompts for a server URL on first run, persists +it, and loads the configured Tessera web frontend in the native WebView. + +Purpose: A user can run the desktop app and reach the live Tessera web app — the thinnest +end-to-end desktop experience. All native polish (tray, window state, autostart, notifications, +icon, production bundles) is layered on in Plan 06-02. + +Output: A runnable `apps/desktop` Tauri project (`pnpm --filter=@tessera/desktop tauri dev`) +that connects to a user-configured server URL and remembers it. + + + +@$HOME/.claude/gsd-core/workflows/execute-plan.md +@$HOME/.claude/gsd-core/templates/summary.md + + + +@.planning/PROJECT.md +@.planning/ROADMAP.md +@.planning/STATE.md +@.planning/phases/06-desktop-client-ci-cd/06-CONTEXT.md +@.planning/phases/06-desktop-client-ci-cd/06-RESEARCH.md +@pnpm-workspace.yaml +@apps/web/src/app/globals.css + + + +This plan introduces the following symbols/files (consumed by Plan 06-02): +- `apps/desktop/` — Tauri project root, pnpm package `@tessera/desktop` +- `apps/desktop/src-tauri/src/lib.rs::run()` — Rust entry function (extended in 06-02) +- `apps/desktop/src-tauri/tauri.conf.json` — central Tauri config (window, bundle, plugins) +- `apps/desktop/src-tauri/capabilities/default.json` — permission grants (extended in 06-02) +- `apps/desktop/src/setup.html` — first-run server URL page +- tauri-plugin-store `config.json` key `server_url` — persisted server URL contract + + + + + + Task 1: Install Tauri toolchain prerequisites (Rust + WebKitGTK dev headers) + + - .planning/phases/06-desktop-client-ci-cd/06-RESEARCH.md (Environment Availability table, Pitfall 1, Installation block) + + + RESEARCH.md confirms the machine is MISSING the Rust toolchain and `libwebkit2gtk-4.1-dev` + (plus likely libssl-dev, libxdo-dev, build-essential). These require `sudo apt` and a rustup + network installer — both need human authorization, so this is a blocking human-action checkpoint. + + Execute these commands (the executor presents them; the human runs/authorizes sudo): + - apt install: `sudo apt install libwebkit2gtk-4.1-dev build-essential curl wget file libxdo-dev libssl-dev libayatana-appindicator3-dev librsvg2-dev` + - Rust: `curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh -s -- -y` then `source "$HOME/.cargo/env"` + + + Run each and confirm a real version/path is printed (not "not found"): + 1. `rustc --version` prints a version >= 1.77.2 + 2. `cargo --version` prints a version + 3. `pkg-config --modversion webkit2gtk-4.1` prints a version (e.g. 2.x) + 4. `pkg-config --exists libssl && echo OK` prints OK + + + - `rustc --version` exits 0 and prints version >= 1.77.2 + - `pkg-config --modversion webkit2gtk-4.1` exits 0 (no "not found") + + Type "approved" once all four checks print versions, or describe which failed + + + + Task 2: Scaffold apps/desktop Tauri project as URL-loading wrapper + apps/desktop/package.json, apps/desktop/src-tauri/Cargo.toml, apps/desktop/src-tauri/tauri.conf.json, apps/desktop/src-tauri/build.rs, apps/desktop/src-tauri/src/main.rs, apps/desktop/src-tauri/src/lib.rs, apps/desktop/src-tauri/capabilities/default.json + + - .planning/phases/06-desktop-client-ci-cd/06-RESEARCH.md (Pattern 1 URL-Loading WebView, Recommended Project Structure, Cargo.toml Plugin Dependencies, Capabilities Configuration, Pitfall 6 monorepo scaffolding) + - pnpm-workspace.yaml (confirms apps/* glob already covers apps/desktop) + - apps/api/package.json (for name/version pattern: `@tessera/*`, version 0.0.1, private true) + + + Create `apps/desktop/` manually to avoid the monorepo scaffold confusion in Pitfall 6 (do NOT run + create-tauri-app at repo root). Set package.json name to `@tessera/desktop`, version `0.0.1`, private true, + with scripts `tauri` (runs `tauri`), `dev` (`tauri dev`), `build` (`tauri build`). Add devDependency + `@tauri-apps/cli@2.11.3` and dependencies `@tauri-apps/api@2.11.1` plus `@tauri-apps/plugin-store@2.4.3`. + Install via `pnpm add -D @tauri-apps/cli --filter=@tessera/desktop` and `pnpm add @tauri-apps/api @tauri-apps/plugin-store --filter=@tessera/desktop`. + + Create `src-tauri/Cargo.toml` with package name `tessera-desktop`, edition 2021, a `[lib]` entry named + `tessera_desktop_lib` (crate-type cdylib + staticlib + rlib), build-dependency `tauri-build = "2"`, and + dependencies: `tauri = { version = "2", features = ["tray-icon"] }`, `tauri-plugin-store = "2"`, + `serde = { version = "1", features = ["derive"] }`, `serde_json = "1"`. Only the store plugin is wired in + this plan; notification/autostart/window-state are added in 06-02. + + Create `src-tauri/build.rs` calling `tauri_build::build()`. + + Create `src-tauri/tauri.conf.json`: `productName` "Tessera", `version` "0.0.1", `identifier` + "de.ctl.tessera.desktop". Under `build`, set `frontendDist` to `../src` (the local setup page directory) and + `devUrl` to `http://localhost:1420`. Under `app.windows`, one window: label "main", title "Tessera", + width 1280, height 800, center true, decorations true, resizable true. Set `app.withGlobalTauri` true. + Add `app.security.csp` allowing connection to the configured server (use `default-src 'self'; connect-src *` + for the URL-configurable wrapper per D-02). Under `bundle`, set `active` true, `targets` `["appimage", "nsis"]`, + `icon` `["icons/icon.png", "icons/icon.ico"]` (icons added in 06-02 — note this in a SUMMARY follow-up if build + is attempted before icons exist). + + Create `src-tauri/src/main.rs` as the generated entry: `#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]` + then `fn main() { tessera_desktop_lib::run() }`. + + Create `src-tauri/src/lib.rs` exposing `pub fn run()`. Register the store plugin via + `tauri_plugin_store::Builder::new().build()`. In a `.setup(|app| { ... })` callback, open the store + `config.json`, read key `server_url`; if present, get the `main` webview window and call `window.navigate(url)` + to load the configured server; if absent, leave the WebView on the bundled setup page. Use + `tauri::generate_context!()` in `.run(...)`. + + Create `src-tauri/capabilities/default.json` with identifier "default", windows `["main"]`, permissions + `["core:default", "store:default"]` (additional permissions added in 06-02). + + + test -f apps/desktop/src-tauri/tauri.conf.json && test -f apps/desktop/src-tauri/src/lib.rs && grep -q '@tessera/desktop' apps/desktop/package.json && grep -q 'store:default' apps/desktop/src-tauri/capabilities/default.json && cd apps/desktop/src-tauri && cargo check 2>&1 | grep -qiv 'could not find webkit' + + + - `apps/desktop/package.json` name is `@tessera/desktop` + - `cargo check` inside `apps/desktop/src-tauri` completes without WebKitGTK "could not find" errors + - `tauri.conf.json` `frontendDist` is `../src` (not a hardcoded server URL) + - `lib.rs` reads `server_url` from store and calls `navigate` when present + + cargo check passes; pnpm recognizes @tessera/desktop; config loads the local setup page as frontendDist + + + + Task 3: First-run setup page — server URL input, validation, persistence, navigate + apps/desktop/src/setup.html + + - .planning/phases/06-desktop-client-ci-cd/06-RESEARCH.md (Pattern 3 Configurable Server URL, First-Run Setup Page code example, Security Domain — V5 Input Validation, malicious URL threat) + - apps/web/src/app/globals.css (OKLCH design tokens: --primary oklch(0.91 0.19 102), dark background oklch(0.17 0.01 260) — match setup page colors to the design system per D-06 spirit) + + + Create `apps/desktop/src/setup.html` as the local first-run page (D-02). It must: + - Render a centered card on dark background `oklch(0.17 0.01 260)` with a "Tessera" heading and a URL input + pre-filled with `http://localhost:3000`, plus a "Verbinden" button styled with primary `oklch(0.91 0.19 102)`. + All visible strings in German (response_language de): heading "Tessera", label "Server-URL eingeben:", + button "Verbinden", error text "Ungueltige URL" for invalid input. + - On submit, validate input with the `URL` constructor (per the established Phase 5 pattern, decision [05] + "URL constructor for client-side https-only validation"). Reject empty/malformed input; for non-https URLs + that are not localhost, show a warning string but allow (internal LAN servers may be http) — this mitigates + the malicious-URL Tampering threat T-06-01 (V5 input validation). + - On valid input, `import { load } from '@tauri-apps/plugin-store'`, `load('config.json', { autoSave: true })`, + `store.set('server_url', url)`, then `window.location.href = url` to navigate the WebView to the server. + - Use `