Skip to content

Repository files navigation

Development Environment

A VS Code DevContainer configuration that provides isolated, reproducible development environments with intelligent host integration.

Features

  • Isolated Environment: Container with sandboxed home directory to prevent pollution of host home
  • Claude AI Integration: Automatic bind mounts for .claude config and state persistence
  • GPG Signing: Forwards host GPG agent socket into the container for commit and tag signing
  • GPU Passthrough: Exposes host render nodes to the webgpu (Vulkan) backend in both execution modes, by the mechanism each kernel driver needs (see AGENTS.md)
  • Smart Mount System: Selective bind mounts preserve tool configurations while isolating the container
  • Automatic Setup: Self-configuring initialisation script
  • Cross-Platform: Works on Windows, Linux, and macOS with automatic path translation
  • Development-Optimized Security: Runs with elevated privileges for debugging and advanced development scenarios (development use only)

Base Image

The development container is built on amery/docker-builder, extending the docker-apptly-builder image with:

  • Go: Full Go development environment
  • Node.js: JavaScript/TypeScript runtime and tooling
  • VS Code Server: Pre-configured for DevContainer support
  • Build Tools: Essential compilation and development utilities

The docker-builder project provides the foundation for this DevContainer, including the base images, build system, and container runtime wrapper. For details on available base images and the build infrastructure, see the docker-builder documentation.

Quick Start

Prerequisites

  • Docker, installed and running
    • Windows: Docker Desktop with the WSL 2 backend (WSL 2 itself must be installed and enabled)
    • macOS: Docker Desktop
    • Linux: Docker Engine
  • Visual Studio Code with the Dev Containers extension
  • Git on your PATH
    • Windows: install Git for Windows. GitHub Desktop's bundled git is not enough — a full git must be on your PATH for cloning and VS Code's Git integration.
  • Node.js on your PATH. VS Code's initializeCommand runs node .devcontainer/init.js on the host before building the container, so a standalone Node.js is required.
    • The runtime bundled inside VS Code (Electron) does not count: it is not exposed as a node command on PATH.

Setup

  1. Clone the repository:

    git clone <repository-url>
    cd dev-env
  2. Open in VS Code:

    code .
  3. When VS Code opens, click "Reopen in Container" when prompted (or use Command Palette → "Dev Containers: Reopen in Container")

The container will automatically initialise on first run. The initialisation process:

  • Detects your operating system (Windows, Linux, or macOS)
  • Runs the appropriate script (init.ps1 for Windows, init.sh for Unix-like systems)
  • Creates necessary directories and configures mounts for Claude AI integration
  • Handles path translation on Windows (e.g. C:\Users\john/C/Users/john)
  • Pulls the base image on first run (if not already present) to read its metadata
  • Generates a custom Dockerfile with user-specific metadata, and with the host's GPU render group when there is one

Project Structure

.devcontainer/        # VS Code DevContainer configuration
├── devcontainer.json # Container settings and mounts (auto-updated)
├── Dockerfile        # Auto-generated by init scripts
├── init.js           # Cross-platform entry point (Node.js)
├── init.sh           # Linux/macOS initialisation script
└── init.ps1          # Windows PowerShell initialisation script

.docker-run-cache/   # Container runtime storage (git-ignored)
└── ${HOME}/        # Sandboxed home directory

docker/              # Base container definitions
├── Dockerfile       # Base image configuration
├── gpu.sh           # Host GPU discovery (sourced by both modes)
└── run.sh          # Container runtime script

run.sh              # Symlink to docker/run.sh (workspace root marker)
go.work             # Go workspace configuration

Two Ways to Work

This environment supports two complementary workflows:

1. VS Code DevContainer (Interactive GUI)

Open the workspace in VS Code and use the "Reopen in Container" feature. The container runs continuously with VS Code Server, providing integrated terminal, debugging, and extensions.

  • Best for: Interactive development, debugging, IDE features
  • Lifecycle: Managed by VS Code (start, stop, rebuild)
  • Terminal: Opens directly inside container
  • User: Matches host UID/GID via entrypoint

2. Command-Line via x (CLI Trampoline)

Use the x helper to execute commands in containers. On the host, each invocation creates a new container via docker-builder-run. Inside the container, run.sh detects /.dockerenv and passes through directly.

  • Best for: Host-side builds, CI/CD, quick commands, nested workspaces
  • Lifecycle: Per-command (container created and destroyed)
  • Persistent State: Sandboxed home and tool configurations (.claude) survive across invocations
  • Terminal: Executes on host, trampolines to container
  • Container-Aware: Also works inside the container (passthrough)
  • User: Same UID/GID matching via entrypoint

Both workflows use the same base image and entrypoint, ensuring consistent behaviour. Scripts never include x in their commands; this makes them portable between DevContainer (direct execution) and host (x ./script.sh).

The x Helper

The x script provides intelligent workspace detection and command execution via the docker-builder-run trampoline. Both are published as release assets by docker-builder.

Installation

Download the latest x and docker-builder-run from docker-builder releases and place them on your PATH:

INSTALL=~/.local/bin
mkdir -p "$INSTALL"
BASE=https://github.com/amery/docker-builder/releases/latest/download
curl -L -o "$INSTALL/x" "$BASE/x"
curl -L -o "$INSTALL/docker-builder-run" "$BASE/docker-builder-run"
chmod +x "$INSTALL/x" "$INSTALL/docker-builder-run"

Usage

# Find workspace root
x --root

# Execute command in container (finds and calls run.sh)
x make build
x go test ./...
x pwd  # Returns actual directory, not /

# Works from any subdirectory
cd src/myproject
x make  # Still finds workspace root run.sh

# Pass through if no run.sh found
x echo "hello"

How It Works

  • Workspace Detection: Searches for run.sh by checking:

    • .repo directory (for repo tool workspaces)
    • Git workspace root: tries superproject first (git rev-parse --show-superproject-working-tree), falls back to repository root (git rev-parse --show-toplevel)
    • Parent directories up to filesystem root (brute force)
  • Trampoline Execution: When run.sh is found on the host:

    • x calls run.sh with your command
    • run.sh invokes docker-builder-run
    • docker-builder-run creates fresh container
    • Entrypoint sets up user and navigates to CURDIR
    • Your command executes in correct directory
  • Container Passthrough: If run.sh detects /.dockerenv (inside a container), it skips docker and executes the command directly

  • Fallback: If no run.sh is found, executes command directly on host

Key Features

  • Script Portability: Write scripts without x; they work in DevContainer and via x ./script.sh from host
  • Directory Preservation: Entrypoint sets CURDIR so x pwd returns actual directory
  • Submodule Aware: Finds correct workspace in nested Git repositories
  • Consistent Environment: Same image and entrypoint as DevContainer

Usage Examples

Combined Workflow

# Start VS Code DevContainer for interactive work
code .
# Click "Reopen in Container" when prompted

# In VS Code terminal (inside container):
./build.sh      # Runs directly in container
make test       # Runs in container environment

# From host terminal, use `x`:
x ./build.sh    # Creates fresh container, runs script
x make test     # Same behaviour as in DevContainer

# Both execute identically: never put 'x' in scripts

Working with Go Projects

# From any subdirectory, use `x` to run commands in the container
cd src/myapp
x go build ./...
x go test -v

# Check workspace root
x --root

# Directory preserved in container
x pwd           # Returns /path/to/src/myapp, not /

CI/CD Integration

# Invoke scripts via `x` from host (never include `x` in scripts)
x ./build.sh
x ./test.sh

# Or call `x` with commands directly
x make clean
x make build
x make test

# Scripts remain portable: work in both DevContainer and via `x`

Environment Variables

The container sets these environment variables:

  • WS: Workspace root path
  • CURDIR: Current working directory at invocation
  • GOPATH: Go path (set to workspace for Go projects)

Login shells additionally point CLI clients at a host Ollama daemon, when host.docker.internal resolves (a silent no-op otherwise):

  • OLLAMA_HOST: host.docker.internal:11434, unless already set
  • OPENAI_BASE_URL, OPENAI_API_KEY: srclight's embedding client has no Ollama override, so it rides Ollama's OpenAI-compatible API
  • SRCLIGHT_EMBED_REQUEST_TIMEOUT: raised for slow local embedding

The OpenAI block is skipped when OPENAI_BASE_URL is already set, so a real OpenAI endpoint configured earlier still wins. These come from the baked /etc/profile.d/ollama.sh (see docker/Dockerfile), not the entrypoint, so they apply to login shells in both modes. For the host-side reachability requirements, see AGENTS.md.

Troubleshooting

Common Issues

  • "Reopen in Container" not appearing: Ensure Docker is running and the Dev Containers extension is installed in VS Code
  • Mount permission errors: Run git status to ensure the repository is clean, then rebuild the container
  • Claude configuration not persisting: Rebuild the container (DevContainer) or verify docker-builder-run is installed (CLI)
  • Windows path issues: The system automatically detects WSL vs Docker Desktop and translates paths accordingly
  • Windows username issues: Special characters and spaces in Windows usernames are automatically sanitized for Linux compatibility

Rebuilding the Container

If you encounter issues, try rebuilding:

  1. Quick Rebuild: Command Palette → "Dev Containers: Rebuild Container"

    • Reuses cached Docker layers when possible
    • Faster but may not pick up base image updates
  2. Clean Rebuild: Command Palette → "Dev Containers: Rebuild Container Without Cache"

    • Removes all cached layers and rebuilds from scratch
    • Downloads latest base image updates
    • Ensures you have the newest versions of Go, Node.js, and tools

You can also access these options from VS Code's UI:

  • Click the remote indicator (bottom-left corner)
  • Select "Rebuild Container" or "Rebuild Container Without Cache"

Relationship with docker-builder

This project is a specialized implementation of docker-builder:

  • Extends: Uses docker-builder's docker-apptly-builder base image
  • Leverages: docker-builder-run for container execution
  • Demonstrates: How to create DevContainer environments on top of docker-builder infrastructure

When docker-builder is updated:

  • Base image improvements automatically benefit this environment
  • Breaking changes may require updates to the Dockerfile or configuration
  • Updates to docker-builder-run improve container execution

Documentation

License

See LICENCE.txt for details.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages