72298ae77e
Validate / base-change-warning (push) Successful in 14s
Validate / docs-check (push) Successful in 13s
Publish Docker Image / resolve-versions (push) Successful in 8s
Publish Docker Image / base-decide (push) Successful in 13s
Validate / validate-omos (push) Successful in 12m42s
Validate / validate-base (push) Successful in 13m39s
Publish Docker Image / build-base (push) Successful in 44m17s
Publish Docker Image / smoke-base (push) Successful in 3m46s
Publish Docker Image / smoke-omos (push) Successful in 5m54s
Publish Docker Image / build-variant-base (push) Successful in 18m11s
Publish Docker Image / build-variant-omos (push) Successful in 19m34s
Publish Docker Image / promote-base-latest (push) Successful in 9s
Publish Docker Image / update-description (push) Successful in 15s
PR-5 (per docs/CLEANUP-v2.0.0.md). Major release with two breaking changes:
1. pi fully removed (deprecated in v1.17.2). Gone: INSTALL_PI + all PI_*
build args; with-pi/omos-with-pi/pi-only variants; base-pi-only publish
job; all ~/.pi entrypoint wiring; the 3 pi smoke/validate/build-variant
CI jobs. Only base + omos variants remain (4 tags/release).
2. NPM_CONFIG_PREFIX relocated ~/.pi/npm-global -> ~/.config/opencode/npm-global
(persistent in both compose files). entrypoint-user.sh gains a one-time
migration shim that copies old global npm packages forward.
Also: opencode 1.17.2->1.17.4; DOCKER_HUB.md gains {{OPENCODE_VERSION}}
placeholder filled by CI at publish time (mirrors pi-devbox); full docs
drift sweep across README/AGENTS/.gitea-README/.env.example/manual-host-publish;
DOCKER_HUB.md regenerated + --check passes; both workflows YAML-valid;
all shell scripts pass bash -n.
220 lines
9.5 KiB
Python
Executable File
220 lines
9.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Generate DOCKER_HUB.md.
|
|
|
|
Rationale
|
|
---------
|
|
DOCKER_HUB.md is the public-facing description shown on Docker Hub. It
|
|
has two hard constraints the README does not:
|
|
|
|
1. A 25 kB byte limit on the full_description field.
|
|
2. A different audience: Hub readers want a 30-second evaluation —
|
|
"what is this, how do I run it, does it have what I need" — and
|
|
reference material is better consulted in context on gitea.
|
|
|
|
For a long time this script tried to derive DOCKER_HUB.md from README.md
|
|
by section selection + targeted replacement. As the README grew that
|
|
approach pushed against the 25 kB ceiling on every change, costing a
|
|
trim-something-else exercise per edit (final state: 3 byte headroom).
|
|
|
|
The new approach is much simpler: a hand-written HUB_TEMPLATE below.
|
|
The template intentionally stays slim and links out to the gitea README
|
|
for everything that benefits from depth. README.md grows freely.
|
|
|
|
Trade-off: when image-variants table or quick-start flow changes,
|
|
update HUB_TEMPLATE here too. That coupling is now explicit and
|
|
local rather than spread across SECTION_RULES + REPLACEMENTS + TRIM
|
|
machinery.
|
|
|
|
Usage
|
|
-----
|
|
Regenerate in place:
|
|
python3 scripts/generate-dockerhub-md.py
|
|
|
|
Fail if DOCKER_HUB.md is out of sync with what this script would emit
|
|
(run this in CI):
|
|
python3 scripts/generate-dockerhub-md.py --check
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parent.parent
|
|
DOCKER_HUB = REPO_ROOT / "DOCKER_HUB.md"
|
|
|
|
# Max size for Docker Hub full_description (bytes, UTF-8).
|
|
MAX_SIZE_BYTES = 25_000
|
|
|
|
# Where readers go for the full reference.
|
|
GITEA = "https://gitea.jordbo.se/joakimp/opencode-devbox"
|
|
|
|
|
|
HUB_TEMPLATE = f"""# opencode-devbox
|
|
|
|
Portable AI developer environment for [opencode](https://opencode.ai). Debian-based, with git, SSH, Node.js, AWS CLI v2, and common dev tools pre-installed.
|
|
|
|
> **Current `:latest` ships opencode `{{{{OPENCODE_VERSION}}}}`** (the baked version is asserted by smoke tests, so this page never drifts from the image).
|
|
|
|
Designed for teams who want a reproducible coding-agent setup that runs the same on every laptop and CI runner — without forcing each developer to install Bun, Node, AWS CLI, mempalace, or maintain shell config drift across machines.
|
|
|
|
## Image Variants
|
|
|
|
| Tag | Description |
|
|
|---|---|
|
|
| `latest` / `vX.Y.Z` | Base image — opencode `{{{{OPENCODE_VERSION}}}}`, Node.js, AWS CLI, dev tools |
|
|
| `latest-omos` / `vX.Y.Z-omos` | Base + [oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim) multi-agent orchestration and Bun |
|
|
|
|
All variants support `linux/amd64` and `linux/arm64`.
|
|
|
|
> **Looking for pi?** As of v2.0.0 the pi coding-agent is no longer bundled in
|
|
> opencode-devbox. It ships as the dedicated
|
|
> [`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox) image, which
|
|
> shares the same mempalace memory layer. See
|
|
> <https://gitea.jordbo.se/joakimp/pi-devbox>.
|
|
|
|
## Quick Start
|
|
|
|
For a fully-configured environment with persistent state (opencode config, mempalace memory, neovim plugins, bash history) surviving container recreation, use docker-compose. **You don't need to clone the repo** — just grab two template files:
|
|
|
|
```bash
|
|
mkdir -p ~/opencode-devbox && cd ~/opencode-devbox
|
|
curl -O https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/docker-compose.yml
|
|
curl -fsSL https://gitea.jordbo.se/joakimp/opencode-devbox/raw/branch/main/.env.example -o .env
|
|
# Edit .env — set OPENCODE_PROVIDER, the matching API key,
|
|
# WORKSPACE_PATH, GIT_USER_NAME, GIT_USER_EMAIL.
|
|
docker compose run --rm devbox
|
|
```
|
|
|
|
This drops you straight into opencode with your project mounted at `/workspace`. Use `bash` as the command (e.g. `docker compose run --rm devbox bash`) to land in a shell first — useful for `aws sso login` or multi-agent workflows.
|
|
|
|
**One-shot run, no persistence:**
|
|
|
|
```bash
|
|
docker run -it --rm \\
|
|
-e ANTHROPIC_API_KEY=your-key \\
|
|
-e OPENCODE_PROVIDER=anthropic \\
|
|
-e GIT_USER_NAME="Your Name" \\
|
|
-e GIT_USER_EMAIL="you@example.com" \\
|
|
-v ~/projects:/workspace \\
|
|
-v ~/.ssh:/home/developer/.ssh:ro \\
|
|
joakimp/opencode-devbox:latest
|
|
```
|
|
|
|
Full setup guide — authentication for each provider (Anthropic, OpenAI, Bedrock SSO + static), persistence model, build args, troubleshooting: <{GITEA}#readme>
|
|
|
|
## What's Inside
|
|
|
|
- **[opencode](https://opencode.ai)** — primary coding-agent harness. Multi-provider (Anthropic, OpenAI, Bedrock, Google, Groq, etc.).
|
|
- **[mempalace](https://github.com/MemPalace/mempalace)** — persistent AI memory layer (ChromaDB + SQLite). Wing/diary/knowledge-graph entries are shareable with the sibling [`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox) image when both point at the same palace.
|
|
- **[oh-my-opencode-slim](https://github.com/alvinunreal/oh-my-opencode-slim)** *(in `*-omos` variants)* — multi-agent orchestration on top of opencode (council, fallback chains, named agents).
|
|
- **AWS CLI v2** with SSO support, **Node.js LTS**, **Bun** (OMOS variants), **uv** (Python), **gosu** for clean UID/GID adjustment to match your host workspace.
|
|
- **MCP wrappers** for mempalace pre-installed and pre-wired to opencode.
|
|
|
|
## Authentication
|
|
|
|
The container reads provider credentials from environment variables and host-mounted config:
|
|
|
|
- **Anthropic / OpenAI / Groq / others:** set `OPENCODE_PROVIDER` and the corresponding `*_API_KEY` via `-e` or `.env`.
|
|
- **AWS Bedrock (SSO):** mount `~/.aws` from the host, `OPENCODE_PROVIDER=amazon-bedrock`, then `aws sso login` inside the container. Tokens persist across container restarts via the host bind-mount.
|
|
- **OAuth / device-code providers:** auth state lives in opencode's config, which is persisted via the `devbox-opencode-config` named volume.
|
|
|
|
Full Bedrock walkthrough (IAM roles, permissions, multi-account setups): see the [AWS Bedrock Authentication](
|
|
{GITEA}#aws-bedrock-authentication
|
|
) section on gitea.
|
|
|
|
## Persistence
|
|
|
|
| Volume | Mount | Survives |
|
|
|---|---|---|
|
|
| `devbox-opencode-config` | `~/.config/opencode` | container recreate, image rebuild — incl. user-installed npm globals via `npm install -g` (`NPM_CONFIG_PREFIX` points into the volume) |
|
|
| `devbox-palace` (uncomment) | `~/.mempalace` | container recreate, image rebuild — palace data is precious, treat as primary storage |
|
|
| `devbox-chroma-cache` | `~/.cache/chroma` | container recreate (model cache, disposable — re-downloads in seconds) |
|
|
|
|
Workspace bind-mount (`/workspace`) is your project directory on the host, so source code is never inside the container.
|
|
|
|
Full persistence reference, including multi-user (`SIGNUM`) isolation and host bind-mount alternatives: see the [README on gitea]({GITEA}#persistence).
|
|
|
|
## Where to Go Next
|
|
|
|
- **Full README** with build args, every feature in detail, troubleshooting: <{GITEA}>
|
|
- **CHANGELOG** for version history: <{GITEA}/src/branch/main/CHANGELOG.md>
|
|
- **Issues / source / docker-compose templates:** <{GITEA}>
|
|
- **Agent-facing internals** (for future maintainers / coding agents working in the repo): <{GITEA}/src/branch/main/AGENTS.md>
|
|
|
|
## Sibling images
|
|
|
|
- **[`joakimp/pi-devbox`](https://hub.docker.com/r/joakimp/pi-devbox)** — the pi coding-agent in its own self-contained image, built on a shared Debian base. Version-tracks the [pi npm package](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) directly and can share this image's mempalace palace. Use it if you want pi instead of (or alongside) opencode. Source: <https://gitea.jordbo.se/joakimp/pi-devbox>
|
|
|
|
## License
|
|
|
|
MIT. See <{GITEA}/src/branch/main/LICENSE>.
|
|
|
|
---
|
|
|
|
> This description is generated by `scripts/generate-dockerhub-md.py` from a hand-maintained template. Edit the template (not this file) and regenerate. The `{{{{OPENCODE_VERSION}}}}` placeholder is filled by CI at publish time.
|
|
"""
|
|
|
|
|
|
def generate() -> str:
|
|
return HUB_TEMPLATE
|
|
|
|
|
|
def main() -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument(
|
|
"--check",
|
|
action="store_true",
|
|
help="Fail if DOCKER_HUB.md differs from generated content.",
|
|
)
|
|
args = parser.parse_args()
|
|
|
|
content = generate()
|
|
size = len(content.encode("utf-8"))
|
|
if size > MAX_SIZE_BYTES:
|
|
print(
|
|
f"ERROR: generated DOCKER_HUB.md is {size} bytes, exceeding the "
|
|
f"Docker Hub limit of {MAX_SIZE_BYTES} bytes.",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
if args.check:
|
|
existing = DOCKER_HUB.read_text(encoding="utf-8") if DOCKER_HUB.exists() else ""
|
|
if existing != content:
|
|
print(
|
|
"ERROR: DOCKER_HUB.md is out of sync with the template.\n"
|
|
"Run: python3 scripts/generate-dockerhub-md.py",
|
|
file=sys.stderr,
|
|
)
|
|
import difflib
|
|
|
|
diff = difflib.unified_diff(
|
|
existing.splitlines(keepends=True),
|
|
content.splitlines(keepends=True),
|
|
fromfile="DOCKER_HUB.md (committed)",
|
|
tofile="DOCKER_HUB.md (generated)",
|
|
n=2,
|
|
)
|
|
sys.stderr.writelines(list(diff)[:80])
|
|
return 1
|
|
print(
|
|
f"OK: DOCKER_HUB.md is in sync with HUB_TEMPLATE "
|
|
f"({size} bytes, {MAX_SIZE_BYTES} limit, "
|
|
f"{MAX_SIZE_BYTES - size} bytes headroom).",
|
|
)
|
|
return 0
|
|
|
|
DOCKER_HUB.write_text(content, encoding="utf-8")
|
|
print(
|
|
f"Wrote {DOCKER_HUB} ({size} bytes, {MAX_SIZE_BYTES} limit, "
|
|
f"{MAX_SIZE_BYTES - size} bytes headroom).",
|
|
)
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|