diff --git a/Dockerfile b/Dockerfile index a2f57b9..38ad14b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,51 +7,64 @@ # lives under ~/workspace. # # Three-stage build: -# base — apt packages, user, sudo, init — shared by builder and final -# builder — fetches relocatable toolchains (opencode, Homebrew, mise) -# final — copies in runtimes from builder; carries only runtime layers +# runtime — minimal apt packages, user, sudo, init +# builder — runtime + compiler toolchain + relocatable toolchains (opencode, Homebrew, mise) +# final — copies in runtimes from builder; carries only runtime layers ARG OPENCODE_VERSION=0.0.0 ARG IMAGE_CREATED="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" # --------------------------------------------------------------------------- -# base: common runtime layer (apt, user, sudo, init) +# runtime: minimal runtime layer (apt, user, sudo, init) # --------------------------------------------------------------------------- -FROM ubuntu:26.04 AS base +FROM ubuntu:26.04 AS runtime ENV DEBIAN_FRONTEND=noninteractive -# General dev toolchain: VCS, build tools, languages, CLI utilities. +# CLI utilities for day-to-day dev work (git, curl, etc.). RUN apt-get update && apt-get install -y --no-install-recommends \ - ca-certificates curl git openssh-client unzip xz-utils \ - build-essential jq pkg-config \ - less sudo tini tzdata locales \ + ca-certificates curl git unzip \ + less libatomic1 sudo tini tzdata \ && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb \ + && rm -rf /usr/share/doc /usr/share/man /usr/share/locale \ && userdel --remove ubuntu 2>/dev/null || true; \ groupdel ubuntu 2>/dev/null || true; \ groupadd --gid 1000 opencode \ && useradd --uid 1000 --gid 1000 --create-home --shell /bin/bash opencode \ && echo 'opencode ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/opencode \ && chmod 0440 /etc/sudoers.d/opencode \ - && visudo -cf /etc/sudoers.d/opencode + && visudo -cf /etc/sudoers.d/opencode \ + && find /var/log -type f -delete 2>/dev/null; \ + rm -f /var/cache/debconf/*.dat 2>/dev/null || true # Entrypoint (tini + init script) COPY entrypoint.sh /usr/local/bin/entrypoint.sh RUN chmod 0755 /usr/local/bin/entrypoint.sh # --------------------------------------------------------------------------- -# builder: fetch relocatable toolchains (layers are ephemeral — -# only what's explicitly COPIED to final lands in the runtime image). +# builder: runtime + compiler toolchain + relocatable toolchains — +# only what's explicitly COPIED to final lands in the runtime image. # Order: most-stable first, so frequent version bumps don't bust the # cache of the other toolchains. # --------------------------------------------------------------------------- -FROM base AS builder +FROM runtime AS builder -# 1. Homebrew — the install script URL is stable; brew releases rarely -# invalidate the layer once installed. +# Compiler toolchain needed for building native extensions during +# toolchain installation (Homebrew bottles, mise plugins, etc.). +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential pkg-config xz-utils \ + && rm -rf /var/lib/apt/lists/* /var/cache/apt/archives/*.deb + +# 1. Homebrew — partial clone with --filter=blob:none avoids downloading +# all past file versions, saving ~70 MB while keeping brew update working. RUN mkdir -p /home/linuxbrew \ && chown opencode:opencode /home/linuxbrew \ - && sudo -u opencode NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" \ + && sudo -u opencode git clone --filter=blob:none \ + https://github.com/Homebrew/brew /home/linuxbrew/.linuxbrew/Homebrew \ + && sudo -u opencode mkdir -p /home/linuxbrew/.linuxbrew/bin \ + && sudo -u opencode ln -sf \ + /home/linuxbrew/.linuxbrew/Homebrew/bin/brew /home/linuxbrew/.linuxbrew/bin/brew \ + && sudo -u opencode /home/linuxbrew/.linuxbrew/bin/brew update --force \ && sudo -u opencode /home/linuxbrew/.linuxbrew/bin/brew cleanup --prune=all \ && sudo -u opencode rm -rf "$(sudo -u opencode /home/linuxbrew/.linuxbrew/bin/brew --cache)" \ && rm -rf /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/test \ @@ -86,7 +99,7 @@ RUN curl -fsSL https://opencode.ai/install | VERSION="${OPENCODE_VERSION}" bash # --------------------------------------------------------------------------- # final: runtime image — only the base layer plus copied-in toolchains # --------------------------------------------------------------------------- -FROM base +FROM runtime ARG OPENCODE_VERSION ARG IMAGE_CREATED @@ -110,20 +123,22 @@ LABEL io.artifacthub.package.readme-url="https://raw.githubusercontent.com/spris io.artifacthub.package.maintainers='[{"name":"Gabriel Meola","email":"banter@gabe.mx"}]' \ io.artifacthub.package.keywords="opencode,server,docker,ai,code,editor,development" -# Runtimes copied from builder (most-stable first so frequent version -# bumps don't invalidate cache for the other layers). +# Copy layers +# Ordered by most stable layers first so cache can be reused. COPY --from=builder --chown=opencode:opencode /home/linuxbrew /home/linuxbrew -COPY --from=builder /opt/opencode /usr/local/bin/opencode + +# Zerobrew — fast Homebrew alternative; mise zerobrew backend. +COPY --from=builder /home/opencode/.local/bin/zb /usr/local/bin/zb +COPY --from=builder /home/opencode/.local/bin/zbx /usr/local/bin/zbx +COPY --from=builder --chown=opencode:opencode /home/opencode/.local/share/zerobrew /home/opencode/.local/share/zerobrew # Mise — dev tool manager; auto-installs tools defined in the global config. COPY --from=builder /usr/local/bin/mise /usr/local/bin/mise COPY --from=builder --chown=opencode:opencode /opt/mise /opt/mise COPY mise-config.toml /etc/mise/config.toml -# Zerobrew — fast Homebrew alternative; mise zerobrew backend. -COPY --from=builder /home/opencode/.local/bin/zb /usr/local/bin/zb -COPY --from=builder /home/opencode/.local/bin/zbx /usr/local/bin/zbx -COPY --from=builder --chown=opencode:opencode /home/opencode/.local/share/zerobrew /home/opencode/.local/share/zerobrew +# Opencode +COPY --from=builder /opt/opencode /usr/local/bin/opencode # Verify runtime and set up login-shell PATH and auto-install handler RUN opencode --version \ diff --git a/README.md b/README.md index 88739d5..26de77e 100644 --- a/README.md +++ b/README.md @@ -14,12 +14,11 @@ A general-purpose Ubuntu Docker image for running [opencode](https://github.com/ | **Base OS** | ubuntu:26.04 | | **User** | `opencode` (uid/gid 1000), passwordless sudo | | **opencode** | Pinned in `version.txt` as `OPENCODE_VERSION` build arg | -| **Build tools** | `build-essential`, `pkg-config` (for native npm addons, pip source builds) | -| **Python 3** | Lazy-installed via mise (see table below) | +| **Lazy Installed Tools** | Node, Python3 (see below) | | **Homebrew** | Linux-native Homebrew (`/home/linuxbrew/.linuxbrew`) — `brew` on PATH | | **zerobrew** | Faster Homebrew alternative (`zb` on PATH) -- used as mise backend for lazy-installed tools | | **mise** | Dev tool manager — tools listed below install on first use via `zerobrew` backend | -| **CLI utilities** | git, curl, jq, less, unzip, ssh client | +| **CLI utilities** | git, curl, less, unzip | | **Init** | tini as PID 1 (zombie reaping, clean shutdown) | ### Lazy-installed tools @@ -29,6 +28,7 @@ These tools install on first use (via mise → github/zerobrew): | Tool | Command | Backend | |---|---|---| | GitHub CLI | `gh` | github | +| jq | `jq` | github | | GitLab CLI | `glab` | zerobrew | | Ruby | `ruby` | zerobrew | | ripgrep | `rg` | github | @@ -38,7 +38,7 @@ These tools install on first use (via mise → github/zerobrew): | Micro | `micro` | github | | Nano | `nano` | zerobrew | | Python 3 | `python3` | zerobrew | -| Node.js | `node` | github | +| Node.js | `node` | core | | Sapling | `sl` | github | The image ships with a system config at `/etc/mise/config.toml` with these pre-approved tools. Users can add or override tools by creating `~/.config/mise/config.toml` — mise merges both. diff --git a/mise-config.toml b/mise-config.toml index 646d0aa..21c3c45 100644 --- a/mise-config.toml +++ b/mise-config.toml @@ -1,5 +1,6 @@ [tools] "github:cli/cli" = "latest" # shim:gh +"github:jqlang/jq" = "latest" "zerobrew:glab" = "latest" "zerobrew:ruby" = "latest" "github:BurntSushi/ripgrep" = "latest" # shim:rg @@ -9,5 +10,5 @@ "github:zyedidia/micro" = "latest" "zerobrew:nano" = "latest" "zerobrew:python" = "latest" -"github:nodejs/node" = "latest" +"node" = "latest" "github:facebook/sapling" = "latest" # shim:sl