| 1 | name: Docker in Cloudflare Containers |
| 2 | |
| 3 | # Checks whether a Cloudflare Container can run a Docker daemon (rootful or |
| 4 | # rootless), so hosted Diffinery runners can offer Docker. Deploys a throwaway |
| 5 | # probe Worker with a container, prints what worked, and removes it. |
| 6 | # Uses the repository secrets CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID. |
| 7 | on: |
| 8 | workflow_dispatch: |
| 9 | |
| 10 | permissions: |
| 11 | contents: read |
| 12 | |
| 13 | jobs: |
| 14 | probe: |
| 15 | name: Probe Docker inside a container |
| 16 | runs-on: ubuntu-latest |
| 17 | timeout-minutes: 40 |
| 18 | env: |
| 19 | CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} |
| 20 | CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} |
| 21 | NAME: diffinery-dind-probe-${{ github.run_id }} |
| 22 | steps: |
| 23 | - uses: actions/checkout@v4 |
| 24 | - uses: pnpm/action-setup@v4 |
| 25 | - uses: actions/setup-node@v4 |
| 26 | with: |
| 27 | node-version: 22 |
| 28 | cache: pnpm |
| 29 | - run: pnpm install --frozen-lockfile |
| 30 | - name: Deploy the probe |
| 31 | working-directory: apps/diffinery/scripts/dind-probe |
| 32 | run: | |
| 33 | set -euo pipefail |
| 34 | # Test the runner image's real Docker wrapper and daemon config. |
| 35 | cp ../../../../containers/runner/image/docker ../../../../containers/runner/image/daemon.json . |
| 36 | cat > wrangler.json <<JSON |
| 37 | {"name":"$NAME","main":"worker.js","compatibility_date":"2026-10-01","workers_dev":true, |
| 38 | "containers":[{"class_name":"Probe","image":"./Dockerfile","instance_type":"standard-2","max_instances":1}], |
| 39 | "durable_objects":{"bindings":[{"name":"PROBE","class_name":"Probe"}]}, |
| 40 | "migrations":[{"tag":"v1","new_sqlite_classes":["Probe"]}]} |
| 41 | JSON |
| 42 | pnpm --dir "$GITHUB_WORKSPACE/apps/diffinery" exec wrangler deploy -c "$PWD/wrangler.json" |
| 43 | sub=$(curl -sS "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/subdomain" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq -r '.result.subdomain // empty') |
| 44 | echo "PROBE_URL=https://$NAME.$sub.workers.dev" >> "$GITHUB_ENV" |
| 45 | - name: Run the probe |
| 46 | run: | |
| 47 | set -uo pipefail |
| 48 | for i in $(seq 1 60); do |
| 49 | curl -fsS -m 30 -o /dev/null "$PROBE_URL/run" && break |
| 50 | sleep 10 |
| 51 | done |
| 52 | for i in $(seq 1 120); do |
| 53 | out=$(curl -sS -m 30 "$PROBE_URL/log") |
| 54 | grep -q "PROBE DONE" <<<"$out" && break |
| 55 | sleep 10 |
| 56 | done |
| 57 | echo "$out" |
| 58 | echo "::group::Summary" |
| 59 | grep '^RESULT' <<<"$out" | tee -a "$GITHUB_STEP_SUMMARY" |
| 60 | echo "::endgroup::" |
| 61 | - name: Remove the probe |
| 62 | if: always() |
| 63 | working-directory: apps/diffinery/scripts/dind-probe |
| 64 | run: | |
| 65 | W=(pnpm --dir "$GITHUB_WORKSPACE/apps/diffinery" exec wrangler) |
| 66 | [ -f wrangler.json ] && "${W[@]}" delete -c "$PWD/wrangler.json" --force || true |
| 67 | for id in $("${W[@]}" containers list --json 2>/dev/null | jq -r --arg n "$NAME" '.[] | select(.name | startswith($n)) | .id'); do |
| 68 | "${W[@]}" containers delete "$id" -c "$PWD/wrangler.json" || true |
| 69 | done |