Skip to content
Draft
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
8 changes: 8 additions & 0 deletions .devcontainer/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ ENV TZ="$TZ"
ARG CLAUDE_CODE_VERSION=latest

# Install basic development tools and iptables/ipset
# Note: X11 libraries (libxext6, libxrender1, etc.) are required for JetBrains Gateway
# https://www.jetbrains.com/help/rust/prerequisites-for-dev-containers.html
RUN apt-get update && apt-get install -y --no-install-recommends \
less \
git \
Expand All @@ -46,6 +48,12 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
nano \
vim \
tmux \
curl \
libxext6 \
libxrender1 \
libxtst6 \
libxi6 \
libfreetype6 \
&& apt-get clean && rm -rf /var/lib/apt/lists/*

# Ensure default node user has access to /usr/local/share
Expand Down
142 changes: 100 additions & 42 deletions .devcontainer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ This directory contains the devcontainer configuration for the Langstar project.

## Overview

The devcontainer uses Docker Compose which provides **native `.env` file support**, solving environment variable management elegantly for both local and Codespaces environments.
The devcontainer uses Docker Compose with **three separate configurations** optimized for different development environments:

### Architecture
| Config | Location | Best For | Workspace | Secrets |
|--------|----------|----------|-----------|---------|
| **Default** | `.devcontainer/` | VS Code local | Bind mount | `.env` file |
| **Codespaces** | `.devcontainer/codespaces/` | GitHub Codespaces | Cloud-managed | GitHub secrets |
| **JetBrains** | `.devcontainer/jetbrains/` | RustRover, IntelliJ | Named volume | `.env` via bind mount |

- **Local Development**: Docker Compose automatically loads `.env` file
- **GitHub Codespaces**: Environment variables come from Codespaces secrets
- **Single Configuration**: No duplication, standard Docker Compose patterns
### Why Three Configurations?

Each environment has unique requirements:

- **VS Code Local**: Bind mounts project from host, uses local `.env` file
- **Codespaces**: Cloud-based, no local files - must use GitHub Codespaces secrets (`.env` is gitignored and doesn't exist in cloud)
- **JetBrains**: Needs named volume for native inotify support (avoids gRPC FUSE file watching issues), but also needs access to local `.env` file via hybrid mount

## Local Development Setup

Expand Down Expand Up @@ -54,36 +62,55 @@ The devcontainer uses Docker Compose which provides **native `.env` file support

### Workspace Mount Configuration

By default, this devcontainer uses a **bind mount** (`..:/workspace:cached`) for maximum compatibility with VS Code, JetBrains, and GitHub Codespaces.
By default, this devcontainer uses a **bind mount** (`..:/workspace:cached`) which works well for VS Code.

**Default behavior:**
- Your local repository files are mounted directly into the container
- Changes sync bidirectionally between host and container
- Standard "Reopen in Container" workflow works seamlessly

#### Optional: Named Volume for JetBrains IDEs
## JetBrains Gateway Setup

If you're using **RustRover, IntelliJ**, or other JetBrains IDEs, use the dedicated JetBrains configuration at `.devcontainer/jetbrains/`.

This configuration uses a **hybrid mount approach** that provides:
- Native inotify support (no "External file changes sync might be slow" warnings)
- Access to your local `.env` file without baking secrets into the image

### JetBrains Prerequisites

If you're using **RustRover, IntelliJ**, or other JetBrains IDEs and see the warning:
> "External file changes sync might be slow"
1. **Local checkout required**: Have a local checkout of the repo with `.devcontainer/.env` configured
2. **SSH agent**: Ensure SSH agent is running on your host machine

This occurs because Docker's bind mount on macOS uses gRPC FUSE, which doesn't provide full `inotify` support.
### JetBrains Setup Steps

**To fix this**, you can switch to a named Docker volume by editing `docker-compose.override.yml`:
1. Open JetBrains Gateway
2. Select **Remote Development > Dev Containers**
3. Click **Clone Repository**
4. Enter the repository URL
5. When prompted for devcontainer path, select: `.devcontainer/jetbrains/devcontainer.json`
6. Gateway will clone into a named volume and bind-mount your local `.devcontainer` for secrets

### How the JetBrains Hybrid Mount Works

```
/workspace → Named volume (cloned by JetBrains, native inotify)
/workspace/.devcontainer → Bind mount from host (provides .env file)
```

1. Copy the template: `cp docker-compose.override.yml.template docker-compose.override.yml`
2. Uncomment the volumes section for named volume
3. Rebuild the container
4. Clone the repo inside the container: `cd /workspace && git clone <repo-url> .`
This allows:
- Full inotify support for file watching (no gRPC FUSE)
- Access to your local `.env` file for secrets
- Code lives in the named volume (cloned separately by JetBrains)

**Trade-offs of named volume:**
| Aspect | Bind Mount (default) | Named Volume (optional) |
|--------|---------------------|------------------------|
| File watching | Limited (gRPC FUSE) | Full inotify support |
| File location | Host filesystem | Container volume |
| Setup workflow | Standard | Requires clone into container |
| Host editing | Supported | Not supported |
### JetBrains Trade-offs

See `docker-compose.override.yml.template` for detailed instructions.
| Aspect | VS Code (default) | JetBrains (hybrid) |
|--------|-------------------|-------------------|
| File watching | gRPC FUSE (limited) | Native inotify |
| Code location | Host filesystem | Named volume |
| Secrets | Direct from `.env` | `.env` via bind mount |
| Prerequisites | Just `.env` | Local checkout + `.env` |

### Files Created (Gitignored)

Expand All @@ -94,9 +121,11 @@ These files are created locally and **will not be committed** to git:

## GitHub Codespaces Setup

### Configure Codespaces Secrets
For Codespaces, use the dedicated configuration at `.devcontainer/codespaces/`.

**Why a separate config?** The `.env` file is gitignored and doesn't exist in Codespaces. The Codespaces config is designed to work exclusively with GitHub Codespaces secrets, with no `.env` file dependency.

Codespaces uses repository or organization secrets instead of local `.env` files.
### Configure Codespaces Secrets

1. **Go to your repository settings:**
- Navigate to `Settings` → `Secrets and variables` → `Codespaces`
Expand All @@ -117,30 +146,52 @@ Codespaces uses repository or organization secrets instead of local `.env` files
- Click the green "Code" button
- Select "Codespaces" tab
- Click "Create codespace on main" (or your branch)
- When prompted, select: `.devcontainer/codespaces/devcontainer.json`

### How Codespaces Works
### How Codespaces Config Works

In Codespaces:
- The `devcontainer.json` uses Docker Compose configuration
- `docker-compose.yml` uses fallback syntax: `${GITHUB_PAT:-${GH_PAT}}`
- Environment variables come from Codespaces secrets (`GH_PAT`, `GH_USER`, etc.)
- No `.env` file is needed or used
The Codespaces configuration:
- Uses `docker-compose.yml` with direct secret references (`${GH_PAT}`, not fallback syntax)
- Has NO `env_file` directive (would fail since `.env` doesn't exist)
- All environment variables come from Codespaces secrets
- `setup-github-auth.sh` configures git authentication using the provided variables

## Architecture

### Directory Structure

```
.devcontainer/
├── devcontainer.json # VS Code local (default)
├── docker-compose.yml # VS Code compose (bind mount, .env)
├── Dockerfile # Shared container image
├── .env.default # Environment template
├── .env # Local secrets (gitignored)
├── post-create.sh # Shared setup script
├── setup-github-auth.sh # Shared auth script
├── codespaces/ # GitHub Codespaces config
│ ├── devcontainer.json # Codespaces-specific
│ └── docker-compose.yml # No .env dependency
└── jetbrains/ # JetBrains Gateway config
├── devcontainer.json # JetBrains-specific
└── docker-compose.yml # Hybrid volume mount
```

### Configuration Files

| File | Purpose | Committed to Git | Environment |
|------|---------|------------------|-------------|
| `devcontainer.json` | Dev Container config (points to Docker Compose) | ✅ Yes | Both |
| `docker-compose.yml` | Docker Compose service definition | ✅ Yes | Both |
| `docker-compose.override.yml.template` | Template for local Docker overrides | ✅ Yes | Both |
| `docker-compose.override.yml` | Local Docker Compose overrides | ❌ No (gitignored) | Local only |
| `.env.default` | Environment variables template | ✅ Yes | Both |
| `.env` | Actual environment variables | ❌ No (gitignored) | Local only |
| `Dockerfile` | Container image definition | ✅ Yes | Both |
| `setup-github-auth.sh` | Git authentication setup | ✅ Yes | Both |
| File | Purpose | Committed | Used By |
|------|---------|-----------|---------|
| `devcontainer.json` | Default Dev Container config | ✅ Yes | VS Code local |
| `docker-compose.yml` | VS Code compose (bind mount) | ✅ Yes | VS Code local |
| `codespaces/devcontainer.json` | Codespaces config | ✅ Yes | GitHub Codespaces |
| `codespaces/docker-compose.yml` | Codespaces compose (no .env) | ✅ Yes | GitHub Codespaces |
| `jetbrains/devcontainer.json` | JetBrains config | ✅ Yes | RustRover, IntelliJ |
| `jetbrains/docker-compose.yml` | JetBrains compose (hybrid mount) | ✅ Yes | RustRover, IntelliJ |
| `Dockerfile` | Container image definition | ✅ Yes | All |
| `.env.default` | Environment variables template | ✅ Yes | VS Code, JetBrains |
| `.env` | Actual environment variables | ❌ No | VS Code, JetBrains |
| `docker-compose.override.yml.template` | Template for local overrides | ✅ Yes | Reference only |
| `docker-compose.override.yml` | Local overrides (deprecated) | ❌ No | Legacy |

### How Docker Compose Environment Variables Work

Expand Down Expand Up @@ -250,7 +301,11 @@ services:

**Problem:** Local overrides in `docker-compose.override.yml` aren't applied

**Solution:**
**Note:** The override file approach is deprecated. Use the dedicated configs instead:
- **JetBrains**: Use `.devcontainer/jetbrains/` instead
- **Codespaces**: Use `.devcontainer/codespaces/` instead

**If you still need override files:**
1. Ensure file is named exactly `docker-compose.override.yml` (not `.template`)
2. Verify it's in `.devcontainer/` directory
3. Check YAML syntax is valid: `docker-compose config`
Expand Down Expand Up @@ -340,6 +395,9 @@ docker-compose config | grep -A 10 environment:

## Related Issues

- [#718](https://github.com/codekiln/langstar/issues/718) - Multi-environment devcontainer setup (VS Code, Codespaces, JetBrains)
- [#711](https://github.com/codekiln/langstar/issues/711) - JetBrains OOM crash from gRPC FUSE
- [#712](https://github.com/codekiln/langstar/pull/712) - Named volume configuration for JetBrains
- [#33](https://github.com/codekiln/langstar/issues/33) - Fix devcontainer .env file handling for Codespaces compatibility
- [#23](https://github.com/codekiln/langstar/issues/23) - Refactor to use `GH_*` variables for Codespaces compatibility
- [#26](https://github.com/codekiln/langstar/issues/26) - Removed problematic `env` section from `.claude/settings.json`
Expand Down
53 changes: 53 additions & 0 deletions .devcontainer/codespaces/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "langstar-codespaces",
// Use Docker Compose for container configuration
"dockerComposeFile": "docker-compose.yml",
"service": "langstar-dev",
"workspaceFolder": "/workspace",

// Dev Container features (installed by VS Code, not Docker Compose)
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {},
"ghcr.io/devcontainers-extra/features/mise:1": {},
"ghcr.io/devcontainers-extra/features/uv:1": {},
"ghcr.io/devcontainers/features/rust:1": {},
"ghcr.io/codekiln/langstar/langstar:1": {
"version": "2.1.2"
}
},

// VS Code customizations
"customizations": {
"vscode": {
"extensions": [
"anthropic.claude-code",
// container tools for docker
"ms-azuretools.vscode-containers",
"ms-azuretools.vscode-docker",
// python language support
"ms-python.python"
],
"settings": {
"files.autoSave": "afterDelay",
"editor.formatOnSave": true,
"terminal.integrated.defaultProfile.linux": "zsh",
"terminal.integrated.profiles.linux": {
"bash": {
"path": "bash",
"icon": "terminal-bash"
},
"zsh": {
"path": "zsh"
}
}
}
}
},

"remoteUser": "node",

// Lifecycle commands
// Note: Scripts are in parent directory, referenced via /workspace path
"postCreateCommand": "bash /workspace/.devcontainer/post-create.sh",
"postStartCommand": "bash /workspace/.devcontainer/setup-github-auth.sh"
}
103 changes: 103 additions & 0 deletions .devcontainer/codespaces/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Docker Compose configuration for GitHub Codespaces
# Compose file format: https://docs.docker.com/compose/compose-file/
#
# IMPORTANT: This configuration is specifically designed for GitHub Codespaces.
# It does NOT depend on .env files (which are gitignored and don't exist in Codespaces).
# All environment variables come from GitHub Codespaces secrets.
#
# For local VS Code development, use the default config at ../.devcontainer/
# For JetBrains Gateway, use .devcontainer/jetbrains/

services:
langstar-dev:
build:
context: ..
dockerfile: Dockerfile
# Build arguments with defaults (Codespaces secrets can override)
# https://docs.docker.com/compose/compose-file/build/#args
args:
TZ: ${TZ:-America/New_York}
CLAUDE_CODE_VERSION: ${CLAUDE_CODE_VERSION:-latest}
GIT_DELTA_VERSION: ${GIT_DELTA_VERSION:-0.18.2}
ZSH_IN_DOCKER_VERSION: ${ZSH_IN_DOCKER_VERSION:-1.2.1}

# Add Linux capabilities for VPN/network tools
# https://docs.docker.com/compose/compose-file/compose-file-v3/#cap_add
cap_add:
- NET_ADMIN
- NET_RAW

# Environment variables from GitHub Codespaces secrets
# https://docs.github.com/en/codespaces/managing-your-codespaces/managing-encrypted-secrets-for-your-codespaces
#
# IMPORTANT: NO fallback syntax (${VAR:-${OTHER}}) - Codespaces secrets only.
# The .env file does NOT exist in Codespaces (it's gitignored).
# All secrets must be configured in GitHub: Settings → Secrets → Codespaces
#
# Required Codespaces secrets:
# - GH_PAT: GitHub Personal Access Token
# - GH_USER: GitHub username
# - AWS_ACCESS_KEY_ID: AWS access key for Bedrock
# - AWS_SECRET_ACCESS_KEY: AWS secret key for Bedrock
# - LANGSMITH_API_KEY: LangSmith API key
environment:
# GitHub Authentication (from Codespaces secrets)
GITHUB_PAT: ${GH_PAT}
GITHUB_USER: ${GH_USER}
GITHUB_PROJECT_PAT: ${GH_PROJECT_PAT}

# Anthropic/AWS Configuration (from Codespaces secrets)
AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
ANTHROPIC_MODEL: ${ANTHROPIC_MODEL:-us.anthropic.claude-sonnet-4-5-20250929-v1:0}
ANTHROPIC_SMALL_FAST_MODEL: ${ANTHROPIC_SMALL_FAST_MODEL:-us.anthropic.claude-haiku-4-5-20251001-v1:0}
AWS_REGION: ${AWS_REGION:-us-east-1}
CLAUDE_CODE_USE_BEDROCK: ${CLAUDE_CODE_USE_BEDROCK:-1}

# LangChain API Configuration (from Codespaces secrets)
LANGSMITH_API_KEY: ${LANGSMITH_API_KEY}
LANGSMITH_ORGANIZATION_NAME: ${LANGSMITH_ORGANIZATION_NAME}
LANGSMITH_ORGANIZATION_ID: ${LANGSMITH_ORGANIZATION_ID}
LANGSMITH_WORKSPACE_NAME: ${LANGSMITH_WORKSPACE_NAME}
LANGSMITH_WORKSPACE_ID: ${LANGSMITH_WORKSPACE_ID}

# Test LangGraph Deployment (optional)
TEST_GRAPH_ID: ${TEST_GRAPH_ID}
TEST_DEPLOYMENT_ID: ${TEST_DEPLOYMENT_ID}

# Container Environment (static values)
NODE_OPTIONS: --max-old-space-size=4096
CLAUDE_CONFIG_DIR: /home/node/.claude
POWERLEVEL9K_DISABLE_GITSTATUS: "true"

# tmux skill configuration
CLAUDE_TMUX_SOCKET_DIR: /tmp/claude-tmux-sockets

# Claude Code Token Configuration
CLAUDE_CODE_MAX_OUTPUT_TOKENS: ${CLAUDE_CODE_MAX_OUTPUT_TOKENS:-4096}
MAX_THINKING_TOKENS: ${MAX_THINKING_TOKENS:-1024}

# Legacy variables (optional)
GITHUB_TOKEN: ${GITHUB_TOKEN}
OPENAI_API_KEY: ${OPENAI_API_KEY}

# Volume mounts
# https://docs.docker.com/compose/compose-file/compose-file-v3/#volumes
volumes:
# Bind mount workspace (Codespaces manages this in cloud)
- ../..:/workspace:cached

# Named volumes for persistent data across container rebuilds
- claude-code-bashhistory:/commandhistory
- claude-code-config:/home/node/.claude

# Keep container running (required for devcontainers)
command: sleep infinity

# Run container as 'node' user (matches Dockerfile USER directive)
user: node

# Named volumes declaration
volumes:
claude-code-bashhistory:
claude-code-config:
Loading
Loading