flagon-io/g1t

public

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t/services/runner/base/Dockerfile

99 lines4,898 bytesCodeBlame
1# syntax=docker/dockerfile:1.7
2#
3# g1t-runner-base: everything a g1t sandbox has apart from the g1t runner
4# itself. Agents, checks, the merge queue, workflow jobs and g1t.page
5# builds all run in it: git, Node, Python, Go, Rust (with the formatter,
6# the linter and the wasm32 target), the usual build tools, and the
7# Claude Code CLI.
8#
9# Built and pushed by `node scripts/deploy.mjs build-base` (by hand, or by
10# .g1t/workflows/runner-base.yml), which records what it pushed in
11# services/runner/base.json. The runner's own image
12# (services/runner/Dockerfile) is this plus one file, so a change to the
13# runner builds in seconds. docs/DEPLOYING.md explains the two.
14#
15# Layers go from what changes least to what changes most: the system's
16# packages, then Go, then Rust, then the Claude Code CLI on top, so a new
17# CLI version rebuilds and pushes one layer.
18#
19# Build context: this folder (nothing is copied from it).
20
21FROM node:24-bookworm-slim
22
23# Docs, man pages and translations are never read in a sandbox; dpkg
24# leaves them out of every package installed from here on (copyright
25# files stay). Downloaded packages are kept for the build's apt cache.
26RUN printf '%s\n' \
27 'path-exclude=/usr/share/doc/*' \
28 'path-include=/usr/share/doc/*/copyright' \
29 'path-exclude=/usr/share/man/*' \
30 'path-exclude=/usr/share/info/*' \
31 'path-exclude=/usr/share/groff/*' \
32 'path-exclude=/usr/share/lintian/*' \
33 'path-exclude=/usr/share/linda/*' \
34 'path-exclude=/usr/share/locale/*' \
35 'path-include=/usr/share/locale/locale.alias' \
36 > /etc/dpkg/dpkg.cfg.d/01-g1t-slim \
37 && rm -f /etc/apt/apt.conf.d/docker-clean \
38 && echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-downloads
39
40# The system's packages. The apt cache and package lists live in
41# BuildKit's cache, not the image (run `sudo apt-get update` before
42# installing more). dpkg skips its fsync after every file, which an image
43# build has no use for and which made this step several times slower.
44RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
45 --mount=type=cache,target=/var/lib/apt/lists,sharing=locked \
46 apt-get update \
47 && apt-get install -y --no-install-recommends -o Dpkg::Options::=--force-unsafe-io \
48 git ca-certificates curl build-essential pkg-config libssl-dev \
49 python3 python3-pip python3-venv ripgrep jq zstd \
50 sudo unzip zip xz-utils wget file gnupg lsb-release openssh-client \
51 && rm -rf /var/log/apt /var/log/dpkg.log /var/cache/debconf/*-old
52
53# Workflows expect GitHub's runner layout under /home/runner, and sudo
54# without a password. The agent works in /work.
55RUN mkdir /work && chown node:node /work \
56 && mkdir -p /home/runner/work /home/runner/_temp /home/runner/_tool /home/runner/_actions \
57 && chown -R node:node /home/runner \
58 && echo 'node ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/node \
59 && chmod 0440 /etc/sudoers.d/node
60
61# Go, from go.dev (Debian's is years behind), without its own test suite
62# and API history, which only Go's developers use.
63ARG GO_VERSION=1.27.1
64ARG GO_SHA256=63d339f0da5ab53635a56f2490a7984dfe12dfcff22ad749f63edaf590168445
65RUN curl -fsSLo /tmp/go.tgz "https://go.dev/dl/go${GO_VERSION}.linux-amd64.tar.gz" \
66 && echo "${GO_SHA256} /tmp/go.tgz" | sha256sum -c - \
67 && tar -C /usr/local -xzf /tmp/go.tgz \
68 && rm -rf /tmp/go.tgz /usr/local/go/test /usr/local/go/api /usr/local/go/doc \
69 && ln -s /usr/local/go/bin/go /usr/local/go/bin/gofmt /usr/local/bin/
70
71# Claude Code refuses to skip permission prompts as root.
72USER node
73ENV HOME=/home/node
74ENV PATH=/home/node/.cargo/bin:/home/node/go/bin:$PATH
75
76# Rust stable, for the user the agent runs as, with the formatter and
77# linter that CI so often checks with (an agent that cannot run them only
78# finds out from a failed workflow), and the wasm32 target that Workers,
79# and g1t's own deploys, build for.
80RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
81 | sh -s -- -y --no-modify-path --profile minimal --default-toolchain stable \
82 --component rustfmt --component clippy --target wasm32-unknown-unknown \
83 && rm -rf /home/node/.rustup/downloads /home/node/.rustup/tmp \
84 /home/node/.rustup/toolchains/*/share/doc \
85 /home/node/.rustup/toolchains/*/share/man
86
87# Commits are the g1t agent's; the harness does not sign them as its own.
88RUN mkdir -p /home/node/.claude \
89 && echo '{"includeCoAuthoredBy": false}' > /home/node/.claude/settings.json
90
91# The Claude Code CLI, pinned, on top: it changes most often of anything
92# here, and is one layer to rebuild and push.
93ARG CLAUDE_CODE_VERSION=2.1.291
94USER root
95# npm's cache stays in BuildKit's cache, out of the image.
96RUN --mount=type=cache,target=/tmp/npm-cache \
97 npm install --global --no-audit --no-fund --cache /tmp/npm-cache "@anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}" \
98 && claude --version
99USER node