chore(dev): reproducible dev container with Android + Flatpak toolchains preinstalled #237

Open
opened 2026-06-23 12:07:43 +00:00 by james · 0 comments
Owner

Context

Local development on Carol works on a vanilla Fedora / Debian / macOS host as long as you install the toolchain by hand (see CONTRIBUTING.md). The host install scales OK for the web target — mise gives you Node, pnpm, gitleaks, actionlint — but the native targets bring heavier system deps:

  • Android: JDK 21, Android cmdline-tools + platform / build-tools, ~6 GB.
  • Linux Flatpak: Rust toolchain, webkit2gtk-4.1-dev + ~10 other system libs, flatpak-builder, the GNOME Platform 48 + SDK Flatpak runtimes (~2 GB).

That's a lot for an "I'd like to try building locally" contributor. It's also bad on immutable distros (Aurora, Bazzite, Silverblue, Kinoite) where system deps need rpm-ostree layering + a reboot, or a toolbox container per build target. Today the message is "use the toolbox" and the CI runner re-installs everything on every release (#227 tracks baking that runner image — this ticket is its developer-side sibling).

A reproducible dev container — .devcontainer/-shaped, runnable in VS Code / Cursor / IntelliJ / devcontainer-cli — would mean: one docker pull and you have a working environment for every target Carol ships.

Source

User-requested (June 2026), after the PR #222 Flatpak landed and the system-deps friction on immutable Fedora variants became visible.

Scope

Build a Dev Containers spec under .devcontainer/:

  • Image base: pick the lightest viable Ubuntu LTS or Fedora image. Ubuntu has the easier-to-script Android SDK install; Fedora keeps things closer to the CI runner. Decide and document the call (one line in the devcontainer.json + the rationale in docs/ci.md or apps/client/README.md).
  • Tools the image preinstalls:
    • Carol's base toolchain via mise (read from .tool-versions): Node, pnpm, gitleaks, actionlint.
    • Android: OpenJDK 21, Android cmdline-tools, the SDK platforms + build-tools the Expo SDK 56 prebuild expects (mirror what release-android.yml installs).
    • Flatpak / Tauri: rustup with the toolchain pinned via apps/client/src-tauri/rust-toolchain.toml if present (else stable), webkit2gtk-4.1-dev + its build deps, flatpak, flatpak-builder. The GNOME Platform 48 + SDK runtimes preinstalled via flatpak install if the image supports it, otherwise pulled on first build.
    • Optional but useful: cosign, git-cliff, jq, the GitHub CLI / gh (despite Forgejo — for migration ease), ripgrep, fd.
  • devcontainer.json features:
    • Mount the workspace at /workspaces/carol.
    • Forward port 3000 (API dev), 8081 (Expo Metro), 19000-19002 (Expo dev/native).
    • postCreateCommand runs pnpm install so the container is immediately usable.
    • customizations.vscode.extensions: the obvious ones (ESLint, Prettier, Vitest, Expo Tools, rust-analyzer, Tauri).
    • runArgs includes --privileged only if Android emulator inside the container is in scope (likely not — see below).
  • Publishing: the image lives at forge.wynning.tech/james/carol-devcontainer:YYYY-MM-DD (or similar). Tagged + signed via cosign per the release pipeline pattern.

What this ticket explicitly does NOT cover

  • Android emulator inside the container. Running an emulator inside Docker needs KVM passthrough, which is fragile across hosts. Developers connect their physical device / host emulator and forward via adb; the container exposes the SDK + build tools.
  • macOS / Windows native builds in the container — Linux containers can't produce those.
  • Auto-syncing with the CI runner image (#227) — they share most deps but the dev container is bigger (editor tooling, dev ergonomics). #227 can layer on top of the dev container's base if convenient.

Acceptance criteria

  • .devcontainer/devcontainer.json + supporting Dockerfile / setup scripts live in the repo.
  • Opening the repo in VS Code with the Dev Containers extension installed gives a working environment for: pnpm -F @carol/api dev, pnpm -F @carol/client export:web, pnpm -F @carol/client build:android, pnpm -F @carol/client build:flatpak.
  • CONTRIBUTING.md gains a section pointing at the dev container as the recommended path; the host-install instructions stay as the fallback.
  • The image is published to a Carol-controlled registry; the devcontainer.json references it by digest (the same pinning pattern the production Dockerfile uses, per ADR-0014).
  • CI builds the image on a tag-driven schedule (e.g. monthly + on any change to .devcontainer/) and publishes the new digest.

Out of scope

  • Cloud Codespaces / GitHub Codespaces integration (the spec is portable; we just don't host it there).
  • IntelliJ-specific tweaks beyond what the standard devcontainer.json gives you.
  • Replacing the host-install path in CONTRIBUTING.md — that stays as a fallback.

Composes with

  • #227 — custom CI runner image. Same dep surface, different consumer; either could layer on the other's base.
  • CONTRIBUTING.md — gets a new section pointing at the dev container as the easy path.
  • docs/ci.md — release-pipeline notes; the dev container's publishing flow gets a paragraph here.
## Context Local development on Carol works on a vanilla Fedora / Debian / macOS host as long as you install the toolchain by hand (see [`CONTRIBUTING.md`](CONTRIBUTING.md)). The host install scales OK for the web target — mise gives you Node, pnpm, gitleaks, actionlint — but the native targets bring heavier system deps: - **Android**: JDK 21, Android `cmdline-tools` + platform / build-tools, ~6 GB. - **Linux Flatpak**: Rust toolchain, `webkit2gtk-4.1-dev` + ~10 other system libs, `flatpak-builder`, the GNOME Platform 48 + SDK Flatpak runtimes (~2 GB). That's a lot for an "I'd like to try building locally" contributor. It's also bad on immutable distros (Aurora, Bazzite, Silverblue, Kinoite) where system deps need rpm-ostree layering + a reboot, or a toolbox container per build target. Today the message is "use the toolbox" and the CI runner re-installs everything on every release ([#227](https://forge.wynning.tech/james/carol/issues/227) tracks baking that runner image — this ticket is its developer-side sibling). A reproducible dev container — `.devcontainer/`-shaped, runnable in VS Code / Cursor / IntelliJ / `devcontainer-cli` — would mean: one `docker pull` and you have a working environment for every target Carol ships. ## Source User-requested (June 2026), after the [PR #222](https://forge.wynning.tech/james/carol/pulls/222) Flatpak landed and the system-deps friction on immutable Fedora variants became visible. ## Scope Build a [Dev Containers](https://containers.dev) spec under `.devcontainer/`: - **Image base**: pick the lightest viable Ubuntu LTS or Fedora image. Ubuntu has the easier-to-script Android SDK install; Fedora keeps things closer to the CI runner. Decide and document the call (one line in the devcontainer.json + the rationale in `docs/ci.md` or `apps/client/README.md`). - **Tools the image preinstalls**: - Carol's base toolchain via mise (read from `.tool-versions`): Node, pnpm, gitleaks, actionlint. - Android: OpenJDK 21, Android `cmdline-tools`, the SDK platforms + build-tools the Expo SDK 56 prebuild expects (mirror what `release-android.yml` installs). - Flatpak / Tauri: rustup with the toolchain pinned via `apps/client/src-tauri/rust-toolchain.toml` if present (else stable), `webkit2gtk-4.1-dev` + its build deps, `flatpak`, `flatpak-builder`. The GNOME Platform 48 + SDK runtimes preinstalled via `flatpak install` if the image supports it, otherwise pulled on first build. - Optional but useful: `cosign`, `git-cliff`, `jq`, the GitHub CLI / `gh` (despite Forgejo — for migration ease), `ripgrep`, `fd`. - **devcontainer.json features**: - Mount the workspace at `/workspaces/carol`. - Forward port 3000 (API dev), 8081 (Expo Metro), 19000-19002 (Expo dev/native). - `postCreateCommand` runs `pnpm install` so the container is immediately usable. - `customizations.vscode.extensions`: the obvious ones (ESLint, Prettier, Vitest, Expo Tools, rust-analyzer, Tauri). - `runArgs` includes `--privileged` only if Android emulator inside the container is in scope (likely not — see below). - **Publishing**: the image lives at `forge.wynning.tech/james/carol-devcontainer:YYYY-MM-DD` (or similar). Tagged + signed via cosign per the release pipeline pattern. ## What this ticket explicitly does NOT cover - **Android emulator inside the container.** Running an emulator inside Docker needs KVM passthrough, which is fragile across hosts. Developers connect their physical device / host emulator and forward via `adb`; the container exposes the SDK + build tools. - **macOS / Windows native builds in the container** — Linux containers can't produce those. - **Auto-syncing with the CI runner image ([#227](https://forge.wynning.tech/james/carol/issues/227))** — they share most deps but the dev container is bigger (editor tooling, dev ergonomics). #227 can layer on top of the dev container's base if convenient. ## Acceptance criteria - [ ] `.devcontainer/devcontainer.json` + supporting Dockerfile / setup scripts live in the repo. - [ ] Opening the repo in VS Code with the Dev Containers extension installed gives a working environment for: `pnpm -F @carol/api dev`, `pnpm -F @carol/client export:web`, `pnpm -F @carol/client build:android`, `pnpm -F @carol/client build:flatpak`. - [ ] CONTRIBUTING.md gains a section pointing at the dev container as the recommended path; the host-install instructions stay as the fallback. - [ ] The image is published to a Carol-controlled registry; the devcontainer.json references it by digest (the same pinning pattern the production Dockerfile uses, per `ADR-0014`). - [ ] CI builds the image on a tag-driven schedule (e.g. monthly + on any change to `.devcontainer/`) and publishes the new digest. ## Out of scope - Cloud Codespaces / GitHub Codespaces integration (the spec is portable; we just don't host it there). - IntelliJ-specific tweaks beyond what the standard devcontainer.json gives you. - Replacing the host-install path in CONTRIBUTING.md — that stays as a fallback. ## Composes with - [#227](https://forge.wynning.tech/james/carol/issues/227) — custom CI runner image. Same dep surface, different consumer; either could layer on the other's base. - [`CONTRIBUTING.md`](CONTRIBUTING.md) — gets a new section pointing at the dev container as the easy path. - [`docs/ci.md`](docs/ci.md) — release-pipeline notes; the dev container's publishing flow gets a paragraph here.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
james/carol#237
No description provided.