Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
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