# opencode-devbox — variant image # # FROMs a base- image produced by Dockerfile.base and adds only # the variant-specific tools (opencode, pi, oh-my-opencode-slim, Go). # # The two published variants are produced from THIS Dockerfile by # varying build args: # # variant INSTALL_OPENCODE INSTALL_OMOS # ──────── ──────────────── ──────────── # base true false # omos true true # # pi was removed in v2.0.0 (it had been deprecated since v1.17.2). It now # ships from its own self-contained image: joakimp/pi-devbox:latest # (https://gitea.jordbo.se/joakimp/pi-devbox). See docs/CLEANUP-v2.0.0.md # for the removal history. # # Pass `--build-arg BASE_IMAGE=:base-` to select the base. # The CI workflow computes the base hash from Dockerfile.base + rootfs/ # + entrypoint*.sh and feeds it in. # # IMPORTANT: the base image sets NPM_CONFIG_PREFIX to # /home/developer/.config/opencode/npm-global so runtime `npm install -g` # by the developer user lands on the named volume. At BUILD time we want # the baked binaries on /usr so they survive the volume mount. Each # `npm install -g` below therefore prefixes the command with # `NPM_CONFIG_PREFIX=/usr`. ARG BASE_IMAGE FROM ${BASE_IMAGE} ARG TARGETARCH ARG USER_NAME=developer # ── Install opencode via npm ───────────────────────────────────────── # OPENCODE_VERSION is intentionally pinned in this Dockerfile (not # 'latest'). It drives the release tag and gets bumped via a source # edit, so the cache-hit class of bug that bit pi-devbox v0.74.0.. # v0.75.5 cannot apply here. ARG INSTALL_OPENCODE=true ARG OPENCODE_VERSION=1.17.8 RUN if [ "${INSTALL_OPENCODE}" = "true" ]; then \ NPM_CONFIG_PREFIX=/usr npm install -g opencode-ai@${OPENCODE_VERSION} && \ opencode --version ; \ fi # ── Optional: Go ───────────────────────────────────────────────────── ARG INSTALL_GO=false ARG GO_VERSION=latest RUN if [ "${INSTALL_GO}" = "true" ]; then \ GOARCH=$(case "${TARGETARCH}" in amd64) echo "amd64" ;; arm64) echo "arm64" ;; *) echo "amd64" ;; esac) && \ V="${GO_VERSION}" && \ if [ "$V" = "latest" ]; then \ V=$(curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://go.dev/dl/?mode=json" | \ awk -F'"' '/"version":/ { sub(/^go/,"",$4); print $4; exit }'); \ fi && \ [ -n "$V" ] && \ echo "Installing Go ${V}" && \ curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://go.dev/dl/go${V}.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 # ── Optional: oh-my-opencode-slim (multi-agent orchestration) ──────── # Installs Bun runtime and the oh-my-opencode-slim npm package. # OMOS_VERSION has a cache-hit footgun when left at the `latest` default # in registry-cached CI builds: the resulting build-arg string is byte- # identical across builds, so the layer-hash is identical, so the # registry buildcache silently reuses the layer from whatever omos # version was current when the cache was first populated. CI resolves it # via `npm view oh-my-opencode-slim version` and passes the concrete # value as a build-arg (see resolve-versions in docker-publish-split.yml). ARG INSTALL_OMOS=false ARG OMOS_VERSION=latest RUN if [ "${INSTALL_OMOS}" = "true" ]; then \ ARCH=$(uname -m) && \ if [ "$ARCH" = "x86_64" ]; then \ BUN_ARCH="x64-baseline"; \ elif [ "$ARCH" = "aarch64" ]; then \ BUN_ARCH="aarch64"; \ fi && \ curl -fsSL --retry 5 --retry-delay 5 --retry-all-errors "https://github.com/oven-sh/bun/releases/latest/download/bun-linux-${BUN_ARCH}.zip" -o /tmp/bun.zip && \ unzip -o /tmp/bun.zip -d /tmp/bun && \ mv /tmp/bun/bun-linux-${BUN_ARCH}/bun /usr/local/bin/bun && \ chmod +x /usr/local/bin/bun && \ ln -sf bun /usr/local/bin/bunx && \ rm -rf /tmp/bun /tmp/bun.zip && \ bun --version && \ test -L /usr/local/bin/bunx && \ NPM_CONFIG_PREFIX=/usr npm install -g oh-my-opencode-slim@${OMOS_VERSION}; \ fi # ── Build provenance: OCI labels + on-disk manifest ────────────────── # These ARGs are declared LAST, immediately before the layer that uses # them, so a changing BUILD_DATE / RELEASE_TAG / SOURCE_REVISION never # invalidates the expensive npm-install layers above. OPENCODE_VERSION, # OMOS_VERSION and INSTALL_OMOS are already in scope from earlier in this # stage and need no re-declaration; MEMPALACE_TOOLKIT_REF is consumed in # Dockerfile.base, so it is re-declared here only to land in the labels. ARG RELEASE_TAG=dev ARG BUILD_DATE= ARG SOURCE_REVISION= ARG MEMPALACE_TOOLKIT_REF=main LABEL org.opencontainers.image.version="${RELEASE_TAG}" \ org.opencontainers.image.revision="${SOURCE_REVISION}" \ org.opencontainers.image.created="${BUILD_DATE}" \ se.jordbo.opencode-devbox.opencode-version="${OPENCODE_VERSION}" \ se.jordbo.opencode-devbox.install-omos="${INSTALL_OMOS}" \ se.jordbo.opencode-devbox.omos-version="${OMOS_VERSION}" \ se.jordbo.opencode-devbox.mempalace-toolkit-ref="${MEMPALACE_TOOLKIT_REF}" # The manifest is written from GROUND TRUTH — the live `opencode --version`, # the omos package's installed version (when present), and the actual # checked-out HEAD of /opt/mempalace-toolkit (cloned in the base) — not # merely the intended build-args. That way it also exposes a dependency # that silently resolved to something other than the requested value. # oh-my-opencode-slim is present only in the omos variant (JSON null # otherwise). NOTE: omos is installed under prefix /usr at build time, so # we resolve its dir via `npm root -g` with that prefix rather than the # runtime NPM_CONFIG_PREFIX the base sets for the developer volume. RUN set -e; \ mkdir -p /etc/opencode-devbox; \ rev() { git -C "$1" rev-parse HEAD 2>/dev/null || echo "unknown"; }; \ OPENCODE_V="$(opencode --version 2>/dev/null | head -n1 | tr -d '\r\n')"; \ OMOS_REV='null'; \ if [ "${INSTALL_OMOS}" = "true" ]; then \ OMOS_DIR="$(NPM_CONFIG_PREFIX=/usr npm root -g 2>/dev/null)/oh-my-opencode-slim"; \ OMOS_V="$(node -e "process.stdout.write(require('${OMOS_DIR}/package.json').version)" 2>/dev/null || echo unknown)"; \ OMOS_REV="\"${OMOS_V}\""; \ fi; \ { \ echo '{'; \ echo " \"release_tag\": \"${RELEASE_TAG}\","; \ echo " \"build_date\": \"${BUILD_DATE}\","; \ echo " \"source_revision\": \"${SOURCE_REVISION}\","; \ echo " \"opencode_version\": \"${OPENCODE_V}\","; \ echo " \"components\": {"; \ echo " \"opencode\": \"${OPENCODE_V}\","; \ echo " \"oh-my-opencode-slim\": ${OMOS_REV},"; \ echo " \"mempalace-toolkit\": \"$(rev /opt/mempalace-toolkit)\""; \ echo " }"; \ echo '}'; \ } > /etc/opencode-devbox/build-manifest.json; \ echo "── build manifest ──"; cat /etc/opencode-devbox/build-manifest.json # WORKDIR / ENTRYPOINT / CMD inherited from base.