Previous phrasing treated the letter suffix as a plain alphabetical sequence, which led to confusion about whether the first rebuild should be 'a' or 'b'. Spell out the intent: the suffix is the build ordinal, and the letter 'a' is reserved to mean '1st build' — which always uses the bare tag (no letter). So letters start at 'b' for the 2nd build, 'c' for the 3rd, and so on. Examples for opencode version 1.14.20: 1st build: v1.14.20 2nd build: v1.14.20b 3rd build: v1.14.20c
4.5 KiB
AGENTS.md
Project overview
Docker image packaging opencode into a production-ready dev container. Two image variants (base and omos) are published to Docker Hub via Gitea Actions CI. Not a library or application — this is infrastructure (Dockerfile, entrypoint scripts, docker-compose, documentation).
File roles
Dockerfile— single multi-stage build for both variants. OMOS variant is controlled byINSTALL_OMOS=truebuild arg. All GitHub-sourced binaries are pinned with version ARGs.entrypoint.sh— runs as root: UID/GID adjustment, SSH permissions, volume ownership fixes. Then drops to developer via gosu.entrypoint-user.sh— runs as developer: git config, opencode.json generation from env vars, OMOS setup.DOCKER_HUB.md— pushed to Docker Hub description via CI API call. Must stay under 25KB. Short description field must be ≤100 bytes.README.md— source repo documentation. Must stay in sync with DOCKER_HUB.md (both describe the same features but for different audiences)..gitea/workflows/docker-publish.yml— CI pipeline: three parallel jobs (build-base, build-omos, update-description). Triggered by tag push only.
Versioning scheme
Tags follow v{opencode_version}[letter] — e.g. v1.14.20 for the first build on a new opencode release, and v1.14.20b, v1.14.20c, … for subsequent rebuilds on the same opencode version.
- The number tracks the opencode npm version (see
OPENCODE_VERSIONARG inDockerfile). - No letter suffix on the first build of a new opencode version — the bare
v{opencode_version}tag is the canonical release. - Letter suffix is the build ordinal, starting at
bfor the second build. The letterais never used — think of the suffix as counting rebuilds:b = 2nd, c = 3rd, d = 4th, …. For opencode version1.14.20: first buildv1.14.20, secondv1.14.20b, thirdv1.14.20c, and so on. - A letter suffix is only used for container-level rebuilds — tooling changes, CVE fixes, doc-driven rebuilds, entrypoint bugfixes — that don't change the underlying opencode version.
CI produces four Docker Hub tags per release: vX.Y.Z[n], latest, vX.Y.Z[n]-omos, latest-omos.
When bumping the opencode version, also bump OPENCODE_VERSION in Dockerfile and update the comment in .env.example if it names a specific model/version for context.
Critical conventions
- entrypoint.sh volume ownership loop — when adding a new named volume mount point, add it to the
for dir in ...loop inentrypoint.shso root-owned volumes get chowned on startup. - Three docs to keep in sync — Dockerfile changes that add tools or features must be reflected in
README.md,DOCKER_HUB.md, and.env.example. The docker-compose examples in both docs must match the sourcedocker-compose.ymlpattern. - GitHub-sourced binaries — fzf, gosu, git-lfs, neovim, bat, eza, zoxide, uv, rustup are installed from upstream releases (not apt) with pinned versions. Use the same
ARCHcase-switch pattern for multi-arch support (amd64/arm64). - Shell scripts use
set -euo pipefail— both entrypoints are strict. Errors in volume chown or SSH permission operations are intentionally suppressed with|| true. - Docker Hub description update — uses
/v2/auth/tokenendpoint (not the deprecated/v2/users/login). Auth usesidentifier/secretfields, returnsaccess_token, sent asBearer. Short description must be ≤100 bytes.
CI quirks
- Both build jobs include an IPv4 preference step (
gai.conf+driver-opts: network=hostfor buildx) to work around intermittent IPv6 failures on the Gitea runners. update-descriptionjob runs only when both builds succeed (needs: [build-base, build-omos]).- Tags must be pushed to trigger CI. Pushing to
mainalone does not build images.
Testing changes
No test suite. Verify by:
- Building locally:
docker compose build - Running:
docker compose run --rm devbox bash - Checking tool availability inside container:
nvim --version,bat --version,uv --version, etc. - For entrypoint changes: test with a non-1000 UID workspace to verify UID adjustment and volume ownership fixes.
Commit style
Imperative mood, first line summarizes the change. Multi-line body explains "why" when non-obvious. Examples from history:
Fix ownership of named volume mount points in entrypointAdd uv package manager to base image for on-demand Python supportUpgrade base image from Debian bookworm to trixie (current stable)