Skip to content

Commit 6d688fa

Browse files
chapterjasonclaude
andcommitted
Speed up workspace image builds
- Drop linux/arm64 from the publish matrix (no arm64 consumers; QEMU emulation roughly doubled build time and cache size). - Remove docker/setup-qemu-action — no longer needed without arm64. - Split scripts/ bind mounts in base + cpp Dockerfiles so each installer lives in its own RUN with a narrow bind. Bumping one tool (e.g. web-shell) no longer invalidates the other installer layers. - Pass BASE_IMAGE to the stacks job as image@digest from the base job's build-push-action output, not the base-<short_sha> tag. Cache hits across stack rebuilds when only non-base source changed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7e90994 commit 6d688fa

3 files changed

Lines changed: 30 additions & 21 deletions

File tree

.github/workflows/publish-workspaces.yml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ jobs:
1818
packages: write
1919
outputs:
2020
short_sha: ${{ steps.sha.outputs.short }}
21+
digest: ${{ steps.build.outputs.digest }}
22+
image: ${{ steps.img.outputs.name }}
2123
steps:
2224
- uses: actions/checkout@v6
2325

@@ -29,7 +31,6 @@ jobs:
2931
id: img
3032
run: echo "name=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/coder-workspace" >> "$GITHUB_OUTPUT"
3133

32-
- uses: docker/setup-qemu-action@v4
3334
- uses: docker/setup-buildx-action@v4
3435

3536
- name: Log in to GHCR
@@ -49,11 +50,12 @@ jobs:
4950
type=sha,format=short,prefix=base-
5051
5152
- name: Build and push
53+
id: build
5254
uses: docker/build-push-action@v7
5355
with:
5456
context: .
5557
file: ./src/base/Dockerfile
56-
platforms: linux/amd64,linux/arm64
58+
platforms: linux/amd64
5759
push: true
5860
tags: ${{ steps.meta.outputs.tags }}
5961
labels: ${{ steps.meta.outputs.labels }}
@@ -77,7 +79,6 @@ jobs:
7779
id: img
7880
run: echo "name=ghcr.io/${GITHUB_REPOSITORY_OWNER,,}/coder-workspace" >> "$GITHUB_OUTPUT"
7981

80-
- uses: docker/setup-qemu-action@v4
8182
- uses: docker/setup-buildx-action@v4
8283

8384
- name: Log in to GHCR
@@ -101,11 +102,11 @@ jobs:
101102
with:
102103
context: .
103104
file: ./src/${{ matrix.stack }}/Dockerfile
104-
platforms: linux/amd64,linux/arm64
105+
platforms: linux/amd64
105106
push: true
106107
tags: ${{ steps.meta.outputs.tags }}
107108
labels: ${{ steps.meta.outputs.labels }}
108109
build-args: |
109-
BASE_IMAGE=${{ steps.img.outputs.name }}:base-${{ needs.base.outputs.short_sha }}
110+
BASE_IMAGE=${{ needs.base.outputs.image }}@${{ needs.base.outputs.digest }}
110111
cache-from: type=gha,scope=workspace-${{ matrix.stack }}
111112
cache-to: type=gha,mode=max,scope=workspace-${{ matrix.stack }}

src/base/Dockerfile

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,19 +78,27 @@ USER root
7878
# env var the devcontainer CLI used at feature-install time.
7979
ENV _REMOTE_USER=${USERNAME}
8080

81-
# System-wide installers run as root.
82-
RUN --mount=type=bind,source=scripts,target=/scripts \
83-
for s in context-mode home-persist jetbrains; do \
84-
bash "/scripts/$s/install.sh"; \
85-
done
81+
# Per-tool bind mounts so a change in one installer only busts its own layer.
82+
# A single mount of scripts/ would hash the whole dir — bumping web-shell
83+
# would rebuild every tool.
84+
RUN --mount=type=bind,source=scripts/context-mode,target=/scripts/context-mode \
85+
bash /scripts/context-mode/install.sh
86+
RUN --mount=type=bind,source=scripts/home-persist,target=/scripts/home-persist \
87+
bash /scripts/home-persist/install.sh
88+
RUN --mount=type=bind,source=scripts/jetbrains,target=/scripts/jetbrains \
89+
bash /scripts/jetbrains/install.sh
8690

8791
# Per-user installers run as the remote user directly — upstream installers
8892
# write into $HOME with correct ownership; no root+chown dance.
8993
USER ${USERNAME}
90-
RUN --mount=type=bind,source=scripts,target=/scripts \
91-
for s in nvm claude-code rtk web-shell; do \
92-
bash "/scripts/$s/install.sh"; \
93-
done
94+
RUN --mount=type=bind,source=scripts/nvm,target=/scripts/nvm \
95+
bash /scripts/nvm/install.sh
96+
RUN --mount=type=bind,source=scripts/claude-code,target=/scripts/claude-code \
97+
bash /scripts/claude-code/install.sh
98+
RUN --mount=type=bind,source=scripts/rtk,target=/scripts/rtk \
99+
bash /scripts/rtk/install.sh
100+
RUN --mount=type=bind,source=scripts/web-shell,target=/scripts/web-shell \
101+
bash /scripts/web-shell/install.sh
94102
USER root
95103

96104
# Coder agent: systemd unit runs /etc/coder/agent-init.sh as the workspace

src/cpp/Dockerfile

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ FROM ${BASE_IMAGE}
66
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
77
ENV DEBIAN_FRONTEND=noninteractive
88

9-
# System-wide installers (root).
10-
RUN --mount=type=bind,source=scripts,target=/scripts \
11-
bash "/scripts/llvm/install.sh"
9+
# Per-tool bind mounts so a change in one installer only busts its own layer.
10+
RUN --mount=type=bind,source=scripts/llvm,target=/scripts/llvm \
11+
bash /scripts/llvm/install.sh
1212

1313
RUN printf 'export CC=/usr/local/bin/clang\nexport CXX=/usr/local/bin/clang++\n' \
1414
> /etc/profile.d/llvm-env.sh
1515

1616
# cmake and sccache drop their binaries in $HOME/.local → run as the remote user.
1717
USER ${_REMOTE_USER}
18-
RUN --mount=type=bind,source=scripts,target=/scripts \
19-
for s in cmake sccache; do \
20-
bash "/scripts/$s/install.sh"; \
21-
done
18+
RUN --mount=type=bind,source=scripts/cmake,target=/scripts/cmake \
19+
bash /scripts/cmake/install.sh
20+
RUN --mount=type=bind,source=scripts/sccache,target=/scripts/sccache \
21+
bash /scripts/sccache/install.sh
2222
USER root

0 commit comments

Comments
 (0)