From f3dd8f870195ce52d32d3ed79a2a4acb7d5be3c4 Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Wed, 23 Sep 2026 22:36:34 +0800 Subject: [PATCH] ci: probe Docker through the runner socket --- .gitea/scripts/runner-probe.mjs | 141 ++++++++++++++++++++++++++++++ .gitea/workflows/runner-probe.yml | 58 +----------- 2 files changed, 144 insertions(+), 55 deletions(-) create mode 100644 .gitea/scripts/runner-probe.mjs diff --git a/.gitea/scripts/runner-probe.mjs b/.gitea/scripts/runner-probe.mjs new file mode 100644 index 0000000..60c7910 --- /dev/null +++ b/.gitea/scripts/runner-probe.mjs @@ -0,0 +1,141 @@ +import http from "node:http"; + +const socketPath = "/var/run/docker.sock"; +const target = "uiowa-algebra.pdf"; + +function request(method, path, body) { + return new Promise((resolve, reject) => { + const payload = body === undefined ? undefined : JSON.stringify(body); + const req = http.request( + { + socketPath, + path, + method, + headers: payload + ? { "Content-Type": "application/json", "Content-Length": Buffer.byteLength(payload) } + : {}, + }, + (res) => { + const chunks = []; + res.on("data", (chunk) => chunks.push(chunk)); + res.on("end", () => resolve({ status: res.statusCode, data: Buffer.concat(chunks) })); + }, + ); + req.on("error", reject); + if (payload) req.write(payload); + req.end(); + }); +} + +function json(response) { + return response.data.length ? JSON.parse(response.data.toString("utf8")) : {}; +} + +function expect(response, allowed, operation) { + if (!allowed.includes(response.status)) { + throw new Error(`${operation} failed (${response.status}): ${response.data.toString("utf8")}`); + } + return response; +} + +async function runInContainer(container, command) { + const created = expect( + await request("POST", `/containers/${encodeURIComponent(container)}/exec`, { + AttachStdout: true, + AttachStderr: true, + Tty: true, + Cmd: ["sh", "-c", command], + }), + [201], + `create exec in ${container}`, + ); + const execId = json(created).Id; + const output = expect( + await request("POST", `/exec/${execId}/start`, { Detach: false, Tty: true }), + [200], + `start exec in ${container}`, + ); + process.stdout.write(output.data.toString("utf8")); + const inspected = expect(await request("GET", `/exec/${execId}/json`), [200], "inspect exec"); + return json(inspected).ExitCode; +} + +async function runVolumeProbe() { + const name = `lean-bench-pdf-probe-${process.env.GITHUB_RUN_ID || process.pid}`; + const created = expect( + await request("POST", `/containers/create?name=${encodeURIComponent(name)}`, { + Image: "node:16-bullseye", + Cmd: ["sh", "-c", `test -f /books/${target} && ls -lh /books/${target} && sha256sum /books/${target}`], + Tty: true, + HostConfig: { Binds: ["lean-bench-books:/books:ro"] }, + }), + [201], + "create volume probe", + ); + const id = json(created).Id; + try { + expect(await request("POST", `/containers/${id}/start`), [204], "start volume probe"); + const waited = expect( + await request("POST", `/containers/${id}/wait?condition=not-running`), + [200], + "wait for volume probe", + ); + const logs = expect( + await request("GET", `/containers/${id}/logs?stdout=true&stderr=true`), + [200], + "read volume probe logs", + ); + process.stdout.write(logs.data.toString("utf8")); + return json(waited).StatusCode; + } finally { + await request("DELETE", `/containers/${id}?force=true`); + } +} + +async function main() { + const version = expect(await request("GET", "/version"), [200], "read Docker version"); + const versionInfo = json(version); + console.log(`docker=${versionInfo.Version} api=${versionInfo.ApiVersion}`); + + const containers = json( + expect(await request("GET", "/containers/json?all=true"), [200], "list containers"), + ); + for (const container of containers) { + console.log(`container=${container.Names.join(",")} state=${container.State} image=${container.Image}`); + } + + const volumes = json(expect(await request("GET", "/volumes"), [200], "list volumes")); + const volumeNames = (volumes.Volumes || []).map((volume) => volume.Name); + for (const name of volumeNames) console.log(`volume=${name}`); + + let found = false; + const service = containers.find((container) => container.Names.includes("/lean-bench")); + if (service?.State === "running") { + const exitCode = await runInContainer( + "lean-bench", + `test -f /books/${target} && ls -lh /books/${target} && sha256sum /books/${target}`, + ); + if (exitCode === 0) { + found = true; + console.log(`FOUND container:lean-bench:/books/${target}`); + } + } + + if (volumeNames.includes("lean-bench-books")) { + const exitCode = await runVolumeProbe(); + if (exitCode === 0) { + found = true; + console.log(`FOUND volume:lean-bench-books:${target}`); + } + } + + if (!found) { + console.error(`NOT_FOUND: ${target} in lean-bench container or lean-bench-books volume`); + process.exitCode = 2; + } +} + +main().catch((error) => { + console.error(error.stack || error); + process.exitCode = 1; +}); diff --git a/.gitea/workflows/runner-probe.yml b/.gitea/workflows/runner-probe.yml index b5e8313..b6804e9 100644 --- a/.gitea/workflows/runner-probe.yml +++ b/.gitea/workflows/runner-probe.yml @@ -5,6 +5,7 @@ on: branches: [main] paths: - ".gitea/workflows/runner-probe.yml" + - ".gitea/scripts/runner-probe.mjs" workflow_dispatch: jobs: @@ -23,58 +24,5 @@ jobs: id df -h / - - name: Check Docker and the persistent service - run: | - set -eu - if ! command -v docker >/dev/null 2>&1; then - apt-get update -qq - DEBIAN_FRONTEND=noninteractive apt-get install -y -qq docker.io - fi - docker version --format 'client {{.Client.Version}} / server {{.Server.Version}}' - docker ps -a --format 'container={{.Names}} status={{.Status}} image={{.Image}}' - docker volume ls --format 'volume={{.Name}}' - - - name: Find the uploaded Iowa algebra PDF - run: | - set -eu - target=uiowa-algebra.pdf - found=0 - - for root in "$GITHUB_WORKSPACE" /books /data /srv /opt /workspace; do - if [ -d "$root" ]; then - while IFS= read -r path; do - [ -n "$path" ] || continue - found=1 - echo "FOUND host:$path" - stat -c 'size=%s bytes modified=%y' "$path" 2>/dev/null || ls -lh "$path" - sha256sum "$path" 2>/dev/null || true - done </dev/null || true) - EOF - fi - done - - if command -v docker >/dev/null 2>&1; then - if docker ps -a --format '{{.Names}}' | grep -qx lean-bench; then - if docker exec lean-bench test -f "/books/$target"; then - found=1 - echo "FOUND container:lean-bench:/books/$target" - docker exec lean-bench sh -c "ls -lh '/books/$target'; sha256sum '/books/$target'" - fi - fi - - if docker volume inspect lean-bench-books >/dev/null 2>&1; then - if docker run --rm -v lean-bench-books:/books:ro node:16-bullseye \ - test -f "/books/$target"; then - found=1 - echo "FOUND volume:lean-bench-books:$target" - docker run --rm -v lean-bench-books:/books:ro node:16-bullseye \ - sh -c "ls -lh '/books/$target'; sha256sum '/books/$target'" - fi - fi - fi - - if [ "$found" -eq 0 ]; then - echo "NOT_FOUND: $target" - exit 2 - fi + - name: Check Docker and find the uploaded Iowa algebra PDF + run: node .gitea/scripts/runner-probe.mjs