Skip to content

Commit a3cc91c

Browse files
gatezhclaude
andcommitted
feat(ci): use docker/github-builder with native ARM runners
Replace QEMU emulation with native amd64/arm64 runners via Docker's reusable build workflow. Builds distribute across native runners automatically, eliminating the QEMU bottleneck. - Switch to docker/github-builder/.github/workflows/build.yml@v1 - Remove QEMU setup, manual Buildx config, cache busting ARG - Add native verify jobs on ubuntu-24.04 and ubuntu-24.04-arm - Update README with native runner details Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bf6823d commit a3cc91c

3 files changed

Lines changed: 72 additions & 78 deletions

File tree

‎.github/workflows/build-claude-code.yml‎

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -8,98 +8,98 @@ on:
88
schedule:
99
- cron: '0 11 * * *'
1010
workflow_dispatch:
11-
inputs:
12-
no-cache:
13-
description: 'Full rebuild without Docker cache'
14-
type: boolean
15-
default: false
1611

1712
permissions:
1813
contents: read
1914
packages: write
20-
attestations: write
2115
id-token: write
2216

23-
env:
24-
REGISTRY: ghcr.io
25-
2617
concurrency:
2718
group: build-claude-code-${{ github.ref }}
2819
cancel-in-progress: true
2920

3021
jobs:
31-
build-and-push:
32-
name: Build & Push (${{ matrix.target }})
33-
runs-on: ubuntu-24.04
22+
build-default:
23+
uses: docker/github-builder/.github/workflows/build.yml@v1
24+
permissions:
25+
contents: read
26+
packages: write
27+
id-token: write
28+
with:
29+
output: image
30+
push: true
31+
target: default
32+
context: claude-code/.devcontainer
33+
file: claude-code/.devcontainer/Dockerfile
34+
platforms: linux/amd64,linux/arm64
35+
meta-images: ghcr.io/gatezh/devcontainer-images/claude-code
36+
meta-tags: |
37+
type=raw,value=latest
38+
type=sha,prefix=
39+
type=raw,value={{date 'YYYYMMDD'}}
40+
secrets:
41+
registry-auths: |
42+
- registry: ghcr.io
43+
username: ${{ github.actor }}
44+
password: ${{ secrets.GITHUB_TOKEN }}
45+
46+
build-sandbox:
47+
uses: docker/github-builder/.github/workflows/build.yml@v1
48+
permissions:
49+
contents: read
50+
packages: write
51+
id-token: write
52+
with:
53+
output: image
54+
push: true
55+
target: sandbox
56+
context: claude-code/.devcontainer
57+
file: claude-code/.devcontainer/Dockerfile
58+
platforms: linux/amd64,linux/arm64
59+
meta-images: ghcr.io/gatezh/devcontainer-images/claude-code-sandbox
60+
meta-tags: |
61+
type=raw,value=latest
62+
type=sha,prefix=
63+
type=raw,value={{date 'YYYYMMDD'}}
64+
secrets:
65+
registry-auths: |
66+
- registry: ghcr.io
67+
username: ${{ github.actor }}
68+
password: ${{ secrets.GITHUB_TOKEN }}
69+
70+
verify:
71+
name: Verify ${{ matrix.image-suffix }} (${{ matrix.arch }})
72+
needs: [build-default, build-sandbox]
3473
strategy:
74+
fail-fast: false
3575
matrix:
3676
include:
37-
- target: default
38-
image-suffix: claude-code
77+
- image-suffix: claude-code
78+
verify-command: "bun --version || true && claude --version && mise --version && fish --version"
79+
runner: ubuntu-24.04
80+
arch: amd64
81+
- image-suffix: claude-code
3982
verify-command: "bun --version || true && claude --version && mise --version && fish --version"
40-
- target: sandbox
41-
image-suffix: claude-code-sandbox
83+
runner: ubuntu-24.04-arm
84+
arch: arm64
85+
- image-suffix: claude-code-sandbox
86+
verify-command: "claude --version && mise --version && fish --version && which iptables"
87+
runner: ubuntu-24.04
88+
arch: amd64
89+
- image-suffix: claude-code-sandbox
4290
verify-command: "claude --version && mise --version && fish --version && which iptables"
91+
runner: ubuntu-24.04-arm
92+
arch: arm64
93+
runs-on: ${{ matrix.runner }}
4394
steps:
44-
- uses: actions/checkout@v6
45-
46-
- name: Lowercase image base
47-
id: repo
48-
run: echo "image_base=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT"
49-
50-
- name: Set up QEMU
51-
uses: docker/setup-qemu-action@v4
52-
53-
- name: Set up Docker Buildx
54-
uses: docker/setup-buildx-action@v4
55-
5695
- name: Log in to GHCR
5796
uses: docker/login-action@v4
5897
with:
59-
registry: ${{ env.REGISTRY }}
98+
registry: ghcr.io
6099
username: ${{ github.actor }}
61100
password: ${{ secrets.GITHUB_TOKEN }}
62101

63-
- name: Extract metadata (tags, labels)
64-
id: meta
65-
uses: docker/metadata-action@v6
66-
with:
67-
images: ${{ steps.repo.outputs.image_base }}/${{ matrix.image-suffix }}
68-
tags: |
69-
type=raw,value=latest
70-
type=sha,prefix=
71-
type=raw,value={{date 'YYYYMMDD'}}
72-
73-
- name: Build and push
74-
id: push
75-
uses: docker/build-push-action@v7
76-
with:
77-
context: claude-code/.devcontainer
78-
file: claude-code/.devcontainer/Dockerfile
79-
target: ${{ matrix.target }}
80-
platforms: linux/amd64,linux/arm64
81-
push: true
82-
tags: ${{ steps.meta.outputs.tags }}
83-
labels: ${{ steps.meta.outputs.labels }}
84-
build-args: |
85-
CACHE_BUST=${{ github.event_name != 'push' && github.run_id || 'stable' }}
86-
no-cache: ${{ inputs.no-cache == true }}
87-
cache-from: type=gha,scope=${{ matrix.target }}
88-
cache-to: type=gha,mode=max,scope=${{ matrix.target }}
89-
90-
- name: Generate artifact attestation
91-
uses: actions/attest@v4
92-
with:
93-
subject-name: ${{ steps.repo.outputs.image_base }}/${{ matrix.image-suffix }}
94-
subject-digest: ${{ steps.push.outputs.digest }}
95-
push-to-registry: true
96-
97-
- name: Verify image (amd64)
98-
run: |
99-
docker pull --platform linux/amd64 ${{ steps.repo.outputs.image_base }}/${{ matrix.image-suffix }}:latest
100-
docker run --rm --platform linux/amd64 ${{ steps.repo.outputs.image_base }}/${{ matrix.image-suffix }}:latest bash -c "${{ matrix.verify-command }}"
101-
102-
- name: Verify image (arm64)
102+
- name: Verify image
103103
run: |
104-
docker pull --platform linux/arm64 ${{ steps.repo.outputs.image_base }}/${{ matrix.image-suffix }}:latest
105-
docker run --rm --platform linux/arm64 ${{ steps.repo.outputs.image_base }}/${{ matrix.image-suffix }}:latest bash -c "${{ matrix.verify-command }}"
104+
docker pull ghcr.io/gatezh/devcontainer-images/${{ matrix.image-suffix }}:latest
105+
docker run --rm ghcr.io/gatezh/devcontainer-images/${{ matrix.image-suffix }}:latest bash -c "${{ matrix.verify-command }}"

‎claude-code/.devcontainer/Dockerfile‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,6 @@ RUN npx -y playwright@${PLAYWRIGHT_VERSION} install-deps chromium
119119
USER node
120120
RUN npx -y playwright@${PLAYWRIGHT_VERSION} install --only-shell
121121

122-
# ── Cache bust ────────────────────────────────────────────────────────────────
123-
# Layers above (system packages, Playwright, etc.) stay cached for speed.
124-
# Layers below (Claude Code, agent-browser) rebuild when CACHE_BUST changes,
125-
# picking up the latest versions on daily scheduled builds.
126-
ARG CACHE_BUST=stable
127-
128122
# ── Claude Code CLI (native installer) ───────────────────────────────────────
129123
# Native installer replaces deprecated npm method (npm install -g @anthropic-ai/claude-code)
130124
# See: https://code.claude.com/docs/en/getting-started

‎claude-code/README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ Both variants are built for:
4040

4141
## Automatic Rebuilds
4242

43-
The image rebuilds daily at 5am MT (11:00 UTC). Cached layers (system packages, Playwright) are reused for speed, while Claude Code and agent-browser always install fresh via a cache-busting build arg. Manual rebuilds can be triggered via the "Run workflow" button in the Actions UI.
43+
The image rebuilds daily at 5am MT (11:00 UTC) using native runners for both amd64 and arm64 (no QEMU emulation). Each rebuild picks up the latest Claude Code and agent-browser. Manual rebuilds can be triggered via the "Run workflow" button in the Actions UI.
4444

4545
## Quick Start
4646

0 commit comments

Comments
 (0)