Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
44 changes: 32 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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**
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
51 changes: 51 additions & 0 deletions client-desktop/Dockerfile
Original file line number Diff line number Diff line change
@@ -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'"]
22 changes: 20 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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: