feat: docker-compose + Dockerfile + .env.example (#24)

* feat(docker): add docker-compose + Dockerfile + .env.example

* docs(handoff): add pr-8 handoff + ADR-004

* docs(handoff): fix PR number

* docs: update project map + handoff + ADR

---------

Co-authored-by: opencode-agent <agent@slaid098.dev>
This commit is contained in:
Sergey 2026-07-23 23:35:18 +03:00 committed by GitHub
parent 6005c88203
commit b9daf31233
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 191 additions and 1 deletions

28
.env.example Normal file
View file

@ -0,0 +1,28 @@
# AI Provider
AI_PROVIDER_BASE_URL=https://your-ai-provider.example.com/v1/
AI_PROVIDER_API_KEY=your-api-key-here
# OpenCode Server
OPENCODE_SERVER_PASSWORD=your-opencode-server-password
# GitHub
GITHUB_TOKEN=your-github-token-here
# Context7 MCP
CONTEX7_API_KEY=your-context7-api-key-here
# Telegram (optional)
TELEGRAM_API_ID=your-telegram-api-id
TELEGRAM_API_HASH=your-telegram-api-hash
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
# Redis (optional)
REDIS_PASSWORD=your-redis-password
# Cloudflare Tunnel (optional — see separate tunnel repo)
CLOUDFLARE_TUNNEL_TOKEN=
# Antidetect Browser MCP
# Local: http://localhost:8765/mcp
# Remote: http://<your-server-ip>:8765/mcp
ANTIDETECT_BROWSER_MCP_URL=http://localhost:8765/mcp

38
Dockerfile Normal file
View file

@ -0,0 +1,38 @@
FROM node:20-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
ca-certificates \
curl \
git \
openssh-client \
autossh \
python3 \
make \
g++ \
chromium \
gnupg \
docker.io \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*
RUN curl -LsSf https://astral.sh/uv/install.sh | UV_INSTALL_DIR=/usr/local/bin sh
RUN mkdir -p -m 0755 /etc/apt/keyrings \
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | gpg --dearmor -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
&& mkdir -p /etc/apt/sources.list.d \
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
&& apt-get update \
&& apt-get install -y gh \
&& rm -rf /var/lib/apt/lists/*
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
RUN npm install -g opencode-ai repomix --unsafe-perm
WORKDIR /root/workspace
EXPOSE 4096
ENTRYPOINT ["opencode"]

62
docker-compose.yml Normal file
View file

@ -0,0 +1,62 @@
services:
dind:
image: docker:dind
container_name: opencode-docker-dind
privileged: true
environment:
- DOCKER_TLS_CERTDIR=
networks:
- opencode_network
restart: unless-stopped
deploy:
resources:
limits:
cpus: '2'
memory: 4G
pids: 512
reservations:
memory: 1G
opencode:
build:
context: .
dockerfile: Dockerfile
container_name: opencode
restart: unless-stopped
command: ["serve", "--hostname", "0.0.0.0", "--port", "4096", "--cors", "*"]
working_dir: /root/workspace
env_file:
- .env
environment:
- DOCKER_HOST=tcp://docker-dind:2375
- OPENCODE_CONFIG_DIR=/root/.config/opencode
- OPENCODE_MEMORY_DIR=/root/workspace/app_data/opencode-memory
- DOCKER_CONTAINER=true
- GIT_AUTHOR_NAME=opencode-agent
- GIT_AUTHOR_EMAIL=agent@opencode.local
- GIT_COMMITTER_NAME=opencode-agent
- GIT_COMMITTER_EMAIL=agent@opencode.local
volumes:
- ./.opencode:/root/.config/opencode
- ./app_data:/root/.local/share/opencode
- ./app_data/workspaces:/root/workspace
- ./app_data/ssh:/root/.ssh:ro
ports:
- "127.0.0.1:4096:4096"
networks:
- opencode_network
depends_on:
dind:
condition: service_started
deploy:
resources:
limits:
cpus: '3'
memory: 6G
pids: 1024
reservations:
memory: 2G
networks:
opencode_network:
name: opencode_network

View file

@ -0,0 +1,30 @@
# ADR-004: Docker setup (public, cleaned)
## Статус
Accepted
## Контекст
Docker-based AI coding assistant с persistent memory. Старый репо `/root/workspace/opencode/` содержал `cloudflared` (Cloudflare Tunnel client), external network `digital_factory_app_network` (shared с monorepo digital_factory), приватные IPs (`80.85.139.21`), приватный домен `ai.slaid098.dev` в `AI_PROVIDER_BASE_URL`, tunnel UUID `f97e66a5-...` в комментариях `.env.example`. Публичный релиз `slaid098/opencode-config` требует очистки от приватной инфраструктуры.
Архитектура нового репо: 4 bind mount'а (`.opencode`/`app_data`/`app_data/workspaces`/`app_data/ssh:ro`) вместо старого `config/`+`sandbox_workspace`.
## Решение
- 2 services в `docker-compose.yml`: `dind` (docker:dind, privileged, 2 CPU / 4G) + `opencode` (build from Dockerfile, 3 CPU / 6G)
- Network `opencode_network` (self-contained, НЕ external)
- 4 bind mounts: `./.opencode``/root/.config/opencode`, `./app_data``/root/.local/share/opencode`, `./app_data/workspaces``/root/workspace`, `./app_data/ssh``/root/.ssh:ro`
- `docker-compose.yml`: `env_file: .env` (gitignored, user copies from `.env.example`)
- `Dockerfile`: node:20-slim + apt (ca-certificates, curl, git, openssh-client, autossh, python3, make, g++, chromium, gnupg, docker.io, ffmpeg) + uv + gh (via apt keyring) + `npm install -g opencode-ai repomix --unsafe-perm` + Puppeteer ENV (system chromium)
- `.env.example`: placeholder only (`your-api-key-here`, `https://your-ai-provider.example.com/v1/`)
- Убрано: `cloudflared` install (Dockerfile строки 33-34 оригинала), `digital_factory_app_network` external network, IP `80.85.139.21`, домен `ai.slaid098.dev`, tunnel UUID `f97e66a5-...`
- `GIT_AUTHOR/COMMITTER_EMAIL` изменён с `agent@slaid098.dev` на `agent@opencode.local`
## Альтернативы
- **Сохранить cloudflared** — отклонено. Публичный репо, tunnel = приватная инфраструктура (named tunnel `slaid098-dev` привязан к конкретному Cloudflare account). Tunnel вынесен в отдельный приватный репо.
- **Docker compose без dind** — отклонено. opencode-ai использует Docker для sandboxed execution (субагенты запускают `docker run`), без dind теряется ключевая возможность.
- **Сохранить external network `digital_factory_app_network`** — отклонено. External network shared с monorepo `digital_factory` на конкретной машине, не self-contained. Заменён на локальную `opencode_network`.
- **Hardcode приватных IPs/domains в `.env.example`** — отклонено. Публичный репо, секреты в `.env` (gitignored), `.env.example` — документация с placeholder'ами.
## Последствия
- `docker compose config` без `.env` падает с `env file .env not found` — expected, пользователь обязан `cp .env.example .env` перед `docker compose up`.
- `OPENCODE_MEMORY_DIR=/root/workspace/app_data/opencode-memory` — путь внутри контейнера маппится через bind mount в `./app_data/workspaces/app_data/opencode-memory` на хосте. Layout может потребовать корректировки после runtime-тестирования.
- AGENTS.md bind-mount volume не включён в этот PR — добавляется в #10.

View file

@ -0,0 +1,29 @@
# PR: Docker setup
## Что сделано
- docker-compose.yml: 2 services (dind + opencode), 4 bind mounts, opencode_network
- Dockerfile: node:20-slim, uv, gh, chromium, docker.io, opencode-ai, repomix
- .env.example: cleaned of private IPs/domains/tunnel UUID, placeholder only
- Убрано: cloudflared (tunnel → отдельный репо), digital_factory_app_network (self-contained network)
## Почему
Docker-based AI coding assistant. Публичный релиз — очищено от приватной инфраструктуры.
Source: `/root/workspace/opencode/` (ветка fix/pipeline-status/review-next-by-verdict) — docker-compose.yml, Dockerfile, .env.example.
## Pending
- AGENTS.md bind-mount volume — добавляется в #10
## Watch out
- `.env` gitignored — пользователь создаёт из `.env.example` (`cp .env.example .env`). `docker compose config` без `.env` падает с `env file .env not found` — это expected, не баг.
- .env.example НЕ содержит реальных ключей — placeholder only.
- CLOUDFLARE_TUNNEL_TOKEN= (optional) — оставлен как placeholder, tunnel перенесён в отдельный репо.
- ANTIDETECT_BROWSER_MCP_URL=http://localhost:8765/mcp — дефолт local, для remote переопределить в `.env` на `http://<server-ip>:8765/mcp`.
- GIT_AUTHOR/COMMITTER_EMAIL изменён с `agent@slaid098.dev` на `agent@opencode.local` — публичный репо без приватного домена.
- OPENCODE_MEMORY_DIR=/root/workspace/app_data/opencode-memory — путь внутри контейнера; bind mount `./app_data/workspaces:/root/workspace` маппит host→container, так что memory лежит в `./app_data/workspaces/app_data/opencode-memory` на хосте. Возможен нюанс с layout — проверить в runtime.
## Файлы
- `docker-compose.yml` — 2 services (dind, opencode), network `opencode_network`, 4 bind mounts
- `Dockerfile` — node:20-slim + apt (ca-certs, curl, git, ssh, autossh, python3, make, g++, chromium, gnupg, docker.io, ffmpeg) + uv + gh + opencode-ai + repomix
- `.env.example` — AI provider, OpenCode server, GitHub, Context7, Telegram, Redis, Cloudflare (optional), Antidetect Browser MCP
- `docs/handoff/pr-24-docker-setup.md` — этот файл
- `docs/decisions/004-pr-24-docker-setup.md` — ADR-004

View file

@ -1,6 +1,6 @@
# Project Map # Project Map
opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). opencode-config — Docker-based AI coding assistant with persistent memory (opencode configuration). Runs in Docker via `docker-compose.yml` (dind + opencode services).
## Structure ## Structure
@ -81,6 +81,9 @@ opencode-config/
├── pyproject.toml # Python project (uv, ruff, pytest config) ├── pyproject.toml # Python project (uv, ruff, pytest config)
├── uv.lock # Locked deps for Python project ├── uv.lock # Locked deps for Python project
├── .pre-commit-config.yaml # ruff + UV hooks ├── .pre-commit-config.yaml # ruff + UV hooks
├── docker-compose.yml # 2 services (dind + opencode), opencode_network, 4 bind mounts — PR#24
├── Dockerfile # node:20-slim + uv + gh + chromium + docker.io + opencode-ai + repomix — PR#24
├── .env.example # Placeholder-only env template (user copies to .env) — PR#24
├── app_data/ ├── app_data/
│ ├── workspaces/ # Agent working directory (.gitkeep) │ ├── workspaces/ # Agent working directory (.gitkeep)
│ └── ssh/ # SSH keys, not in git (.gitkeep) │ └── ssh/ # SSH keys, not in git (.gitkeep)