From 163e1efa8fa1cb6586bcfaca07592af8d70dd646 Mon Sep 17 00:00:00 2001 From: Thomas Kosiewski Date: Wed, 22 Apr 2026 13:13:31 +0000 Subject: [PATCH] feat(dogfood/coder): add brew and mise to ubuntu images --- dogfood/coder/main.tf | 39 ++++++++++++++++++++++++++ dogfood/coder/ubuntu-22.04/Dockerfile | 40 +++++++++++++++++++++++---- dogfood/coder/ubuntu-26.04/Dockerfile | 38 +++++++++++++++++++++---- 3 files changed, 106 insertions(+), 11 deletions(-) diff --git a/dogfood/coder/main.tf b/dogfood/coder/main.tf index 5003f8f776c5a..fbd2d951328e6 100644 --- a/dogfood/coder/main.tf +++ b/dogfood/coder/main.tf @@ -747,6 +747,38 @@ resource "docker_volume" "home_volume" { } } +resource "coder_metadata" "homebrew_volume" { + resource_id = docker_volume.homebrew_volume.id + hide = true # Hide it as it only backs Homebrew state. +} + +resource "docker_volume" "homebrew_volume" { + name = "coder-${data.coder_workspace.me.id}-homebrew" + # Protect the volume from being deleted due to changes in attributes. + lifecycle { + ignore_changes = all + } + # Add labels in Docker to keep track of orphan resources. + labels { + label = "coder.owner" + value = data.coder_workspace_owner.me.name + } + labels { + label = "coder.owner_id" + value = data.coder_workspace_owner.me.id + } + labels { + label = "coder.workspace_id" + value = data.coder_workspace.me.id + } + # This field becomes outdated if the workspace is renamed but can + # be useful for debugging or cleaning out dangling volumes. + labels { + label = "coder.workspace_name_at_creation" + value = data.coder_workspace.me.name + } +} + resource "coder_metadata" "docker_volume" { resource_id = docker_volume.docker_volume.id hide = true # Hide it as it is not useful to see in the UI. @@ -843,6 +875,13 @@ resource "docker_container" "workspace" { volume_name = docker_volume.home_volume.name read_only = false } + # Homebrew is baked into this path. A Docker named volume copies the + # image contents on first mount, then persists user-installed formulae. + volumes { + container_path = "/home/linuxbrew/" + volume_name = docker_volume.homebrew_volume.name + read_only = false + } volumes { container_path = "/var/lib/docker/" volume_name = docker_volume.docker_volume.name diff --git a/dogfood/coder/ubuntu-22.04/Dockerfile b/dogfood/coder/ubuntu-22.04/Dockerfile index 439a36f51076d..9ec282731fadf 100644 --- a/dogfood/coder/ubuntu-22.04/Dockerfile +++ b/dogfood/coder/ubuntu-22.04/Dockerfile @@ -27,7 +27,7 @@ ENV PATH=$PATH:/usr/local/go/bin ARG GOPATH="/tmp/" # Install Go utilities. RUN apt-get update && \ - apt-get install --yes gcc && \ + apt-get install --yes gcc libc6-dev && \ mkdir --parents /usr/local/go && \ tar --extract --gzip --directory=/usr/local/go --file=/usr/local/go.tar.gz --strip-components=1 && \ mkdir --parents "$GOPATH" && \ @@ -163,6 +163,7 @@ RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.edge.kernel.org/u openssh-server \ openssl \ pkg-config \ + procps \ postgresql-16 \ python3 \ python3-pip \ @@ -288,7 +289,10 @@ ARG GOLANGCI_LINT_VERSION=1.64.8 \ KUBECTX_VERSION=0.9.4 \ SYFT_VERSION=1.20.0 \ COSIGN_VERSION=2.4.3 \ - BUN_VERSION=1.2.15 + BUN_VERSION=1.2.15 \ + MISE_VERSION=v2026.4.19 \ + MISE_SHA256=6b58ff5f1e1ce98ed2b7e5372c344ea48182c460e5b6df12d9e0def35aad4438 \ + MISE_INSTALL_DIR=/opt/mise/bin RUN \ # golangci-lint performs static code analysis for our Go code @@ -324,6 +328,25 @@ RUN useradd coder \ --uid=1000 \ --user-group +# Install mise to a stable path outside /home/coder, but keep its target +# directory writable so `mise self-update` can replace the binary as coder. +RUN install --directory --owner=coder --group=coder --mode=0755 "${MISE_INSTALL_DIR}" && \ + curl --silent --show-error --location --fail \ + "https://github.com/jdx/mise/releases/download/${MISE_VERSION}/mise-${MISE_VERSION}-linux-x64" \ + --output "${MISE_INSTALL_DIR}/mise" && \ + echo "${MISE_SHA256} ${MISE_INSTALL_DIR}/mise" | sha256sum -c && \ + chown coder:coder "${MISE_INSTALL_DIR}/mise" && \ + chmod 0755 "${MISE_INSTALL_DIR}/mise" && \ + ln --symbolic "${MISE_INSTALL_DIR}/mise" /usr/local/bin/mise && \ + test -x /usr/local/bin/mise && \ + sudo --login --user=coder /bin/bash -lc 'set -euo pipefail && mise_bin="$(readlink --canonicalize /usr/local/bin/mise)" && test -w "$(dirname "$mise_bin")" && /usr/local/bin/mise --version && /usr/local/bin/mise self-update --help >/dev/null && /usr/local/bin/mise upgrade --help >/dev/null' + +# Install Homebrew as the coder user so the supported Linux prefix remains +# writable after the image build. +RUN sudo --login --user=coder env NONINTERACTIVE=1 CI=1 /bin/bash -lc 'set -euo pipefail && curl --silent --show-error --location --fail https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | /bin/bash' && \ + test -x /home/linuxbrew/.linuxbrew/bin/brew && \ + sudo --login --user=coder /bin/bash -lc '/home/linuxbrew/.linuxbrew/bin/brew --version' + # Adjust OpenSSH config RUN echo "PermitUserEnvironment yes" >>/etc/ssh/sshd_config && \ echo "X11Forwarding yes" >>/etc/ssh/sshd_config && \ @@ -346,10 +369,15 @@ COPY --from=proto /tmp/include /usr/local/bin/include USER coder -# Ensure go bins are in the 'coder' user's path. Note that no go bins are -# installed in this docker file, as they'd be mounted over by the persistent -# home volume. -ENV PATH="/home/coder/go/bin:${PATH}" +# Configure Homebrew and mise for the coder user. mise shims must stay first +# so `command -v` and `mise doctor` resolve mise-managed tools ahead of +# Homebrew and system binaries. Note that no go bins are installed in this +# docker file, as they'd be mounted over by the persistent home volume. +ENV HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew" \ + HOMEBREW_CELLAR="/home/linuxbrew/.linuxbrew/Cellar" \ + HOMEBREW_REPOSITORY="/home/linuxbrew/.linuxbrew/Homebrew" \ + MISE_DATA_DIR="/home/coder/.local/share/mise" +ENV PATH="${MISE_DATA_DIR}/shims:${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin:/home/coder/go/bin:${PATH}" # Override CARGO_HOME so cargo registry/cache writes go to the coder # user's home directory instead of the root-owned /usr/local/cargo. diff --git a/dogfood/coder/ubuntu-26.04/Dockerfile b/dogfood/coder/ubuntu-26.04/Dockerfile index 9e507996fe78f..9e1268a9d0f02 100644 --- a/dogfood/coder/ubuntu-26.04/Dockerfile +++ b/dogfood/coder/ubuntu-26.04/Dockerfile @@ -162,6 +162,7 @@ RUN sed -i 's|http://archive.ubuntu.com/ubuntu/|http://mirrors.edge.kernel.org/u openssh-server \ openssl \ pkg-config \ + procps \ postgresql-18 \ python3 \ python3-pip \ @@ -286,7 +287,10 @@ ARG GOLANGCI_LINT_VERSION=1.64.8 \ KUBECTX_VERSION=0.9.4 \ SYFT_VERSION=1.20.0 \ COSIGN_VERSION=2.4.3 \ - BUN_VERSION=1.2.15 + BUN_VERSION=1.2.15 \ + MISE_VERSION=v2026.4.19 \ + MISE_SHA256=6b58ff5f1e1ce98ed2b7e5372c344ea48182c460e5b6df12d9e0def35aad4438 \ + MISE_INSTALL_DIR=/opt/mise/bin RUN \ # golangci-lint performs static code analysis for our Go code @@ -325,6 +329,25 @@ RUN userdel -r ubuntu && \ --uid=1000 \ --user-group +# Install mise to a stable path outside /home/coder, but keep its target +# directory writable so `mise self-update` can replace the binary as coder. +RUN install --directory --owner=coder --group=coder --mode=0755 "${MISE_INSTALL_DIR}" && \ + curl --silent --show-error --location --fail \ + "https://github.com/jdx/mise/releases/download/${MISE_VERSION}/mise-${MISE_VERSION}-linux-x64" \ + --output "${MISE_INSTALL_DIR}/mise" && \ + echo "${MISE_SHA256} ${MISE_INSTALL_DIR}/mise" | sha256sum -c && \ + chown coder:coder "${MISE_INSTALL_DIR}/mise" && \ + chmod 0755 "${MISE_INSTALL_DIR}/mise" && \ + ln --symbolic "${MISE_INSTALL_DIR}/mise" /usr/local/bin/mise && \ + test -x /usr/local/bin/mise && \ + sudo --login --user=coder /bin/bash -lc 'set -euo pipefail && mise_bin="$(readlink --canonicalize /usr/local/bin/mise)" && test -w "$(dirname "$mise_bin")" && /usr/local/bin/mise --version && /usr/local/bin/mise self-update --help >/dev/null && /usr/local/bin/mise upgrade --help >/dev/null' + +# Install Homebrew as the coder user so the supported Linux prefix remains +# writable after the image build. +RUN sudo --login --user=coder env NONINTERACTIVE=1 CI=1 /bin/bash -lc 'set -euo pipefail && curl --silent --show-error --location --fail https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh | /bin/bash' && \ + test -x /home/linuxbrew/.linuxbrew/bin/brew && \ + sudo --login --user=coder /bin/bash -lc '/home/linuxbrew/.linuxbrew/bin/brew --version' + # Adjust OpenSSH config RUN echo "PermitUserEnvironment yes" >>/etc/ssh/sshd_config && \ echo "X11Forwarding yes" >>/etc/ssh/sshd_config && \ @@ -347,10 +370,15 @@ COPY --from=proto /tmp/include /usr/local/bin/include USER coder -# Ensure go bins are in the 'coder' user's path. Note that no go bins are -# installed in this docker file, as they'd be mounted over by the persistent -# home volume. -ENV PATH="/home/coder/go/bin:${PATH}" +# Configure Homebrew and mise for the coder user. mise shims must stay first +# so `command -v` and `mise doctor` resolve mise-managed tools ahead of +# Homebrew and system binaries. Note that no go bins are installed in this +# docker file, as they'd be mounted over by the persistent home volume. +ENV HOMEBREW_PREFIX="/home/linuxbrew/.linuxbrew" \ + HOMEBREW_CELLAR="/home/linuxbrew/.linuxbrew/Cellar" \ + HOMEBREW_REPOSITORY="/home/linuxbrew/.linuxbrew/Homebrew" \ + MISE_DATA_DIR="/home/coder/.local/share/mise" +ENV PATH="${MISE_DATA_DIR}/shims:${HOMEBREW_PREFIX}/bin:${HOMEBREW_PREFIX}/sbin:/home/coder/go/bin:${PATH}" # Override CARGO_HOME so cargo registry/cache writes go to the coder # user's home directory instead of the root-owned /usr/local/cargo.