feat(opencode): ship tooling image with git, node, and build deps
Extend the Alpine OpenCode base so agent sessions can clone, npm/pnpm install, and compile native modules; seed Forgejo HTTPS creds at start. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
74f1e9eb0b
commit
d90ff11f5b
4 changed files with 87 additions and 8 deletions
|
|
@ -214,7 +214,10 @@ services:
|
||||||
|
|
||||||
opencode:
|
opencode:
|
||||||
profiles: ["opencode"]
|
profiles: ["opencode"]
|
||||||
image: ghcr.io/anomalyco/opencode:latest
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/opencode/Dockerfile
|
||||||
|
image: submatter-opencode:tooling
|
||||||
container_name: submatter-opencode
|
container_name: submatter-opencode
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
working_dir: /workspace/repo
|
working_dir: /workspace/repo
|
||||||
|
|
@ -235,6 +238,10 @@ services:
|
||||||
- OPENCODE_SERVER_PASSWORD=${OPENCODE_SERVER_PASSWORD}
|
- OPENCODE_SERVER_PASSWORD=${OPENCODE_SERVER_PASSWORD}
|
||||||
- OPENCODE_SERVER_USERNAME=${OPENCODE_SERVER_USERNAME:-opencode}
|
- OPENCODE_SERVER_USERNAME=${OPENCODE_SERVER_USERNAME:-opencode}
|
||||||
- OMNIROUTE_API_KEY=${OMNIROUTE_API_KEY}
|
- OMNIROUTE_API_KEY=${OMNIROUTE_API_KEY}
|
||||||
|
- FORGEJO_TOKEN=${FORGEJO_TOKEN:-}
|
||||||
|
- GITHUB_TOKEN=${GITHUB_TOKEN:-}
|
||||||
|
- GIT_USER_NAME=${GIT_USER_NAME:-opencode}
|
||||||
|
- GIT_USER_EMAIL=${GIT_USER_EMAIL:-opencode@stillwell.cloud}
|
||||||
volumes:
|
volumes:
|
||||||
- echodo-workspace:/workspace
|
- echodo-workspace:/workspace
|
||||||
- opencode-data:/data
|
- opencode-data:/data
|
||||||
|
|
@ -255,7 +262,8 @@ services:
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
memory: 2G
|
# Next.js builds need headroom beyond the bare serve binary.
|
||||||
|
memory: 3G
|
||||||
cpus: "2"
|
cpus: "2"
|
||||||
networks:
|
networks:
|
||||||
- echodo
|
- echodo
|
||||||
|
|
|
||||||
31
docker/opencode/Dockerfile
Normal file
31
docker/opencode/Dockerfile
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
# OpenCode serve image + host tooling for interactive / worker agent sessions.
|
||||||
|
# Official image is Alpine and ships only the `opencode` binary.
|
||||||
|
FROM ghcr.io/anomalyco/opencode:latest
|
||||||
|
|
||||||
|
USER root
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
bash \
|
||||||
|
ca-certificates \
|
||||||
|
curl \
|
||||||
|
git \
|
||||||
|
github-cli \
|
||||||
|
openssh-client \
|
||||||
|
ripgrep \
|
||||||
|
python3 \
|
||||||
|
make \
|
||||||
|
g++ \
|
||||||
|
libc6-compat \
|
||||||
|
nodejs \
|
||||||
|
npm \
|
||||||
|
&& curl -fsSL -o /usr/local/bin/pnpm \
|
||||||
|
https://github.com/pnpm/pnpm/releases/download/v9.15.9/pnpm-linuxstatic-x64 \
|
||||||
|
&& chmod +x /usr/local/bin/pnpm \
|
||||||
|
&& npm install -g tsx \
|
||||||
|
&& rm -rf /root/.npm/_cacache /tmp/*
|
||||||
|
|
||||||
|
ENV GIT_CONFIG_COUNT=1 \
|
||||||
|
GIT_CONFIG_KEY_0=safe.directory \
|
||||||
|
GIT_CONFIG_VALUE_0=*
|
||||||
|
|
||||||
|
WORKDIR /workspace
|
||||||
|
|
@ -7,17 +7,37 @@ Runs `opencode serve` on `:4096` (HTTP API for the worker). Attach a TUI with
|
||||||
is happy. The image has no desktop browser — do not use `opencode web` in
|
is happy. The image has no desktop browser — do not use `opencode web` in
|
||||||
compose without an `xdg-open` stub (entrypoint provides one if you switch back).
|
compose without an `xdg-open` stub (entrypoint provides one if you switch back).
|
||||||
|
|
||||||
|
## Tooling image
|
||||||
|
|
||||||
|
`docker/opencode/Dockerfile` extends `ghcr.io/anomalyco/opencode:latest` with:
|
||||||
|
|
||||||
|
- `git`, `openssh-client`, `gh`
|
||||||
|
- Node.js + `npm` + `pnpm` + `tsx`
|
||||||
|
- `bash`, `curl`, `ripgrep`, `python3`, `make`, `g++` (native module builds)
|
||||||
|
|
||||||
|
Forgejo HTTPS auth is seeded from `FORGEJO_TOKEN` into `~/.git-credentials` at
|
||||||
|
container start. Projects live under `/workspace/<repo>` on the shared volume.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export COMPOSE_PROFILES=opencode
|
||||||
|
docker compose --project-directory /opt/submatter \
|
||||||
|
-f docker/docker-compose.prod.yml --env-file .env \
|
||||||
|
build opencode
|
||||||
|
bash scripts/deploy-prod.sh up
|
||||||
|
```
|
||||||
|
|
||||||
## Enable
|
## Enable
|
||||||
|
|
||||||
On CT 116 `/opt/submatter/.env`:
|
On CT 116 `/opt/submatter/.env`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
COMPOSE_PROFILES=opencode
|
COMPOSE_PROFILES=opencode
|
||||||
WORKER_SESSION_TYPE=opencode # after worker image rebuild with opencode session
|
WORKER_SESSION_TYPE=opencode
|
||||||
OPENCODE_SERVER_PASSWORD=<openssl rand -hex 24>
|
OPENCODE_SERVER_PASSWORD=<openssl rand -hex 24>
|
||||||
OPENCODE_SERVER_URL=http://opencode:4096
|
OPENCODE_SERVER_URL=http://opencode:4096
|
||||||
OMNIROUTE_BASE_URL=https://omniroute.stillwell.cloud/v1
|
OMNIROUTE_BASE_URL=https://omniroute.stillwell.cloud/v1
|
||||||
OMNIROUTE_API_KEY=<gateway inference key>
|
OMNIROUTE_API_KEY=<gateway inference key>
|
||||||
|
FORGEJO_TOKEN=<forgejo pat with repo scope>
|
||||||
```
|
```
|
||||||
|
|
||||||
Then:
|
Then:
|
||||||
|
|
@ -29,9 +49,6 @@ bash scripts/deploy-prod.sh up
|
||||||
# bash scripts/deploy-prod.sh build
|
# bash scripts/deploy-prod.sh build
|
||||||
```
|
```
|
||||||
|
|
||||||
Keep `WORKER_SESSION_TYPE=minimal` until the rebuilt worker image includes the
|
|
||||||
`opencode` session type (and the `yaml` dependency fix).
|
|
||||||
|
|
||||||
## URLs
|
## URLs
|
||||||
|
|
||||||
| Surface | URL |
|
| Surface | URL |
|
||||||
|
|
@ -45,7 +62,8 @@ Keep `WORKER_SESSION_TYPE=minimal` until the rebuilt worker image includes the
|
||||||
- Browser: Authentik via Traefik (`authentik-auth`) **and** OpenCode basic auth.
|
- Browser: Authentik via Traefik (`authentik-auth`) **and** OpenCode basic auth.
|
||||||
- Worker: same basic auth via `OPENCODE_SERVER_PASSWORD`.
|
- Worker: same basic auth via `OPENCODE_SERVER_PASSWORD`.
|
||||||
- Models: `@omniroute/opencode-plugin` + `OMNIROUTE_API_KEY` (seeded into `auth.json` by `entrypoint.sh`).
|
- Models: `@omniroute/opencode-plugin` + `OMNIROUTE_API_KEY` (seeded into `auth.json` by `entrypoint.sh`).
|
||||||
|
- Git: `FORGEJO_TOKEN` → HTTPS credential helper for `forgejo.stillwell.cloud`.
|
||||||
|
|
||||||
## RAM
|
## RAM
|
||||||
|
|
||||||
Container capped at 2 GB. Keep one concurrent agent until host RAM → 64 GB.
|
Container capped at 3 GB (Next.js builds). Keep one concurrent agent until host RAM → 64 GB.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ set -eu
|
||||||
|
|
||||||
CONFIG_HOME="${XDG_CONFIG_HOME:-${HOME:-/data}/.config}"
|
CONFIG_HOME="${XDG_CONFIG_HOME:-${HOME:-/data}/.config}"
|
||||||
DATA_HOME="${XDG_DATA_HOME:-${HOME:-/data}/.local/share}"
|
DATA_HOME="${XDG_DATA_HOME:-${HOME:-/data}/.local/share}"
|
||||||
mkdir -p "$CONFIG_HOME/opencode" "$DATA_HOME/opencode" /data/bin
|
mkdir -p "$CONFIG_HOME/opencode" "$DATA_HOME/opencode" /data/bin "$HOME"
|
||||||
|
|
||||||
# Container has no desktop — stub xdg-open so `opencode web` does not exit.
|
# Container has no desktop — stub xdg-open so `opencode web` does not exit.
|
||||||
if [ ! -x /data/bin/xdg-open ]; then
|
if [ ! -x /data/bin/xdg-open ]; then
|
||||||
|
|
@ -24,4 +24,26 @@ if [ -n "${OMNIROUTE_API_KEY:-}" ]; then
|
||||||
chmod 600 "$DATA_HOME/opencode/auth.json" || true
|
chmod 600 "$DATA_HOME/opencode/auth.json" || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Git identity + Forgejo HTTPS auth for clone/push from agent sessions.
|
||||||
|
if command -v git >/dev/null 2>&1; then
|
||||||
|
git config --global --add safe.directory '*'
|
||||||
|
if [ -n "${GIT_AUTHOR_NAME:-}" ]; then
|
||||||
|
git config --global user.name "$GIT_AUTHOR_NAME"
|
||||||
|
else
|
||||||
|
git config --global user.name "${GIT_USER_NAME:-opencode}"
|
||||||
|
fi
|
||||||
|
if [ -n "${GIT_AUTHOR_EMAIL:-}" ]; then
|
||||||
|
git config --global user.email "$GIT_AUTHOR_EMAIL"
|
||||||
|
else
|
||||||
|
git config --global user.email "${GIT_USER_EMAIL:-opencode@stillwell.cloud}"
|
||||||
|
fi
|
||||||
|
if [ -n "${FORGEJO_TOKEN:-}" ]; then
|
||||||
|
git config --global credential.helper store
|
||||||
|
# oauth2:<token> is Forgejo's HTTPS token username convention.
|
||||||
|
printf 'https://oauth2:%s@forgejo.stillwell.cloud\n' "$FORGEJO_TOKEN" \
|
||||||
|
> "$HOME/.git-credentials"
|
||||||
|
chmod 600 "$HOME/.git-credentials"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
exec "$@"
|
exec "$@"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue