Skip to content

Replace manual Dockerfile generation with native devcontainer CLI for 10-second worker startup #38

@buremba

Description

@buremba

Summary

Implement native @devcontainers/cli integration to replace manual Dockerfile generation, enabling full devcontainer feature support and reducing worker startup time from 2-3 minutes to under 10 seconds.

Current Problem

Our current implementation:

  • Manually parses devcontainer.json to extract only the base image
  • Misses all devcontainer features (databases, tools, extensions, postCreateCommand, etc.)
  • Requires workers to clone repos and install dependencies on every startup (2-3 min)
  • Maintains complex custom Dockerfile generation logic
  • Cannot leverage the rich devcontainer ecosystem that users expect

Proposed Solution

Use @devcontainers/cli to build worker images with:

  • Full devcontainer feature support (all features, not just base image)
  • Repository pre-cloned during image build
  • All dependencies and tools pre-installed
  • Workers only need to git pull for updates (seconds, not minutes)

Implementation Plan

1. Add Dependencies

  • Add @devcontainers/cli to packages/orchestrator/package.json
  • Add @anthropic-ai/claude-code to packages/worker/package.json
  • Remove separate Claude CLI installation steps

2. Create Devcontainer Builder Module

New file: packages/orchestrator/src/devcontainer-builder.ts

export class DevcontainerBuilder {
  async build(repoUrl: string): Promise<string> {
    // 1. Shallow clone repo to temp directory
    // 2. Check for .devcontainer/devcontainer.json
    // 3. Run: npx @devcontainers/cli build --workspace-folder /tmp/repo
    // 4. Add termos packages via post-installation
    // 5. Return image tag with content hash for caching
  }
}

3. Update Queue Consumer for Status Updates

File: packages/orchestrator/src/task-queue-consumer.ts

  • Add status update messages during build:
    • "🔧 Building your development environment..."
    • "📋 Applying devcontainer configuration..."
    • "📦 Installing project dependencies..."
    • "✅ Environment ready! Starting Claude Code..."
  • Call DevcontainerBuilder instead of manual Dockerfile generation

4. Simplify Worker Startup

File: packages/worker/scripts/worker-entrypoint.sh

  • Remove build steps (already in image)
  • Change from git clone to git pull
  • Remove MCP server setup (already in image)
  • Reduce from 170 lines to ~50 lines

5. Remove All Legacy Code

  • DELETE: Dockerfile.worker - no longer needed
  • DELETE: Manual Dockerfile generation logic
  • DELETE: Custom feature parsing code
  • DELETE: Unused build scripts
  • No backwards compatibility, no feature flags

6. Create termos Integration Feature

New directory: .devcontainer-features/termos-integration/

{
  "id": "termos-integration",
  "name": "termos Worker Integration",
  "options": {},
  "postCreateCommand": [
    "npm install -g /app/packages/worker",
    "npm install -g /app/packages/shared"
  ]
}

7. Development Mode with Hot Reload

if (process.env.NODE_ENV === 'development') {
  // Mount source code as volumes for hot reload
  return {
    image: 'termos-worker:dev',
    volumes: [
      './packages/worker:/app/packages/worker',
      './packages/shared:/app/packages/shared'
    ],
    command: 'bun --watch /app/packages/worker/src/index.ts'
  };
}

Acceptance Criteria

  • Full devcontainer.json support (not just base image):
    • Features (databases, tools, runtimes)
    • postCreateCommand, postStartCommand
    • customizations
    • VS Code extensions
  • Worker startup time < 10 seconds (git pull only)
  • Status messages visible in Slack during build
  • Image caching by content hash works
  • Default Bun environment for repos without devcontainer
  • Hot reload in development mode
  • All legacy Dockerfile code removed
  • Claude Code CLI available without separate install
  • MCP process manager pre-configured

Example Commands

Build with devcontainer CLI:

npx @devcontainers/cli build \
  --workspace-folder /tmp/user-repo \
  --image-name termos-worker:abc123 \
  --cache-from termos-worker:latest

Detect devcontainer:

const devcontainerPath = '.devcontainer/devcontainer.json';
const config = await github.getFileContent(repoUrl, devcontainerPath, 'main');

Performance Impact

  • Current: 2-3 minute worker startup (clone + install)
  • After: <10 second worker startup (pull only)
  • User Experience: Near-instant response to Slack commands
  • Resource Usage: Reduced CPU/memory from avoiding repeated builds

Testing Requirements

  1. Repository with complex devcontainer (Python + PostgreSQL + Redis)
  2. Repository with simple devcontainer (Node.js only)
  3. Repository without devcontainer (uses default)
  4. Verify all devcontainer features work
  5. Test image caching and reuse
  6. Validate hot reload in development
  7. Confirm Slack status messages appear

Benefits

  • ✅ 95% faster worker startup
  • ✅ Full devcontainer ecosystem support
  • ✅ Less code to maintain
  • ✅ Industry-standard tooling
  • ✅ Better user experience
  • ✅ Reduced cloud costs (less compute time)

@claude - Please implement this following the clean, lean approach. Remove all legacy code, no backwards compatibility needed.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions