authorgravatar for saphid@gmail.comAlex Southwell <saphid@gmail.com> 2026-09-07 17:15:40+10:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-07 00:15:40-07:00
log7376536b23e8db0d1b09054bbc0b919cf0c85405
treeb95e7905e5320e85b140cdf7cbea6de347ea2f38
parent5b68b2c8e1e971e4ee6956157a1698646ef52a55
signature Signed by PGP key B5690EEEBB952194

fix(devcontainer): make repository setup work (#7875)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

5 files changed, 154 insertions(+), 19 deletions(-)

.devcontainer/devcontainer.json+63-19
......@@ -1,29 +1,73 @@
1// Dev container for T3 Code. Mirrors CI (ubuntu-24.04, Node 24, Rust stable)
2// and the canonical setup in docs/operations/development.md: global `vp`, `vp i`.
3// Contributor doc: docs/internals/devcontainer.md
14{
2 "name": "T3 Code Dev",
3 "image": "debian:bookworm",
5 "name": "T3 Code",
6 "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04",
47 "features": {
5 "ghcr.io/devcontainers/features/git:1": {},
6 "ghcr.io/devcontainers-extra/features/bun:1": {
7 "version": "1.3.11"
8 // nodeGypDependencies (default true) brings python3/make/g++, which Linux
9 // needs for node-pty's node-gyp fallback (its prebuilds are mac/win only).
10 "ghcr.io/devcontainers/features/node:2": {
11 "version": "24"
812 },
9 "ghcr.io/devcontainers/features/node:1": {
10 "version": "24.13.1"
11 },
12 "ghcr.io/devcontainers/features/python:1": {
13 "version": "3.10",
14 "installTools": false
15 }
13 // native/resource-monitor (edition 2024, needs stable >= 1.85). The server
14 // degrades gracefully without the binary, but CI checks cargo fmt + test.
15 "ghcr.io/devcontainers/features/rust:1": {},
16 "ghcr.io/devcontainers/features/github-cli:1": {}
17 },
18 "hostRequirements": {
19 "cpus": 4,
20 "memory": "8gb",
21 "storage": "32gb"
22 },
23 "containerEnv": {
24 // Keep all runtime state inside the (gitignored) workspace .t3, matching
25 // the worktree default. An explicit --home-dir still wins.
26 "T3CODE_HOME": "${containerWorkspaceFolder}/.t3"
1627 },
17 "overrideFeatureInstallOrder": [
18 "ghcr.io/devcontainers/features/git",
19 "ghcr.io/devcontainers-extra/features/bun"
28 "mounts": [
29 // vp keeps the pnpm content-addressable store and metadata cache under
30 // ~/.cache/pnpm (verified: the v11 store dir lives there); mounting a
31 // volume there lets installs survive container rebuilds.
32 "source=t3code-pnpm-store,target=/home/vscode/.cache/pnpm,type=volume",
33 // Root node_modules holds the whole pnpm virtual store (.pnpm), so one
34 // volume keeps the heavy tree off the slow macOS/Windows bind mount.
35 // Scoped by devcontainerId so parallel checkouts do not share it.
36 // Codespaces note: prebuild snapshots exclude volumes; drop these mounts
37 // if prebuilt codespaces become the primary workflow.
38 "source=t3code-node-modules-${devcontainerId},target=${containerWorkspaceFolder}/node_modules,type=volume"
2039 ],
21 "postCreateCommand": {
22 "bun-install": "bun install --backend=copyfile --frozen-lockfile"
40 "onCreateCommand": "bash .devcontainer/on-create.sh",
41 "updateContentCommand": "bash .devcontainer/update-content.sh",
42 "forwardPorts": [5733, 13773],
43 "portsAttributes": {
44 "5733": {
45 "label": "t3 web (open via the pairing URL, not the bare origin)",
46 "onAutoForward": "notify"
47 },
48 "13773": {
49 "label": "t3 server",
50 "onAutoForward": "silent"
51 }
2352 },
2453 "customizations": {
54 "codespaces": {
55 "openFiles": ["docs/internals/devcontainer.md"]
56 },
2557 "vscode": {
26 "extensions": ["oxc.oxc-vscode"]
58 "extensions": ["oxc.oxc-vscode", "rust-lang.rust-analyzer"],
59 "settings": {
60 // .repos is a large vendored read-only reference tree; watching it
61 // burns CPU and file handles.
62 "files.watcherExclude": {
63 "**/.repos/**": true,
64 "**/.t3/**": true
65 },
66 "search.exclude": {
67 "**/.repos": true
68 }
69 }
2770 }
28 }
71 },
72 "remoteUser": "vscode"
2973}
.devcontainer/on-create.sh created+38
......@@ -0,0 +1,38 @@
1#!/usr/bin/env bash
2# One-time container setup, baked into prebuilds. Content-dependent work
3# (dependency install, Chromium) lives in update-content.sh.
4set -euo pipefail
5
6# The Vite+ CLI is the repo task runner (vp i, vp run dev, vp test run).
7# Download to a file first: a curl failure inside $( ) would yield an empty
8# script and a false success. VP_NODE_MANAGER=no skips the installer's node
9# shims; Node comes from the devcontainer feature. The VP_*_DIR group (set
10# together, absolute) pins the install location instead of the installer's
11# XDG platform defaults, which moved the binary from ~/.vite-plus/bin to
12# ~/.local/share/vite-plus/bin between releases and would break the check
13# below either way.
14export VP_BIN_DIR="$HOME/.local/share/vite-plus/bin"
15export VP_DATA_DIR="$HOME/.local/share/vite-plus"
16export VP_CACHE_DIR="$HOME/.cache/vite-plus"
17installer=$(mktemp)
18curl -fsSL https://vite.plus -o "$installer"
19VP_NODE_MANAGER=no bash "$installer"
20rm -f "$installer"
21
22# Non-login lifecycle shells never source the profile the installer edits,
23# so expose vp on the default PATH. test -x keeps a bad install loud.
24test -x "$VP_BIN_DIR/vp"
25sudo ln -sf "$VP_BIN_DIR/vp" /usr/local/bin/vp
26
27# First-run terminal notice, rendered by the devcontainers base image.
28sudo mkdir -p /usr/local/etc/vscode-dev-containers
29sudo tee /usr/local/etc/vscode-dev-containers/first-run-notice.txt >/dev/null <<'EOF'
30T3 Code devcontainer
31
32 vp run dev start server + web, then open the pairing URL it
33 prints (the bare forwarded port will not authenticate)
34 cp .env.example .env optional: enable T3 Connect cloud features
35 (public identifiers, not secrets)
36
37Details: docs/internals/devcontainer.md
38EOF
.devcontainer/update-content.sh created+19
......@@ -0,0 +1,19 @@
1#!/usr/bin/env bash
2# Runs at creation and on every prebuild content refresh, so codespaces start
3# with deps installed and caches warm. Everything here is idempotent.
4set -euo pipefail
5
6# Volume mounts (pnpm store, node_modules) and the directories docker creates
7# for them arrive root-owned; hand them to the dev user before installing.
8for dir in "$HOME/.cache" "$HOME/.cache/pnpm" node_modules; do
9 if [ -d "$dir" ] && [ "$(stat -c %U "$dir")" != "$(id -un)" ]; then
10 sudo chown "$(id -un):$(id -gn)" "$dir"
11 fi
12done
13
14CI=true vp i
15# Repairs electron's path.txt and exec bits after install, same as CI.
16vp run --filter @t3tools/desktop ensure:electron
17# Pre-warms Vite's dep optimizer (cache is keyed on the absolute path, which
18# is stable inside the container).
19node apps/web/scripts/warm-dep-cache.ts
docs/internals/devcontainer.md created+32
......@@ -0,0 +1,32 @@
1# Dev container
2
3> For maintainers. Using T3 Code? See [docs/user](../user/).
4
5`.devcontainer/` gives you a ready-to-code Linux environment matching CI: Ubuntu 24.04, Node 24, pnpm, Rust stable, the global `vp` CLI, and the GitHub CLI. Open the repo in VS Code and "Reopen in Container", or create a GitHub Codespace. Dependency install (`vp i`), the Electron exec-bit repair, and the Vite dep-cache warmup all run automatically before you attach.
6
7## What works in the container
8
9- The full dev stack: `vp run dev`, then open the pairing URL it prints through the forwarded web port (5733). The bare origin is useless without the pairing token. In VS Code the forwarded port is a true localhost, so the printed URL works as-is; in browser Codespaces the forwarded origin differs, and if the server rejects it, pass the forwarded origin via `T3CODE_DEV_ALLOWED_ORIGINS`.
10- Everything the Linux CI jobs run: focused `vp test run <files>`, `vp lint <files>`, package typechecks, `vp run build:desktop`, and the resource-monitor cargo build and tests. (`vpr` is not on PATH here; the curl installer only shims `vp`. Use `vp run <script>` or `node_modules/.bin/vpr` after install.)
11
12## State and safety
13
14`T3CODE_HOME` points at the workspace's gitignored `.t3`, so all runtime state stays inside the container workspace, mirroring the worktree default. There is no live install to damage inside a container, but the test-data rule from AGENTS.md still holds: copy data in, never point at shared state.
15
16## Caching
17
18Two named volumes keep rebuilds fast and installs off the slow macOS/Windows bind mount: the pnpm store (shared across checkouts, mounted where `vp i` keeps it) and root `node_modules` (per-container, which covers the whole `.pnpm` virtual store since workspace packages just symlink into it). Deleting a container and recreating it reuses both, so a rebuild's `vp i` is seconds, not minutes. The host sees an empty `node_modules`; run host-side tooling inside the container.
19
20## Out of scope
21
22- Windowed Electron development is host-only. Building and verifying the desktop bundle works fine in the container (CI does exactly that, headless); launching the app needs a display.
23- Mobile native builds are host-only (Xcode for iOS, Android SDK for Android). Typecheck, lint, and the mobile static checks run fine.
24- `vp run dev --share` needs a tailscale binary and a tailnet; not provisioned here.
25
26## Prebuilds
27
28Container creation from scratch does a full `vp i` plus toolchain installs, which is worth prebuilding. Codespaces prebuilds are configured in repo settings, not files, and pick this config up as-is: the heavy steps live in `onCreateCommand` and `updateContentCommand`, which prebuilds bake in. Restrict prebuilds to one region and one retained version; storage bills per region per version. Note that prebuild snapshots exclude the caching volumes, so a prebuild-first workflow may prefer dropping the mounts. Outside Codespaces, the Dev Container CLI can push a prebuilt image:
29
30```bash
31devcontainer build --workspace-folder . --push true --image-name <registry>/t3code-devcontainer:latest
32```
docs/operations/development.md+2
......@@ -13,6 +13,8 @@ vp run dev
1313Open the one-time pairing URL printed by the dev runner. The bare origin does not authenticate
1414a new browser.
1515
16Prefer a container? See [Dev container](../internals/devcontainer.md) for VS Code and Codespaces setup.
17
1618## Choosing a dev process
1719
1820Use `vp run dev` for server and web, or `vp run dev:desktop` for the Electron client.