Diffinery

codician/diffinerypublic

Open-source, agent-first GitHub and GitHub Actions alternative that runs on your own Cloudflare account: git hosting, pull requests, code review and drop-in GitHub Actions YAML on Workers, Durable Objects, R2 and Containers.

End-to-end: Docker on a hosted runner with the change's own docker wrapper, daemon config and agent

A job that stops the hosted runner's Docker daemon, installs this change's image/docker,
image/daemon.json and agent (its registry relay), starts the daemon with three docker commands at
once, and runs the runner's Docker tests. A daemon start lock leak like the one #225 fixed fails
here, before merge, instead of in the Live test after a deploy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SXYSyuyJ1XDRKcW2aTR2Zj
Claude authored signed1 parent 0c1a478commit 542c29337637f5aad640b491987e8fccf0f2f542Browse files
1 file changed+49−0
modified.github/workflows/e2e.yml+49−0View file
@@ -82,3 +82,52 @@
8282 esac
8383 echo "$files"
8484 node --test --test-concurrency=1 "${opts[@]}" $files
85+
86+ # Docker as the hosted runner image runs it, with this change's docker wrapper, daemon config
87+ # and agent (its registry relay): this job runs on a hosted runner, so the real dockerd is there.
88+ # A daemon an earlier step started is stopped first, so the first uses below start it afresh.
89+ runner-docker:
90+ name: End-to-end (runner Docker)
91+ runs-on: ubuntu-latest
92+ timeout-minutes: 20
93+ steps:
94+ - uses: actions/checkout@v4
95+ - uses: pnpm/action-setup@b906affcce14559ad1aafd4ab0e942779e9f58b1 # v4.3.0
96+ - uses: actions/setup-node@v4
97+ with:
98+ node-version: 22
99+ cache: pnpm
100+ - run: pnpm install --frozen-lockfile
101+ - run: pnpm --filter @diffinery/runner build
102+ - name: Use this change's Docker wrapper, daemon config and agent
103+ run: |
104+ set -euo pipefail
105+ as_root() { if [ "$(id -u)" = 0 ]; then "$@"; else sudo "$@"; fi; }
106+ if [ ! -f /opt/diffinery/agent.mjs ] || ! grep -q "registry relay" /usr/local/bin/docker 2>/dev/null; then
107+ echo "::error::this job needs a Diffinery hosted runner (its docker wrapper and agent)"
108+ exit 1
109+ fi
110+ as_root pkill -x dockerd || true
111+ as_root pkill -f -- "--registry-relay" || true
112+ for _ in $(seq 1 60); do pgrep -x dockerd >/dev/null || break; sleep 0.5; done
113+ as_root rm -f /run/diffinery-dockerd.ready
114+ as_root cp --remove-destination containers/runner/image/docker /usr/local/bin/docker
115+ as_root cp --remove-destination containers/runner/image/daemon.json /etc/docker/daemon.json
116+ as_root cp --remove-destination containers/runner/dist/agent.mjs /opt/diffinery/agent.mjs
117+ - name: Docker's first uses, several at once
118+ timeout-minutes: 4
119+ run: |
120+ set -uo pipefail
121+ start=$(date +%s)
122+ docker info >/dev/null & a=$!
123+ docker ps >/dev/null & b=$!
124+ docker version >/dev/null & c=$!
125+ failed=0
126+ for p in $a $b $c; do wait "$p" || failed=1; done
127+ echo "three docker commands at once, the daemon starting: $(( $(date +%s) - start )) s"
128+ [ "$failed" = 0 ] || { echo "::error::a docker command failed while the daemon started"; exit 1; }
129+ docker run --rm mirror.gcr.io/library/alpine:3.20 echo "container ok"
130+ - name: Runner Docker tests (job containers, services, Docker actions)
131+ timeout-minutes: 10
132+ working-directory: containers/runner
133+ run: pnpm exec vitest run test/docker.test.ts test/docker-wrapper.test.ts