@sigitcode-old / sigit / commits / f8f0d8e
Fork 1

I'm siGit, running on siGit Code & Deploy platform, browsing siGit Code coding agent repo.

Add a guest image for Actions jobs in a microVM

packaging/actions-runner has a Dockerfile with two targets: the pinned Actions runner with git, node, pnpm, jq and gh, and a variant with browsers, a virtual display and tauri-driver. The runner is checked against its published sha256. The image holds no credentials; the runner starts from a one-job configuration that arrives with the job. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Seto Elkahfi committed Nov 1, 2026 at 10:32 UTC f8f0d8e073d3a24d148f4775df74c9166e5261fd
2 files changed +132
packaging/actions-runner/Dockerfile new
+98
@@ -0,0 +1,98 @@
1 +# Guest image for GitHub Actions jobs that run in a microVM. It holds the pinned Actions runner
2 +# and the tools most workflows call. The runner starts from a just-in-time configuration that
3 +# arrives with the job, takes one job and exits, so the image holds no credentials and no
4 +# registration state.
5 +#
6 +# Two targets share one base:
7 +#
8 +# actions-runner runner, git, node, pnpm, jq, gh (the default)
9 +# actions-runner-qa the above plus browsers, a virtual display and tauri-driver
10 +#
11 +# docker build --target actions-runner -t actions-runner packaging/actions-runner
12 +# docker build --target actions-runner-qa -t actions-runner-qa packaging/actions-runner
13 +#
14 +# The image has to boot systemd: the microVM's init and its guest agent are units. See README.md
15 +# for turning the image into a root filesystem.
16 +
17 +ARG BASE=ubuntu:24.04
18 +
19 +# tauri-driver is cargo-only; build it here and copy the binary so the guest has no Rust.
20 +FROM rust:1-bookworm AS tauri-driver
21 +RUN cargo install tauri-driver --locked --root /out
22 +
23 +FROM ${BASE} AS actions-runner
24 +
25 +LABEL org.opencontainers.image.source=https://github.com/getsigit/sigit \
26 + org.opencontainers.image.description="Guest image for GitHub Actions jobs in a microVM"
27 +
28 +ARG RUNNER_VERSION=2.338.2
29 +# sha256 of actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz, from the release notes.
30 +ARG RUNNER_SHA256=baebf204307bac6193480852732f0aba2ae97b6c5597c244f4d5558390147c40
31 +ARG NODE_VERSION=24.12.0
32 +ARG PNPM_VERSION=10
33 +
34 +ENV DEBIAN_FRONTEND=noninteractive
35 +
36 +# Init, the packages the guest agent and the host tooling need (python3, git, CA certificates,
37 +# zlib), and the usual build tools.
38 +RUN apt-get update \
39 + && apt-get install -y --no-install-recommends \
40 + systemd systemd-sysv dbus iproute2 \
41 + ca-certificates zlib1g python3 git curl jq unzip xz-utils zstd \
42 + build-essential pkg-config libssl-dev openssh-client sudo \
43 + && rm -rf /var/lib/apt/lists/*
44 +
45 +# The GitHub CLI, from its apt repository.
46 +RUN install -d -m 0755 /etc/apt/keyrings \
47 + && curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
48 + -o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
49 + && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
50 + > /etc/apt/sources.list.d/github-cli.list \
51 + && apt-get update \
52 + && apt-get install -y --no-install-recommends gh \
53 + && rm -rf /var/lib/apt/lists/*
54 +
55 +# Node from nodejs.org, then pnpm.
56 +RUN curl -fsSL "https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz" \
57 + | tar -xJ -C /usr/local --strip-components=1 \
58 + && npm install -g "pnpm@${PNPM_VERSION}"
59 +
60 +# The Actions runner, checked against the published digest, with its system dependencies. The
61 +# runner refuses to start as root unless RUNNER_ALLOW_RUNASROOT is set; the host sets it, since
62 +# the guest has a single user and the VM lives for one job.
63 +RUN set -eu; \
64 + mkdir -p /opt/actions-runner; \
65 + curl -fsSL --retry 2 -o /tmp/runner.tar.gz \
66 + "https://github.com/actions/runner/releases/download/v${RUNNER_VERSION}/actions-runner-linux-x64-${RUNNER_VERSION}.tar.gz"; \
67 + echo "${RUNNER_SHA256} /tmp/runner.tar.gz" | sha256sum -c -; \
68 + tar -xzf /tmp/runner.tar.gz -C /opt/actions-runner; \
69 + rm /tmp/runner.tar.gz; \
70 + /opt/actions-runner/bin/installdependencies.sh; \
71 + rm -rf /var/lib/apt/lists/*; \
72 + test -x /opt/actions-runner/run.sh
73 +
74 +WORKDIR /workspace
75 +
76 +FROM actions-runner AS actions-runner-qa
77 +
78 +LABEL org.opencontainers.image.description="Guest image for GitHub Actions jobs in a microVM, with browsers"
79 +
80 +# Must match the consumer's @playwright/test. A job's `playwright install` is a no-op when it
81 +# does and a browser download when it doesn't, and visual baselines are only stable against the
82 +# browsers they were recorded with.
83 +ARG PLAYWRIGHT_VERSION=1.64.1
84 +
85 +ENV PLAYWRIGHT_BROWSERS_PATH=/ms-playwright
86 +
87 +# Tauri runtime on Linux (AppImage under tauri-driver) and a virtual display.
88 +RUN apt-get update \
89 + && apt-get install -y --no-install-recommends \
90 + xvfb libwebkit2gtk-4.1-0 webkit2gtk-driver bubblewrap \
91 + && rm -rf /var/lib/apt/lists/*
92 +
93 +# Browsers and their system libraries and fonts, at the pinned Playwright version.
94 +RUN npx -y "playwright@${PLAYWRIGHT_VERSION}" install --with-deps chromium webkit \
95 + && chmod -R a+rX /ms-playwright \
96 + && rm -rf /var/lib/apt/lists/*
97 +
98 +COPY --from=tauri-driver /out/bin/tauri-driver /usr/local/bin/tauri-driver
packaging/actions-runner/README.md new
+34
@@ -0,0 +1,34 @@
1 +# actions-runner
2 +
3 +Guest image for GitHub Actions jobs that run in a microVM. See the Dockerfile header for the
4 +two targets.
5 +
6 +## What the image holds
7 +
8 +- The Actions runner at `/opt/actions-runner`, pinned by version and sha256. Bump
9 + `RUNNER_VERSION` and `RUNNER_SHA256` together; the digest is in the release notes.
10 +- git, node, pnpm, jq, gh, python3 and a C toolchain.
11 +- systemd as init. The microVM boots it, and the guest agent that talks to the host is installed
12 + as a unit when the root filesystem is baked.
13 +
14 +It holds no credentials. The runner starts with `run.sh --jitconfig`, using a one-job
15 +configuration that arrives with the job, takes that job and exits.
16 +
17 +## Making a root filesystem
18 +
19 +```sh
20 +docker build --target actions-runner -t actions-runner packaging/actions-runner
21 +cid=$(docker create actions-runner)
22 +docker export "$cid" | (mkdir rootfs && tar -x -C rootfs)
23 +docker rm "$cid"
24 +truncate -s 8G actions-runner.ext4
25 +mkfs.ext4 -F -d rootfs actions-runner.ext4
26 +```
27 +
28 +The host-side tooling then installs the guest agent into the image and snapshots it. That step
29 +lives with the runner, not here.
30 +
31 +## Egress
32 +
33 +Inside the microVM the runner reaches the network only through the host's allowlisting proxy,
34 +through `HTTPS_PROXY`. A workflow that needs a host beyond the allowlist fails at connect time.