Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.
| A deploy makes the runner's image in the registry, without Docker: it reads the base's manifest and config from Cloudflare's registry, uploads one gzipped layer holding the runner binary at /usr/local/bin/g1t-runner with mode 0755, and writes a config (the entrypoint g1t-runner, the base's command cleared, as the Dockerfile's ENTRYPOINT does) and a manifest that name the base's layers and that one, under the runner's tag, for linux/amd64 (scripts/deploy/registry.mjs, the registry's own upload calls with the credentials Wrangler gives). Deploy #256 ran out of disk because docker build pulled the 4.5 GB base into the job's own Docker Engine, compressed and unpacked, on a 20 GB sandbox whose own filesystem is that same base; now nothing of the base is downloaded and the job's Docker Engine is never started. Docker is asked for only when it is needed: a dry run (build, or image --no-push) still builds services/runner/Dockerfile with it, and so does a deploy whose registry step fails, logging df -h and docker system df before and after. The image job removes npm's downloads, which it only restores, once its install has read them, and logs the disk before and after the deploy. Tests cover the layer's tar, the config and manifest, the calls against a registry kept in memory (the base's layers are never fetched), the token flow, and that the Dockerfile says the same path, mode and entrypoint; the image was also checked against a local registry, where it runs and has the same config as a docker build of the Dockerfile. | 1 | // The runner's image made in the registry (registry.mjs): its layer, |
| 2 | // config and manifest, and the calls it makes, against a registry kept in | |
| 3 | // memory. | |
| 4 | import assert from "node:assert/strict"; | |
| 5 | import { readFileSync } from "node:fs"; | |
| 6 | import { join } from "node:path"; | |
| 7 | import { test } from "node:test"; | |
| 8 | import { gunzipSync } from "node:zlib"; | |
| 9 | ||
| 10 | import { | |
| 11 | DOCKER_MANIFEST, | |
| 12 | OCI_MANIFEST, | |
| 13 | RUNNER_BINARY_MODE, | |
| 14 | RUNNER_BINARY_PATH, | |
| 15 | RUNNER_ENTRYPOINT, | |
| 16 | appendRunnerImage, | |
| 17 | bearerChallenge, | |
| 18 | parseRef, | |
| 19 | runnerConfig, | |
| 20 | runnerLayer, | |
| 21 | runnerManifest, | |
| 22 | sha256, | |
| 23 | tarOf, | |
| 24 | } from "./registry.mjs"; | |
| 25 | import { ROOT } from "./stack.mjs"; | |
| 26 | ||
| 27 | /** A tar's entries: name, mode, size, owner, type and bytes, checksums checked. */ | |
| 28 | function untar(tar) { | |
| 29 | const entries = []; | |
| 30 | for (let at = 0; at + 512 <= tar.length; ) { | |
| 31 | const header = tar.subarray(at, at + 512); | |
| 32 | if (header.every((b) => b === 0)) break; | |
| 33 | const field = (start, length) => header.subarray(start, start + length).toString("ascii").replace(/\0.*$/s, "").trim(); | |
| 34 | const summed = [...header].reduce((sum, byte, i) => sum + (i >= 148 && i < 156 ? 32 : byte), 0); | |
| 35 | assert.equal(Number.parseInt(field(148, 8), 8), summed, "the header's checksum"); | |
| 36 | assert.equal(field(257, 6), "ustar"); | |
| 37 | const size = Number.parseInt(field(124, 12), 8); | |
| 38 | entries.push({ | |
| 39 | name: field(0, 100), | |
| 40 | mode: Number.parseInt(field(100, 8), 8), | |
| 41 | uid: Number.parseInt(field(108, 8), 8), | |
| 42 | gid: Number.parseInt(field(116, 8), 8), | |
| 43 | mtime: Number.parseInt(field(136, 12), 8), | |
| 44 | type: field(156, 1), | |
| 45 | data: tar.subarray(at + 512, at + 512 + size), | |
| 46 | }); | |
| 47 | at += 512 + Math.ceil(size / 512) * 512; | |
| 48 | } | |
| 49 | return entries; | |
| 50 | } | |
| 51 | ||
| 52 | test("the runner's layer is the binary alone, at its path and mode, owned by root", () => { | |
| 53 | const binary = Buffer.from("\x7fELF a runner, of a length that is not a whole block"); | |
| 54 | const layer = runnerLayer(binary, { mtime: 1_760_000_000 }); | |
| 55 | const tar = gunzipSync(layer.bytes); | |
| 56 | assert.equal(tar.length % 512, 0); | |
| 57 | assert.equal(layer.digest, sha256(layer.bytes)); | |
| 58 | assert.equal(layer.size, layer.bytes.length); | |
| 59 | assert.equal(layer.diffId, sha256(tar)); | |
| 60 | const entries = untar(tar); | |
| 61 | assert.equal(entries.length, 1); | |
| 62 | const [entry] = entries; | |
| 63 | assert.deepEqual( | |
| 64 | { name: entry.name, mode: entry.mode, uid: entry.uid, gid: entry.gid, mtime: entry.mtime, type: entry.type }, | |
| 65 | { name: "usr/local/bin/g1t-runner", mode: 0o755, uid: 0, gid: 0, mtime: 1_760_000_000, type: "0" }, | |
| 66 | ); | |
| 67 | assert.ok(entry.data.equals(binary)); | |
| 68 | // The same bytes make the same layer. | |
| 69 | assert.equal(runnerLayer(binary, { mtime: 1_760_000_000 }).digest, layer.digest); | |
| 70 | // An exact number of blocks, and an empty file. | |
| 71 | assert.equal(untar(tarOf("a", Buffer.alloc(512, 1)))[0].data.length, 512); | |
| 72 | assert.equal(untar(tarOf("a", Buffer.alloc(0)))[0].data.length, 0); | |
| 73 | }); | |
| 74 | ||
| 75 | test("the layer and entrypoint are what services/runner/Dockerfile builds", () => { | |
| 76 | const dockerfile = readFileSync(join(ROOT, "services/runner/Dockerfile"), "utf8"); | |
| 77 | const copy = /^COPY --chmod=(\d+) g1t-runner (\S+)$/m.exec(dockerfile); | |
| 78 | assert.ok(copy, "the Dockerfile copies the binary in"); | |
| 79 | assert.equal(Number.parseInt(copy[1], 8), RUNNER_BINARY_MODE); | |
| 80 | assert.equal(copy[2], `/${RUNNER_BINARY_PATH}`); | |
| 81 | const entrypoint = /^ENTRYPOINT (.+)$/m.exec(dockerfile); | |
| 82 | assert.deepEqual(JSON.parse(entrypoint[1]), RUNNER_ENTRYPOINT); | |
| 83 | // Nothing else that would change the image. | |
| 84 | const instructions = dockerfile.split(/\r?\n/).filter((line) => line.trim() && !line.startsWith("#")).map((line) => line.split(" ")[0]); | |
| 85 | assert.deepEqual(instructions, ["ARG", "FROM", "COPY", "ENTRYPOINT"]); | |
| 86 | }); | |
| 87 | ||
| 88 | const BASE_CONFIG = { | |
| 89 | architecture: "amd64", | |
| 90 | os: "linux", | |
| 91 | created: "2026-10-08T19:50:00Z", | |
| 92 | config: { Env: ["PATH=/usr/local/bin:/usr/bin"], Cmd: ["bash"], WorkingDir: "/home/runner", User: "root" }, | |
| 93 | rootfs: { type: "layers", diff_ids: ["sha256:" + "1".repeat(64), "sha256:" + "2".repeat(64)] }, | |
| 94 | history: [{ created_by: "FROM debian" }, { created_by: "RUN apt-get install" }], | |
| 95 | }; | |
| 96 | ||
| 97 | test("the config is the base's, with the layer, the entrypoint and no inherited command", () => { | |
| 98 | const before = structuredClone(BASE_CONFIG); | |
| 99 | const diffId = "sha256:" + "3".repeat(64); | |
| 100 | const config = runnerConfig(BASE_CONFIG, { diffId, created: "2026-10-13T00:00:00.000Z" }); | |
| 101 | assert.deepEqual(BASE_CONFIG, before, "the base's config is left as it was"); | |
| 102 | assert.deepEqual(config.config.Entrypoint, ["g1t-runner"]); | |
| 103 | assert.equal(config.config.Cmd, undefined); | |
| 104 | assert.deepEqual(config.config.Env, BASE_CONFIG.config.Env); | |
| 105 | assert.equal(config.config.WorkingDir, "/home/runner"); | |
| 106 | assert.deepEqual(config.rootfs.diff_ids, [...BASE_CONFIG.rootfs.diff_ids, diffId]); | |
| 107 | assert.equal(config.created, "2026-10-13T00:00:00.000Z"); | |
| 108 | // History: one entry per layer, plus one that adds none. | |
| 109 | assert.equal(config.history.filter((h) => !h.empty_layer).length - BASE_CONFIG.history.length, 1); | |
| 110 | assert.equal(config.history.at(-1).empty_layer, true); | |
| 111 | assert.throws(() => runnerConfig({ ...BASE_CONFIG, architecture: "arm64" }, { diffId, created: "x" }), /linux\/amd64/); | |
| 112 | }); | |
| 113 | ||
| 114 | test("the manifest names the base's layers, then the binary's, in the base's format", () => { | |
| 115 | const layers = [ | |
| 116 | { mediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", digest: "sha256:" + "a".repeat(64), size: 10 }, | |
| 117 | { mediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", digest: "sha256:" + "b".repeat(64), size: 20 }, | |
| 118 | ]; | |
| 119 | const config = { digest: "sha256:" + "c".repeat(64), size: 5 }; | |
| 120 | const layer = { digest: "sha256:" + "d".repeat(64), size: 7 }; | |
| 121 | const docker = runnerManifest( | |
| 122 | { schemaVersion: 2, mediaType: DOCKER_MANIFEST, config: { mediaType: "application/vnd.docker.container.image.v1+json", digest: "x", size: 1 }, layers }, | |
| 123 | { config, layer }, | |
| 124 | ); | |
| 125 | assert.deepEqual(docker, { | |
| 126 | schemaVersion: 2, | |
| 127 | mediaType: DOCKER_MANIFEST, | |
| 128 | config: { mediaType: "application/vnd.docker.container.image.v1+json", ...config }, | |
| 129 | layers: [...layers, { mediaType: "application/vnd.docker.image.rootfs.diff.tar.gzip", ...layer }], | |
| 130 | }); | |
| 131 | // OCI, with its type left unsaid. | |
| 132 | const oci = runnerManifest( | |
| 133 | { schemaVersion: 2, config: { mediaType: "application/vnd.oci.image.config.v1+json", digest: "x", size: 1 }, layers: [], annotations: { a: "b" } }, | |
| 134 | { config, layer }, | |
| 135 | ); | |
| 136 | assert.equal(oci.mediaType, OCI_MANIFEST); | |
| 137 | assert.deepEqual(oci.layers, [{ mediaType: "application/vnd.oci.image.layer.v1.tar+gzip", ...layer }]); | |
| 138 | assert.equal(oci.annotations, undefined); | |
| 139 | }); | |
| 140 | ||
| 141 | test("image references and bearer challenges are read", () => { | |
| 142 | const digest = "sha256:" + "e".repeat(64); | |
| 143 | assert.deepEqual(parseRef("registry.cloudflare.com/acct/g1t-runner:abc"), { host: "registry.cloudflare.com", name: "acct/g1t-runner", tag: "abc", digest: null }); | |
| 144 | assert.deepEqual(parseRef(`localhost:5000/g1t-runner:base-1@${digest}`), { host: "localhost:5000", name: "g1t-runner", tag: "base-1", digest }); | |
| 145 | assert.deepEqual(parseRef(`r.example/x@${digest}`), { host: "r.example", name: "x", tag: null, digest }); | |
| 146 | assert.throws(() => parseRef("r.example/x")); | |
| 147 | assert.deepEqual(bearerChallenge('Bearer realm="https://auth.example/token",service="registry",scope="repository:x:pull"'), { | |
| 148 | realm: "https://auth.example/token", | |
| 149 | service: "registry", | |
| 150 | scope: "repository:x:pull", | |
| 151 | }); | |
| 152 | assert.equal(bearerChallenge('Basic realm="x"'), null); | |
| 153 | assert.equal(bearerChallenge(null), null); | |
| 154 | }); | |
| 155 | ||
| 156 | /** A registry in memory: one repository, Basic auth, uploads by POST, PATCH and PUT. */ | |
| 157 | function fakeRegistry({ name, auth = "Basic " + Buffer.from("u:p").toString("base64") }) { | |
| 158 | const blobs = new Map(); | |
| 159 | const manifests = new Map(); | |
| 160 | const uploads = new Map(); | |
| 161 | const calls = []; | |
| 162 | let next = 0; | |
| 163 | const reply = (status, { headers = {}, body = null } = {}) => ({ | |
| 164 | status, | |
| 165 | ok: status >= 200 && status < 300, | |
| 166 | headers: new Headers(headers), | |
| 167 | arrayBuffer: async () => (body ? body.buffer.slice(body.byteOffset, body.byteOffset + body.byteLength) : new ArrayBuffer(0)), | |
| 168 | text: async () => (body ? body.toString("utf8") : ""), | |
| 169 | json: async () => JSON.parse(body.toString("utf8")), | |
| 170 | }); | |
| 171 | const fetchImpl = async (url, init) => { | |
| 172 | const { pathname, searchParams } = new URL(url); | |
| 173 | calls.push(`${init.method} ${pathname}`); | |
| 174 | if (init.headers.authorization !== auth) return reply(401); | |
| 175 | const prefix = `/v2/${name}/`; | |
| 176 | if (!pathname.startsWith(prefix)) return reply(404); | |
| 177 | const path = pathname.slice(prefix.length); | |
| 178 | let match; | |
| 179 | if ((match = /^manifests\/(.+)$/.exec(path))) { | |
| 180 | if (init.method === "PUT") { | |
| 181 | const bytes = Buffer.from(init.body); | |
| 182 | const digest = sha256(bytes); | |
| 183 | for (const ref of [...JSON.parse(bytes).layers, JSON.parse(bytes).config]) { | |
| 184 | if (!blobs.has(ref.digest)) return reply(400, { body: Buffer.from('{"errors":[{"code":"MANIFEST_BLOB_UNKNOWN"}]}') }); | |
| 185 | } | |
| 186 | manifests.set(match[1], { bytes, type: init.headers["content-type"] }); | |
| 187 | manifests.set(digest, { bytes, type: init.headers["content-type"] }); | |
| 188 | return reply(201, { headers: { "docker-content-digest": digest } }); | |
| 189 | } | |
| 190 | const found = manifests.get(match[1]); | |
| 191 | return found ? reply(200, { headers: { "content-type": found.type }, body: found.bytes }) : reply(404); | |
| 192 | } | |
| 193 | if ((match = /^blobs\/(sha256:[0-9a-f]{64})$/.exec(path))) { | |
| 194 | const found = blobs.get(match[1]); | |
| 195 | if (!found) return reply(404); | |
| 196 | return init.method === "HEAD" ? reply(200) : reply(200, { body: found }); | |
| 197 | } | |
| 198 | if (path === "blobs/uploads/" && init.method === "POST") { | |
| 199 | const id = `u${next++}`; | |
| 200 | uploads.set(id, Buffer.alloc(0)); | |
| 201 | return reply(202, { headers: { location: `${prefix}blobs/uploads/${id}` } }); | |
| 202 | } | |
| 203 | if ((match = /^blobs\/uploads\/(u\d+)$/.exec(path))) { | |
| 204 | const id = match[1]; | |
| 205 | if (init.method === "PATCH") { | |
| 206 | uploads.set(id, Buffer.concat([uploads.get(id), Buffer.from(init.body)])); | |
| 207 | return reply(202, { headers: { location: `${prefix}blobs/uploads/${id}?state=1` } }); | |
| 208 | } | |
| 209 | if (init.method === "PUT") { | |
| 210 | const bytes = uploads.get(id); | |
| 211 | if (sha256(bytes) !== searchParams.get("digest")) return reply(400); | |
| 212 | blobs.set(searchParams.get("digest"), bytes); | |
| 213 | return reply(201); | |
| 214 | } | |
| 215 | } | |
| 216 | return reply(405); | |
| 217 | }; | |
| 218 | return { blobs, manifests, calls, fetchImpl }; | |
| 219 | } | |
| 220 | ||
| 221 | test("the runner's image is made in the registry from the base's manifest and one new layer, pulling none of the base", async () => { | |
| 222 | const registry = fakeRegistry({ name: "acct/g1t-runner" }); | |
| 223 | // The base, as `docker push` left it. | |
| 224 | const layerBlobs = [Buffer.from("base layer one"), Buffer.from("base layer two")]; | |
| 225 | for (const blob of layerBlobs) registry.blobs.set(sha256(blob), blob); | |
| 226 | const configBytes = Buffer.from(JSON.stringify(BASE_CONFIG)); | |
| 227 | registry.blobs.set(sha256(configBytes), configBytes); | |
| 228 | const baseManifest = Buffer.from( | |
| 229 | JSON.stringify({ | |
| 230 | schemaVersion: 2, | |
| 231 | mediaType: OCI_MANIFEST, | |
| 232 | config: { mediaType: "application/vnd.oci.image.config.v1+json", digest: sha256(configBytes), size: configBytes.length }, | |
| 233 | layers: layerBlobs.map((blob) => ({ mediaType: "application/vnd.oci.image.layer.v1.tar+gzip", digest: sha256(blob), size: blob.length })), | |
| 234 | }), | |
| 235 | ); | |
| 236 | registry.manifests.set(sha256(baseManifest), { bytes: baseManifest, type: OCI_MANIFEST }); | |
| 237 | const base = `registry.cloudflare.com/acct/g1t-runner:base-20261008-a314499af03b@${sha256(baseManifest)}`; | |
| 238 | const ref = "registry.cloudflare.com/acct/g1t-runner:0123456789abcdef"; | |
| 239 | const binary = Buffer.from("the runner binary"); | |
| 240 | const credentials = async () => ({ username: "u", password: "p" }); | |
| 241 | ||
| 242 | const digest = await appendRunnerImage({ ref, base, binary, credentials, fetchImpl: registry.fetchImpl, created: "2026-10-13T00:00:00.000Z" }); | |
| 243 | const made = registry.manifests.get("0123456789abcdef"); | |
| 244 | assert.equal(sha256(made.bytes), digest); | |
| 245 | assert.equal(made.type, OCI_MANIFEST); | |
| 246 | const manifest = JSON.parse(made.bytes); | |
| 247 | assert.deepEqual(manifest.layers.slice(0, 2).map((l) => l.digest), layerBlobs.map(sha256)); | |
| 248 | assert.equal(manifest.layers.length, 3); | |
| 249 | const config = JSON.parse(registry.blobs.get(manifest.config.digest)); | |
| 250 | assert.deepEqual(config.config.Entrypoint, ["g1t-runner"]); | |
| 251 | assert.equal(config.rootfs.diff_ids.at(-1), sha256(gunzipSync(registry.blobs.get(manifest.layers[2].digest)))); | |
| 252 | assert.ok(untar(gunzipSync(registry.blobs.get(manifest.layers[2].digest)))[0].data.equals(binary)); | |
| 253 | // The base's layers were asked about, never downloaded. | |
| 254 | for (const blob of layerBlobs) assert.ok(!registry.calls.includes(`GET /v2/acct/g1t-runner/blobs/${sha256(blob)}`)); | |
| 255 | ||
| 256 | // Made again: the layer is already there, so only the config and manifest are written. | |
| 257 | const before = registry.calls.length; | |
| 258 | await appendRunnerImage({ ref, base, binary, credentials, fetchImpl: registry.fetchImpl, created: "2026-10-13T00:00:00.000Z" }); | |
| 259 | assert.ok(!registry.calls.slice(before).some((call) => call.startsWith("PATCH"))); | |
| 260 | }); | |
| 261 | ||
| 262 | test("a base that is not where it should be, or not what base.json says, stops it", async () => { | |
| 263 | const registry = fakeRegistry({ name: "acct/g1t-runner" }); | |
| 264 | const credentials = async () => ({ username: "u", password: "p" }); | |
| 265 | const digest = "sha256:" + "f".repeat(64); | |
| 266 | const options = { binary: Buffer.from("x"), credentials, fetchImpl: registry.fetchImpl, attempts: 1 }; | |
| 267 | await assert.rejects( | |
| 268 | appendRunnerImage({ ...options, ref: "registry.cloudflare.com/acct/g1t-runner:t", base: `registry.cloudflare.com/acct/other:b@${digest}` }), | |
| 269 | /not in the runner's repository/, | |
| 270 | ); | |
| 271 | await assert.rejects(appendRunnerImage({ ...options, ref: "registry.cloudflare.com/acct/g1t-runner:t", base: `registry.cloudflare.com/acct/g1t-runner:b@${digest}` }), /404/); | |
| 272 | // Wrong credentials: the registry's answer is the error. | |
| 273 | await assert.rejects( | |
| 274 | appendRunnerImage({ ...options, credentials: async () => ({ username: "u", password: "wrong" }), ref: "registry.cloudflare.com/acct/g1t-runner:t", base: `registry.cloudflare.com/acct/g1t-runner:b@${digest}` }), | |
| 275 | /401/, | |
| 276 | ); | |
| 277 | }); | |
| 278 | ||
| 279 | test("a registry that asks for a bearer token is given the one its realm trades the credentials for", async () => { | |
| 280 | const registry = fakeRegistry({ name: "acct/r", auth: "Bearer tok" }); | |
| 281 | const inner = registry.fetchImpl; | |
| 282 | const asked = []; | |
| 283 | const fetchImpl = async (url, init) => { | |
| 284 | if (url.startsWith("https://auth.example/")) { | |
| 285 | asked.push([url, init.headers.authorization]); | |
| 286 | return { ok: true, status: 200, json: async () => ({ token: "tok" }) }; | |
| 287 | } | |
| 288 | const answer = await inner(url, init); | |
| 289 | if (answer.status === 401) answer.headers.set("www-authenticate", 'Bearer realm="https://auth.example/token",service="r.example"'); | |
| 290 | return answer; | |
| 291 | }; | |
| 292 | const config = Buffer.from(JSON.stringify(BASE_CONFIG)); | |
| 293 | registry.blobs.set(sha256(config), config); | |
| 294 | const manifest = Buffer.from( | |
| 295 | JSON.stringify({ schemaVersion: 2, mediaType: DOCKER_MANIFEST, config: { mediaType: "application/vnd.docker.container.image.v1+json", digest: sha256(config), size: config.length }, layers: [] }), | |
| 296 | ); | |
| 297 | registry.manifests.set("base", { bytes: manifest, type: DOCKER_MANIFEST }); | |
| 298 | await appendRunnerImage({ | |
| 299 | ref: "r.example/acct/r:new", | |
| 300 | base: "r.example/acct/r:base", | |
| 301 | binary: Buffer.from("x"), | |
| 302 | credentials: async () => ({ username: "u", password: "p" }), | |
| 303 | fetchImpl, | |
| 304 | }); | |
| 305 | assert.ok(registry.manifests.has("new")); | |
| 306 | assert.equal(asked.length, 1, "one token, kept"); | |
| 307 | assert.equal(asked[0][0], "https://auth.example/token?service=r.example&scope=repository%3Aacct%2Fr%3Apull%2Cpush"); | |
| 308 | assert.equal(asked[0][1], `Basic ${Buffer.from("u:p").toString("base64")}`); | |
| 309 | }); |