diff --git a/.gitignore b/.gitignore index 447f1f8..ae372c5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ **/target # Tauri generated ACL/permission schemas (regenerated on every desktop build) **/src-tauri/gen/ +# Desktop artifacts produced by the `client_desktop` compose service +/artifacts/ # Node node_modules .next diff --git a/README.md b/README.md index 47017c1..7946c9a 100644 --- a/README.md +++ b/README.md @@ -53,10 +53,11 @@ and relay, with no business logic. > (with logout/revocation), teams + 3-role RBAC, incident lifecycle, real-time > roster presence, timeline editing, emoji reactions, member moderation, > private messages, and GIPHY-powered GIF timeline entries, all on PostgreSQL -> (SQLx). Desktop is partially implemented as a Tauri URL-mode shell with tray -> and native assignment/high-severity notifications. Release management, -> `release_blocked` notifications, AppImage packaging, and the final compose -> desktop artifact remain open. +> (SQLx). Release management is implemented with step validation and automatic +> blocking by linked incidents. Desktop is partially implemented as a Tauri +> URL-mode shell with tray/background behavior and native assignment, +> high-severity, and `release_blocked` notifications. AppImage packaging and the +> release CI artifact remain open. ## Scope @@ -80,15 +81,16 @@ microservices instinct is honored where it pays, without distributed-systems tax - Private messages between users sharing a team, delivered over a user-scoped WebSocket event - GIPHY GIF search via a server-side API key and authenticated backend proxy +- Releases with ordered step validation and automatic blocking/unblocking by + linked incident state - `docker-compose` for server + db; local web via npm/Next.js; GitHub Actions CI/CD; FR/EN i18n **Extended Features** (in progress / planned) -- Tauri desktop URL-mode shell is present (OS notifications + tray); packaged - desktop binary/AppImage is still open -- Google OAuth2 exists as optional auth plumbing; Releases + automatic blocking - by a linked incident are still open +- Tauri desktop URL-mode shell is present (OS notifications + tray); Compose can + build a local `.deb`, while AppImage/release CI packaging remains open +- Google OAuth2 exists as optional auth plumbing - GitLab as an Action; additional REActions (Slack / HTTP / Email) **Long-term vision** @@ -134,6 +136,24 @@ curl http://localhost:8080/about.json # -> service catalog + SHA-256 token curl http://localhost:8081/en # -> 200, the web UI (FR at /fr) ``` +### Desktop app (Tauri, URL-mode) + +The desktop shell loads the web UI from `http://localhost:8081`, so it needs the +compose stack (or a dev server) running. In dev: `just desktop-dev`. + +A build-only `client_desktop` compose service builds an installable Linux package +in an Ubuntu/FHS container (the Tauri bundler can't run on a NixOS host) and drops +it on the host: + +```bash +docker compose --profile desktop up --build client_desktop +# -> ./artifacts/OpsWarden_amd64.deb (install: sudo apt install ./artifacts/OpsWarden_amd64.deb) +``` + +The **AppImage** is intentionally deferred to the release CI path (GitHub +`ubuntu-22.04` runner via `tauri-action`): Tauri's pinned `linuxdeploy` is broken +inside a local Docker container, so the local Compose path ships the `.deb`. + ### The project at a glance ```text @@ -246,10 +266,10 @@ handlers (Axum, WS) -> app (use-cases) -> ports (traits) -> domain (pure) **Desktop & delivery** - Tauri URL-mode shell reusing the front-end, with tray/background behavior -- Native OS notifications: assignment and high/critical severity are live-proven; - blocked Release is still open -- Final AppImage and `docker-compose.yml`: `server` 8080 / `client_web` 8081 / - `client_desktop` / `db` remain the packaging gate +- Native OS notifications: assignment, high/critical severity, and blocked + Release are live-proven +- Compose now covers `server` 8080 / `client_web` 8081 / `client_desktop` + build-only `.deb` / `db`; final AppImage + release CI remain the packaging gate - FR/EN i18n (labels, states, severities) persisted server-side ## Contributing diff --git a/client-desktop/Dockerfile b/client-desktop/Dockerfile new file mode 100644 index 0000000..4a83c13 --- /dev/null +++ b/client-desktop/Dockerfile @@ -0,0 +1,51 @@ +# --- client-desktop/Dockerfile --- +# +# Builds an OpsWarden desktop Linux package in an Ubuntu 22.04 (FHS) environment — +# the path Tauri's bundler expects, which it cannot take on the NixOS host (the +# bundler resolves libayatana-appindicator through Nix linker flags, see U1). +# ubuntu:22.04 (glibc 2.35) matches the platform `tauri-action` uses in CI. +# +# Local default = the `.deb` bundle: it builds reliably here and is a real +# installable Linux package (`apt install ./OpsWarden.deb`). The **AppImage** is +# deliberately left to CI/U4 (release.yml `tauri-action` on a GitHub ubuntu-22.04 +# runner via `--bundles appimage`): Tauri's pinned `linuxdeploy` v1-alpha fails its self- +# recursive AppImage invocation inside a local Docker container (`subprocess +# failed (exit code 6)`, identical on Debian 12 and Ubuntu 22.04) — a documented +# linuxdeploy/Docker fragility, not a config gap. The AppImage tooling +# (libfuse2 …) stays installed so the same recipe can `--bundles appimage` on a +# runner where linuxdeploy works. +# +# The shell is URL-mode (loads http://localhost:8081), so no web frontend is +# built here. The build-only service copies the artifact to a /out bind mount. + +FROM ubuntu:22.04 + +ENV DEBIAN_FRONTEND=noninteractive +# Tauri v2 Linux toolchain (matches the tauri-action recipe) + AppImage tooling +# (kept for the CI `--bundles appimage` path). +RUN apt-get update && apt-get install -y --no-install-recommends \ + curl wget ca-certificates build-essential pkg-config libssl-dev \ + libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev \ + patchelf file desktop-file-utils libfuse2 \ + && rm -rf /var/lib/apt/lists/* + +# Rust (stable) + Node 20 (NodeSource). +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain stable +ENV PATH="/root/.cargo/bin:${PATH}" +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y --no-install-recommends nodejs \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /app +# Use the root lockfile so the Tauri CLI version is deterministic. URL-mode +# desktop packaging does not build the web workspace. +COPY package.json package-lock.json ./ +COPY client-desktop/package.json ./client-desktop/package.json +RUN npm ci --workspace client-desktop --include-workspace-root=false --no-audit --no-fund +COPY client-desktop/ ./client-desktop/ +WORKDIR /app/client-desktop + +# Compile + bundle the `.deb`, then deposit it on the /out bind mount. NO_STRIP +# keeps patchelf from choking. (`--bundles appimage` is the CI override.) +ENV NO_STRIP=true +CMD ["sh", "-c", "npm run tauri build -- --bundles deb && mkdir -p /out && cp -v src-tauri/target/release/bundle/deb/*.deb /out/OpsWarden_amd64.deb && echo 'Desktop .deb ready at /out/OpsWarden_amd64.deb'"] diff --git a/docker-compose.yml b/docker-compose.yml index 0dd2b90..d7ee5db 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,5 +1,5 @@ -# S0 : server + db uniquement. -# client_web / client_desktop arrivent en Phase 3 (compose final + binaire desktop). +# Compose runtime: db + server + client_web. +# The build-only client_desktop service lives behind the optional `desktop` profile. services: db: image: postgres:18-alpine @@ -81,5 +81,23 @@ services: timeout: 5s retries: 12 + # Build-only desktop artifact: produces the installable Linux `.deb` in an + # Ubuntu/FHS container (Tauri's bundler cannot run on the NixOS host) and copies + # it to ./artifacts on the host, then exits. The AppImage is intentionally left + # to the release CI/U4 path (release.yml tauri-action) — Tauri's linuxdeploy is + # broken inside a local Docker container (see client-desktop/Dockerfile). Behind the `desktop` + # profile so a normal `docker compose up` stays lean (db + server + client_web). + # Generate with: + # docker compose --profile desktop up --build client_desktop + # -> ./artifacts/OpsWarden_amd64.deb + client_desktop: + profiles: ["desktop"] + build: + context: . + dockerfile: client-desktop/Dockerfile + volumes: + - ./artifacts:/out + restart: "no" + volumes: db_data: