commit 3dfc14c6c1eccd74ca759b412e0209b08ddc7ced Author: Joakim Persson Date: Thu Apr 9 00:26:48 2026 +0200 Initial scaffold: Debian-based opencode v1.4.0 dev container Dockerfile with Node.js 22, git, ssh, fzf, ripgrep, fd, non-root user. Entrypoint auto-configures provider from env vars. docker-compose with workspace mount, SSH keys, and persistent data volume. diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..efa3363 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +.git +.env +*.md +LICENSE +.DS_Store diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..5f47642 --- /dev/null +++ b/.env.example @@ -0,0 +1,32 @@ +# opencode-devbox environment configuration +# Copy this file to .env and fill in your values: +# cp .env.example .env + +# ── LLM Provider ───────────────────────────────────────────────────── +# Which provider to auto-configure (anthropic, openai, amazon-bedrock) +OPENCODE_PROVIDER=anthropic + +# Model override (optional, defaults per provider) +# OPENCODE_MODEL=anthropic/claude-sonnet-4-5 + +# ── API Keys (set the one matching your provider) ──────────────────── +ANTHROPIC_API_KEY= +# OPENAI_API_KEY= +# GEMINI_API_KEY= + +# ── AWS Bedrock (if using amazon-bedrock provider) ─────────────────── +# AWS_REGION=eu-west-1 +# AWS_PROFILE=default +# AWS_ACCESS_KEY_ID= +# AWS_SECRET_ACCESS_KEY= + +# ── Git Configuration ──────────────────────────────────────────────── +GIT_USER_NAME= +GIT_USER_EMAIL= + +# ── Workspace ──────────────────────────────────────────────────────── +# Path on host to mount as /workspace in the container +WORKSPACE_PATH=~/projects + +# Path to SSH keys on host +SSH_KEY_PATH=~/.ssh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ecb473 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.env +*.swp +*.swo +*~ +.DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..c2b3335 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,98 @@ +# opencode-devbox — portable AI dev environment +# Debian-based container with opencode and configurable dev tools + +ARG DEBIAN_VERSION=bookworm-slim +FROM debian:${DEBIAN_VERSION} AS base + +ARG TARGETARCH +ARG OPENCODE_VERSION=1.4.0 + +LABEL maintainer="joakimp" +LABEL description="Portable opencode developer container" +LABEL org.opencontainers.image.source="https://gitea.jordbo.se/joakimp/opencode-devbox" + +# Avoid interactive prompts during build +ENV DEBIAN_FRONTEND=noninteractive + +# ── Core system packages ───────────────────────────────────────────── +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + wget \ + git \ + git-lfs \ + openssh-client \ + gnupg \ + jq \ + ripgrep \ + fd-find \ + fzf \ + tree \ + less \ + vim-tiny \ + sudo \ + locales \ + procps \ + && ln -s /usr/bin/fdfind /usr/local/bin/fd \ + && rm -rf /var/lib/apt/lists/* + +# Set locale +RUN sed -i '/en_US.UTF-8/s/^# //g' /etc/locale.gen && locale-gen +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 + +# ── Node.js (required for opencode v1.x install + MCP servers) ────── +ARG NODE_VERSION=22 +RUN curl -fsSL https://deb.nodesource.com/setup_${NODE_VERSION}.x | bash - && \ + apt-get install -y --no-install-recommends nodejs && \ + rm -rf /var/lib/apt/lists/* + +# ── Install opencode via npm ───────────────────────────────────────── +# v1.x is distributed as an npm package with platform-specific binaries +RUN npm install -g opencode-ai@${OPENCODE_VERSION} && \ + opencode --version + +# ── Optional: Python ───────────────────────────────────────────────── +ARG INSTALL_PYTHON=false +RUN if [ "${INSTALL_PYTHON}" = "true" ]; then \ + apt-get update && apt-get install -y --no-install-recommends \ + python3 python3-pip python3-venv && \ + rm -rf /var/lib/apt/lists/*; \ + fi + +# ── Optional: Go ───────────────────────────────────────────────────── +ARG INSTALL_GO=false +ARG GO_VERSION=1.23.4 +RUN if [ "${INSTALL_GO}" = "true" ]; then \ + GOARCH=$(case "${TARGETARCH}" in amd64) echo "amd64" ;; arm64) echo "arm64" ;; *) echo "amd64" ;; esac) && \ + curl -fsSL "https://go.dev/dl/go${GO_VERSION}.linux-${GOARCH}.tar.gz" | tar -C /usr/local -xz && \ + ln -s /usr/local/go/bin/go /usr/local/bin/go && \ + ln -s /usr/local/go/bin/gofmt /usr/local/bin/gofmt; \ + fi + +# ── Non-root user ──────────────────────────────────────────────────── +ARG USER_NAME=developer +ARG USER_UID=1000 +ARG USER_GID=1000 + +RUN groupadd --gid ${USER_GID} ${USER_NAME} && \ + useradd --uid ${USER_UID} --gid ${USER_GID} -m -s /bin/bash ${USER_NAME} && \ + echo "${USER_NAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers.d/${USER_NAME} + +# Create standard directories +RUN mkdir -p /workspace \ + /home/${USER_NAME}/.config/opencode \ + /home/${USER_NAME}/.local/share/opencode \ + /home/${USER_NAME}/.ssh && \ + chown -R ${USER_NAME}:${USER_NAME} /workspace /home/${USER_NAME} + +# ── Entrypoint ──────────────────────────────────────────────────────── +COPY entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh + +USER ${USER_NAME} +WORKDIR /workspace + +ENTRYPOINT ["entrypoint.sh"] +CMD ["opencode"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d2caa0 --- /dev/null +++ b/README.md @@ -0,0 +1,91 @@ +# opencode-devbox + +Portable AI developer environment in a Docker container. Run [opencode](https://opencode.ai) on any Docker-capable machine with configurable LLM providers, dev tools, and host filesystem access. + +## Why? + +The official `ghcr.io/anomalyco/opencode` image (now archived) was Alpine-based and minimal — no git, no dev tools, broken PTY support due to musl/glibc incompatibility. This project provides a **Debian-based, production-ready** alternative using the current v1.x release. + +## Quick Start + +```bash +# Clone +git clone ssh://gitea.jordbo.se:2222/joakimp/opencode-devbox.git +cd opencode-devbox + +# Configure +cp .env.example .env +# Edit .env with your provider, API key, workspace path, git config + +# Build and run +docker compose run --rm devbox +``` + +## Features + +- **Debian bookworm** base — glibc, full PTY/terminal support +- **Configurable providers** — Anthropic, OpenAI, AWS Bedrock via env vars +- **Host filesystem access** — bind mount any directory as `/workspace` +- **SSH key forwarding** — git push/pull to private repos +- **MCP server support** — Node.js included for `npx`-based MCP servers +- **Non-root user** — runs as `developer` (UID 1000) with sudo +- **Optional runtimes** — Python, Go via build args (Node.js always included — required for opencode v1.x) +- **Multi-arch** — amd64 and arm64 + +## Configuration + +### Environment Variables + +| Variable | Description | Default | +|---|---|---| +| `OPENCODE_PROVIDER` | LLM provider (`anthropic`, `openai`, `amazon-bedrock`) | `anthropic` | +| `OPENCODE_MODEL` | Model override | Provider default | +| `ANTHROPIC_API_KEY` | Anthropic API key | — | +| `OPENAI_API_KEY` | OpenAI API key | — | +| `AWS_REGION` | AWS region for Bedrock | `us-east-1` | +| `GIT_USER_NAME` | Git commit author name | — | +| `GIT_USER_EMAIL` | Git commit author email | — | +| `WORKSPACE_PATH` | Host path to mount | `.` | +| `SSH_KEY_PATH` | Host SSH key directory | `~/.ssh` | + +### Custom opencode config + +Mount your own `opencode.json` for full control (MCP servers, custom models, etc.): + +```yaml +volumes: + - ./my-opencode.json:/home/developer/.config/opencode/opencode.json:ro +``` + +### Build Args + +Enable optional language runtimes: + +```bash +docker compose build --build-arg INSTALL_PYTHON=true --build-arg INSTALL_GO=true +``` + +| Arg | Default | Description | +|---|---|---| +| `INSTALL_PYTHON` | `false` | Python 3 + pip + venv | +| `INSTALL_GO` | `false` | Go toolchain | + +## Architecture + +``` +Host Machine +├── ~/projects/my-app ──bind mount──▶ /workspace (container) +├── ~/.ssh ──bind mount──▶ /home/developer/.ssh (ro) +└── .env ──env vars───▶ provider config + API keys + +Container (Debian bookworm) +├── opencode binary +├── git, ssh, ripgrep, fd, jq, curl +├── Node.js (for MCP servers) +├── entrypoint.sh (SSH perms, git config, provider setup) +└── /workspace ← your code lives here +``` + +## License + +MIT diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..88125af --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,43 @@ +# opencode-devbox docker-compose +# +# Usage: +# cp .env.example .env # configure your provider and keys +# docker compose up -d +# docker compose exec devbox opencode +# +# Or for interactive one-shot: +# docker compose run --rm devbox + +services: + devbox: + build: + context: . + args: + INSTALL_PYTHON: "false" + INSTALL_GO: "false" + image: opencode-devbox:latest + container_name: opencode-devbox + stdin_open: true + tty: true + env_file: + - .env + environment: + - TERM=xterm-256color + volumes: + # Host workspace — mount your project here + - ${WORKSPACE_PATH:-.}:/workspace + + # SSH keys (read-only) — for git push/pull + - ${SSH_KEY_PATH:-~/.ssh}:/home/developer/.ssh:ro + + # Optional: mount your own opencode config + # - ./config/opencode.json:/home/developer/.config/opencode/opencode.json:ro + + # Optional: persist opencode data (auth, memory, etc.) + - devbox-data:/home/developer/.local/share/opencode + + # Optional: AWS credentials for Bedrock + # - ~/.aws:/home/developer/.aws:ro + +volumes: + devbox-data: diff --git a/entrypoint.sh b/entrypoint.sh new file mode 100644 index 0000000..1e19a15 --- /dev/null +++ b/entrypoint.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +set -euo pipefail + +# ── SSH key permissions ────────────────────────────────────────────── +# If SSH keys are mounted, fix permissions (bind mounts may have wrong perms) +if [ -d "$HOME/.ssh" ] && [ "$(ls -A "$HOME/.ssh" 2>/dev/null)" ]; then + chmod 700 "$HOME/.ssh" + find "$HOME/.ssh" -type f -name "id_*" ! -name "*.pub" -exec chmod 600 {} \; 2>/dev/null || true + find "$HOME/.ssh" -type f -name "*.pub" -exec chmod 644 {} \; 2>/dev/null || true + [ -f "$HOME/.ssh/known_hosts" ] && chmod 644 "$HOME/.ssh/known_hosts" + [ -f "$HOME/.ssh/config" ] && chmod 600 "$HOME/.ssh/config" +fi + +# ── Git config defaults ────────────────────────────────────────────── +# Set git config from env vars if not already configured via mounted .gitconfig +if [ -n "${GIT_USER_NAME:-}" ] && ! git config --global user.name &>/dev/null; then + git config --global user.name "$GIT_USER_NAME" +fi +if [ -n "${GIT_USER_EMAIL:-}" ] && ! git config --global user.email &>/dev/null; then + git config --global user.email "$GIT_USER_EMAIL" +fi + +# ── Generate opencode config from env vars if no config mounted ────── +CONFIG_DIR="$HOME/.config/opencode" +CONFIG_FILE="$CONFIG_DIR/opencode.json" + +if [ ! -f "$CONFIG_FILE" ] && [ -n "${OPENCODE_PROVIDER:-}" ]; then + echo "Generating opencode config for provider: $OPENCODE_PROVIDER" + mkdir -p "$CONFIG_DIR" + + # Build provider-specific config + case "$OPENCODE_PROVIDER" in + anthropic) + cat > "$CONFIG_FILE" < "$CONFIG_FILE" < "$CONFIG_FILE" < "$CONFIG_FILE" <