From 3927926bfb25168bf75252ad3328d09ad9607ed2 Mon Sep 17 00:00:00 2001 From: Alex Southwell Date: Sat, 22 Aug 2026 15:24:58 +1000 Subject: [PATCH] fix(ci): make the devcontainer actually work The devcontainer.json that landed alongside an unrelated web styling change (0b1ce5882) does not produce a working environment: bun install skips the pnpm catalogs and the patched dependencies, the node feature is a major behind, nothing installs vp, there is no Rust toolchain for native/resource-monitor, no ports are forwarded, and everything runs as root. Rebuild it to mirror CI (ubuntu-24.04, Node 24, Rust stable) and the documented setup: the global vp CLI installs on create, and vp i plus the Electron exec-bit repair and the Vite dep-cache warmup run on content updates so Codespaces prebuilds stay warm. Named volumes for the pnpm store and root node_modules make rebuild installs take seconds and keep the heavy tree off slow macOS bind mounts. Dev ports are forwarded with pairing guidance, state stays in the workspace's gitignored .t3, and docs/internals/devcontainer.md documents what works and what stays host-only. Co-Authored-By: Claude Fable 5 --- .devcontainer/devcontainer.json | 82 +++++++++++++++++++++++++-------- .devcontainer/on-create.sh | 31 +++++++++++++ .devcontainer/update-content.sh | 19 ++++++++ docs/internals/devcontainer.md | 32 +++++++++++++ docs/internals/scripts.md | 3 ++ 5 files changed, 148 insertions(+), 19 deletions(-) create mode 100755 .devcontainer/on-create.sh create mode 100644 .devcontainer/update-content.sh create mode 100644 docs/internals/devcontainer.md diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cc1f11cd8baf..90ae22715482 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,29 +1,73 @@ +// Dev container for T3 Code. Mirrors CI (ubuntu-24.04, Node 24, Rust stable) +// and the canonical setup in docs/internals/scripts.md: global `vp`, `vp i`. +// Contributor doc: docs/internals/devcontainer.md { - "name": "T3 Code Dev", - "image": "debian:bookworm", + "name": "T3 Code", + "image": "mcr.microsoft.com/devcontainers/base:ubuntu-24.04", "features": { - "ghcr.io/devcontainers/features/git:1": {}, - "ghcr.io/devcontainers-extra/features/bun:1": { - "version": "1.3.11" + // nodeGypDependencies (default true) brings python3/make/g++, which Linux + // needs for node-pty's node-gyp fallback (its prebuilds are mac/win only). + "ghcr.io/devcontainers/features/node:2": { + "version": "24" }, - "ghcr.io/devcontainers/features/node:1": { - "version": "24.13.1" - }, - "ghcr.io/devcontainers/features/python:1": { - "version": "3.10", - "installTools": false - } + // native/resource-monitor (edition 2024, needs stable >= 1.85). The server + // degrades gracefully without the binary, but CI checks cargo fmt + test. + "ghcr.io/devcontainers/features/rust:1": {}, + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + "hostRequirements": { + "cpus": 4, + "memory": "8gb", + "storage": "32gb" + }, + "containerEnv": { + // Keep all runtime state inside the (gitignored) workspace .t3, matching + // the worktree default. An explicit --home-dir still wins. + "T3CODE_HOME": "${containerWorkspaceFolder}/.t3" }, - "overrideFeatureInstallOrder": [ - "ghcr.io/devcontainers/features/git", - "ghcr.io/devcontainers-extra/features/bun" + "mounts": [ + // vp keeps the pnpm content-addressable store and metadata cache under + // ~/.cache/pnpm (verified: the v11 store dir lives there); mounting a + // volume there lets installs survive container rebuilds. + "source=t3code-pnpm-store,target=/home/vscode/.cache/pnpm,type=volume", + // Root node_modules holds the whole pnpm virtual store (.pnpm), so one + // volume keeps the heavy tree off the slow macOS/Windows bind mount. + // Scoped by devcontainerId so parallel checkouts do not share it. + // Codespaces note: prebuild snapshots exclude volumes; drop these mounts + // if prebuilt codespaces become the primary workflow. + "source=t3code-node-modules-${devcontainerId},target=${containerWorkspaceFolder}/node_modules,type=volume" ], - "postCreateCommand": { - "bun-install": "bun install --backend=copyfile --frozen-lockfile" + "onCreateCommand": "bash .devcontainer/on-create.sh", + "updateContentCommand": "bash .devcontainer/update-content.sh", + "forwardPorts": [5733, 13773], + "portsAttributes": { + "5733": { + "label": "t3 web (open via the pairing URL, not the bare origin)", + "onAutoForward": "notify" + }, + "13773": { + "label": "t3 server", + "onAutoForward": "silent" + } }, "customizations": { + "codespaces": { + "openFiles": ["docs/internals/devcontainer.md"] + }, "vscode": { - "extensions": ["oxc.oxc-vscode"] + "extensions": ["oxc.oxc-vscode", "rust-lang.rust-analyzer"], + "settings": { + // .repos is a large vendored read-only reference tree; watching it + // burns CPU and file handles. + "files.watcherExclude": { + "**/.repos/**": true, + "**/.t3/**": true + }, + "search.exclude": { + "**/.repos": true + } + } } - } + }, + "remoteUser": "vscode" } diff --git a/.devcontainer/on-create.sh b/.devcontainer/on-create.sh new file mode 100755 index 000000000000..7aaf18a4a8ce --- /dev/null +++ b/.devcontainer/on-create.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# One-time container setup, baked into prebuilds. Content-dependent work +# (dependency install, Chromium) lives in update-content.sh. +set -euo pipefail + +# The Vite+ CLI is the repo task runner (vp i, vp run dev, vp test run). +# Download to a file first: a curl failure inside $( ) would yield an empty +# script and a false success. VP_NODE_MANAGER=no skips the installer's node +# shims; Node comes from the devcontainer feature. +installer=$(mktemp) +curl -fsSL https://vite.plus -o "$installer" +VP_NODE_MANAGER=no bash "$installer" +rm -f "$installer" + +# Non-login lifecycle shells never source the profile the installer edits, +# so expose vp on the default PATH. test -x keeps a layout change loud. +test -x "$HOME/.vite-plus/bin/vp" +sudo ln -sf "$HOME/.vite-plus/bin/vp" /usr/local/bin/vp + +# First-run terminal notice, rendered by the devcontainers base image. +sudo mkdir -p /usr/local/etc/vscode-dev-containers +sudo tee /usr/local/etc/vscode-dev-containers/first-run-notice.txt >/dev/null <<'EOF' +T3 Code devcontainer + + vp run dev start server + web, then open the pairing URL it + prints (the bare forwarded port will not authenticate) + cp .env.example .env optional: enable T3 Connect cloud features + (public identifiers, not secrets) + +Details: docs/internals/devcontainer.md +EOF diff --git a/.devcontainer/update-content.sh b/.devcontainer/update-content.sh new file mode 100644 index 000000000000..47424d62a5f4 --- /dev/null +++ b/.devcontainer/update-content.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +# Runs at creation and on every prebuild content refresh, so codespaces start +# with deps installed and caches warm. Everything here is idempotent. +set -euo pipefail + +# Volume mounts (pnpm store, node_modules) and the directories docker creates +# for them arrive root-owned; hand them to the dev user before installing. +for dir in "$HOME/.cache" "$HOME/.cache/pnpm" node_modules; do + if [ -d "$dir" ] && [ "$(stat -c %U "$dir")" != "$(id -un)" ]; then + sudo chown "$(id -un):$(id -gn)" "$dir" + fi +done + +vp i +# Repairs electron's path.txt and exec bits after install, same as CI. +vp run --filter @t3tools/desktop ensure:electron +# Pre-warms Vite's dep optimizer (cache is keyed on the absolute path, which +# is stable inside the container). +node apps/web/scripts/warm-dep-cache.ts diff --git a/docs/internals/devcontainer.md b/docs/internals/devcontainer.md new file mode 100644 index 000000000000..d740a9f5e159 --- /dev/null +++ b/docs/internals/devcontainer.md @@ -0,0 +1,32 @@ +# Dev container + +> For maintainers. Using T3 Code? See [docs/user](../user/). + +`.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. + +## What works in the container + +- 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`. +- Everything the Linux CI jobs run: `vp check`, `vp run typecheck`, `vp run test`, `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