Containers, registry, sandboxes, webhooks, schedules, LFS and pull requests
took 4m 55s Raw logs
Failed at Hosted Actions jobs (and Docker) run in Cloudflare Containers
the push run did not succeed
Set up job
Diffinery runner: job live (Containers, registry, sandboxes, webhooks, schedules, LFS and pull requests) for codician/diffinery@88b59f0790e2Runner: image diffinery-runner:4d3bf17f06575e64ab4155b3, in lis01, WEUR, container 0e29b565f547b8d28b6864da4cd8279082c9267b9010b79190ed0cf8cc5f9c85Runner size: large (4 vCPU, 12 GiB of memory, 20 GB of disk), this job uses what a smaller runner doesn't have; runs-on: diffinery-large (or -medium, -small, -tiny) sets a sizeHost: AMD EPYC (family 26, model 17), 4 vCPUs, 2250 MHzWorkspace: /workspace/diffineryHost: 0% of the vCPUs' time went to other work on the host (steal)Job completed with conclusion: failureResources: 0.0 of 4 vCPUs busy on average (0.2 at the 95th percentile), peak memory 0.4 GiB of 11.9 GiB, peak disk 1.1 GB of 19.2 GB
Check the token1s
##[group]Run if [ "$GITHUB_EVENT_NAME" = workflow_run ]; then # Deploy's gate skips a commit that is no longer main's tip (the newer commit's run deploys it): # then nothing new is live, and this run would only test the site as it was. deployed=$(curl -fsS "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/actions/runs/$DEPLOY_RUN/jobs" -H "Authorization: Bearer $GH_TOKEN" | jq -r '[.jobs[] | select(.name == "Deploy to Cloudflare") | .conclusion][0] // "unknown"' || echo unknown) if [ "$deployed" = skipped ]; then echo "::notice::Deploy deployed nothing for $HEAD_SHA (main has moved on), so there is nothing new to test" echo "skip=true" >> "$GITHUB_OUTPUT" exit 0 fifiif [ -z "$TOKEN" ]; then # After a deploy of diffinery.com a missing token is an error: a green run would mean nothing was tested. if [ "$GITHUB_EVENT_NAME" = workflow_run ]; then echo "::error::DIFFINERY_LIVE_TOKEN is not set in the production environment, so nothing was tested. Create a write token at $DIFFINERY/settings/tokens and save it as a production environment secret." exit 1 fi echo "::warning::DIFFINERY_LIVE_TOKEN is not set, so the live test is skipped. Create a write token at $DIFFINERY/settings/tokens and save it as a production environment secret." echo "skip=true" >> "$GITHUB_OUTPUT" exit 0filogin=$(curl -fsS "$DIFFINERY/api/v3/user" -H "Authorization: Bearer $TOKEN" | jq -r .login)echo "OWNER=$login" >> "$GITHUB_ENV"echo "testing as $login in $login/$REPO"env: GH_TOKEN: *** DEPLOY_RUN: 766##[endgroup]testing as cnbrkdmrci in cnbrkdmrci/live-767-1
Choose the registry check's image sizes1s
##[group]Run set -euo pipefail# Layers over Cloudflare's 100 MB request limit (which hosted runners push through# the registry relay) take minutes to push and pull. They're pushed on manual runs and# whenever the registry, the runner image or this test changed since the last# successful live test; other deploys push small images through the same path.full=trueif [ "$GITHUB_EVENT_NAME" = workflow_run ]; then api() { curl -fsS "$DIFFINERY/api/v3/repos/$GITHUB_REPOSITORY$1" -H "Authorization: Bearer $TOKEN"; } last=$(api "/actions/runs?workflow=live-test.yml&status=success&per_page=1" | jq -r '.workflow_runs[0].head_sha // empty' || true) if [ -n "$last" ] && files=$(api "/compare/$last...$HEAD_SHA" | jq -r '.files[].filename'); then grep -qE '^(apps/diffinery/src/registry/|containers/runner/|\.github/workflows/live-test\.yml$)' <<<"$files" || full=false echo "changed since $last: $(wc -l <<<"$files") files" fifiif [ "$full" = true ]; then echo "BIG=150000000" >> "$GITHUB_ENV" echo "BUILDX=120000000" >> "$GITHUB_ENV"else echo "BIG=5000000" >> "$GITHUB_ENV" echo "BUILDX=4000000" >> "$GITHUB_ENV"fiecho "registry check: full=$full"##[endgroup]changed since dd9f88fdc1f124292d7f34761a08ad3743df4460: 2 filesregistry check: full=false
Profile picture upload (WASM resize in the Worker)6s
##[group]Run set -euo pipefailav="$DIFFINERY/api/avatars/$OWNER"# Keep the account's current picture, if it has one, to put it back afterwards.had=$(curl -fsS -o before.webp -w '%{content_type}' "$av?s=256&v=1")restore() { if [ "$had" = image/webp ]; then curl -fsS -X PUT "$DIFFINERY/api/v3/user/avatar" -H "Authorization: Bearer $TOKEN" -H "content-type: image/webp" --data-binary @before.webp | jq -r '"restored the previous picture: \(.avatar_url)"' else curl -fsS -o /dev/null -X DELETE "$DIFFINERY/api/v3/user/avatar" -H "Authorization: Bearer $TOKEN" fi}trap restore EXIT# A 600×400 PNG gradient; the server crops it to a square and makes small WebPs.python3 - <<'EOF'import struct, zlibw, h = 600, 400rows = b"".join(b"\0" + bytes(c for x in range(w) for c in (x * 255 // w, y * 255 // h, 128)) for y in range(h))chunk = lambda t, d: struct.pack(">I", len(d)) + t + d + struct.pack(">I", zlib.crc32(t + d))open("avatar.png", "wb").write(b"\x89PNG\r\n\x1a\n" + chunk(b"IHDR", struct.pack(">IIBBBBB", w, h, 8, 2, 0, 0, 0)) + chunk(b"IDAT", zlib.compress(rows)) + chunk(b"IEND", b""))EOFurl=$(curl -fsS -X PUT "$DIFFINERY/api/v3/user/avatar" -H "Authorization: Bearer $TOKEN" -H "content-type: image/png" --data-binary @avatar.png | jq -r .avatar_url)echo "uploaded: $url"v=${url##*v=}type=$(curl -fsS -o small.webp -w '%{content_type}' "$av?s=64&v=$v")[ "$type" = image/webp ] || { echo "::error::expected image/webp, got $type"; exit 1; }python3 - <<'EOF'import struct, sysb = open("small.webp", "rb").read()ok = b[:4] == b"RIFF" and b[8:12] == b"WEBP"if b[12:16] == b"VP8 ": size = (struct.unpack("<H", b[26:28])[0] & 0x3FFF, struct.unpack("<H", b[28:30])[0] & 0x3FFF)elif b[12:16] == b"VP8X": size = (int.from_bytes(b[24:27], "little") + 1, int.from_bytes(b[27:30], "little") + 1)else: size = Noneprint(f"64 px avatar: {len(b)} bytes, {size}")if not ok or size != (64, 64) or len(b) > 5000: print("::error::the 64 px avatar is not a 64x64 WebP under 5 KB"); sys.exit(1)EOFcurl -fsS -X DELETE "$DIFFINERY/api/v3/user/avatar" -H "Authorization: Bearer $TOKEN" >/dev/null# A `v` that is not current bypasses the edge cache, so this sees the removal from any colo.type=$(curl -fsS -o /dev/null -w '%{content_type}' "$av?s=64&v=1")[[ "$type" == image/svg+xml* ]] || { echo "::error::after removal expected the initials SVG, got $type"; exit 1; }echo "removed: initials again ($type)"##[endgroup]uploaded: https://diffinery.com/api/avatars/cnbrkdmrci?v=179285012464 px avatar: 256 bytes, (64, 64)removed: initials again (image/svg+xml; charset=utf-8)restored the previous picture: https://diffinery.com/api/avatars/cnbrkdmrci?v=1792850127
Public docs are served without signing in1s
##[group]Run set -euo pipefailcheck() { # path, content type, text it must contain body=$(curl -fsS -D /tmp/h "$DIFFINERY$1") grep -qi "content-type: $2" /tmp/h || { echo "::error::$1 is not $2"; exit 1; } grep -qF -- "$3" <<<"$body" || { echo "::error::$1 doesn't contain: $3"; exit 1; } echo "$1 ok ($(wc -c <<<"$body") bytes)"}check /docs "text/html" "Overview"check /docs/actions/workflow-syntax "text/html" "View as Markdown"check /docs/quickstart.md "text/markdown" "# Quickstart"check /llms.txt "text/plain" "/docs/actions/workflow-syntax.md"check /llms-full.txt "text/plain" "# REST API reference"check /docs/api/openapi.json "application/json" '"openapi": "3.1.0"'check /-/cli/diffinery.mjs "text/javascript" "#!/usr/bin/env node"md=$(curl -fsS -H "Accept: text/markdown" "$DIFFINERY/docs/agents/mcp")grep -q "^# MCP" <<<"$md" || { echo "::error::Accept: text/markdown didn't return Markdown"; exit 1; }curl -fsS "$DIFFINERY/-/cli/diffinery.mjs" -o /tmp/diffinery.mjs && node /tmp/diffinery.mjs --version##[endgroup]/docs ok (95480 bytes)/docs/actions/workflow-syntax ok (181306 bytes)/docs/quickstart.md ok (17655 bytes)/llms.txt ok (24038 bytes)/llms-full.txt ok (2593963 bytes)/docs/api/openapi.json ok (825256 bytes)/-/cli/diffinery.mjs ok (256504 bytes)diffinery 0.1.0
Email sending is configured0s
##[group]Run set -euo pipefails=$(curl -sS -w '\n%{http_code}' "$DIFFINERY/api/v3/diffinery/admin/email" -H "Authorization: Bearer $TOKEN")code=${s##*$'\n'}; s=${s%$'\n'*}# Only a site administrator's token reads the email settings, and the live-test account isn't one.if [ "$code" = 403 ]; then echo "::notice::skipped: the live-test token isn't a site administrator's"; exit 0; fi[ "$code" = 200 ] || { echo "::error::/diffinery/admin/email answered $code"; exit 1; }echo "$s" | jq '{sending, queue, from, last_24h}'[ "$(jq -r .sending <<<"$s")" = true ] || { echo "::error::the Worker has no Email Sending binding"; exit 1; }[ "$(jq -r .queue <<<"$s")" = true ] || { echo "::error::the Worker has no mail queue binding"; exit 1; }failed=$(jq -r .last_24h.failed <<<"$s")[ "$failed" = 0 ] || echo "::warning::$failed emails failed in the last 24 hours; see Settings -> Email in site administration"##[endgroup]{ "sending": true, "queue": true, "from": "notifications@diffinery.com", "last_24h": { "sent": 19, "failed": 0, "skipped": 0 }}
Create a throwaway repository with a webhook3s
##[group]Run set -euo pipefailapi() { curl -fsS -X "$1" "$DIFFINERY/api/v3$2" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" ${3:+--data "$3"}; }api POST /user/repos "{\"name\":\"$REPO\",\"private\":true}" >/dev/null# Webhooks may only reach public hosts other than Diffinery itself; the push's delivery goes to example.com.code=$(curl -sS -o /dev/null -w '%{http_code}' -X POST "$DIFFINERY/api/v3/repos/$OWNER/$REPO/hooks" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" --data "{\"config\":{\"url\":\"$DIFFINERY/healthz\"},\"events\":[\"push\"]}")[ "$code" = 422 ] || { echo "::error::a webhook to Diffinery itself answered $code, not 422"; exit 1; }api POST "/repos/$OWNER/$REPO/hooks" "{\"config\":{\"url\":\"https://example.com/diffinery-live-test\"},\"events\":[\"push\"]}" >/dev/nullecho "created $OWNER/$REPO"##[endgroup]created cnbrkdmrci/live-767-1
Wait for jobs to start on the deployed runner image16s
##[group]Run set -euo pipefail# A deploy's container rollout updates instances over a while after it starts, and an# instance not yet updated starts jobs on the previous runner image. Jobs report their# image (`runner.image`), so a probe workflow runs until three jobs in a row land on the# image the deploy set (/meta's runner_image) before the Docker and registry checks.api() { curl -fsS -X "$1" "$DIFFINERY/api/v3$2" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" ${3:+--data "$3"}; }expected=$(api GET /meta | jq -r '.runner_image // empty')if [ -z "$expected" ]; then echo "::warning::/meta doesn't name the deployed runner image, so jobs aren't checked against it" exit 0fiecho "DEPLOYED_RUNNER_IMAGE=$expected" >> "$GITHUB_ENV"mkdir -p src/.github/workflows && cd srcgit init -q -b maingit config user.name "Live test" && git config user.email "live-test@diffinery.com"cat > .github/workflows/probe.yml <<'EOF'on: workflow_dispatchjobs: probe: runs-on: ubuntu-latest strategy: matrix: n: [1, 2, 3] steps: - run: echo "probe ok"EOFgit add -A && git commit -q -m "Runner image probe"git push -q "https://x-token:${TOKEN}@${DIFFINERY#https://}/$OWNER/$REPO.git" maindeadline=$(( $(date +%s) + 15 * 60 ))while :; do before=$(api GET "/repos/$OWNER/$REPO/actions/runs?event=workflow_dispatch" | jq '.workflow_runs | length') api POST "/repos/$OWNER/$REPO/actions/workflows/probe.yml/dispatches" '{"ref":"main"}' >/dev/null run="" # Up to 10 minutes: a job turned away from a replaced image starts again after 30 s, 1, 2 and 4 minutes. for i in $(seq 1 120); do runs=$(api GET "/repos/$OWNER/$REPO/actions/runs?event=workflow_dispatch") if [ "$(jq '.workflow_runs | length' <<<"$runs")" -gt "$before" ]; then run=$(jq -c '.workflow_runs[0]' <<<"$runs") [ "$(jq -r .status <<<"$run")" = completed ] && break fi sleep 5 done [ -n "$run" ] || { echo "::error::the probe workflow didn't start"; exit 1; } probe=$(api GET "/repos/$OWNER/$REPO/actions/runs/$(jq -r .id <<<"$run")/jobs") images=$(jq -r '.jobs[] | .runner.image // "no image reported"' <<<"$probe") echo "probe jobs ran on: $(sort <<<"$images" | uniq -c | sed 's/^ *//' | paste -sd ','), started again $(jq '[.jobs[].placementTries // 0] | add // 0' <<<"$probe") time(s) in all" [ "$(grep -cxF "$expected" <<<"$images" || true)" = 3 ] && { echo "jobs start on $expected"; exit 0; } if [ "$(date +%s)" -ge "$deadline" ]; then echo "::error::15 minutes after the deploy, jobs still start on a runner image other than the deployed $expected" exit 1 fi sleep 20done##[endgroup]probe jobs ran on: 3 diffinery-runner:4d3bf17f06575e64ab4155b3, started again 0 time(s) in alljobs start on diffinery-runner:4d3bf17f06575e64ab4155b3
Push workflows and an LFS file8s
##[group]Run set -euo pipefailmkdir -p src/.github/workflows && cd srcgit init -q -b maingit config user.name "Live test" && git config user.email "live-test@diffinery.com"git lfs install --local >/dev/nullgit lfs track "*.bin" >/dev/nullhead -c 3145728 /dev/urandom > blob.binsha256sum blob.bin | cut -d' ' -f1 > ../blob.shaecho "# $REPO" > README.mdcat > .github/workflows/ci.yml <<'EOF'on: pushjobs: hosted: runs-on: ubuntu-latest strategy: matrix: n: [1, 2] steps: - uses: actions/checkout@v4 - run: echo "live ok ${{ matrix.n }} $(cat README.md)" # Passwordless sudo, as on GitHub's runners (`sudo apt-get install ...` is common). - run: sudo -n true && echo "sudo ok $(sudo -n id -u)" docker: runs-on: ubuntu-latest services: cache: image: redis:7-alpine ports: ["6379"] options: --health-cmd "redis-cli ping" --health-interval 1s --health-retries 30 steps: - run: | printf 'FROM alpine:3.20\nRUN echo built > /msg\nCMD ["cat", "/msg"]\n' > Dockerfile docker build -q -t live-image . echo "docker ok $(docker run --rm live-image)" exec 3<>/dev/tcp/127.0.0.1/${{ job.services.cache.ports['6379'] }} printf 'PING\r\n' >&3 read -r line <&3 echo "service ok ${line%$'\r'}" # Container registry: a layer pushed with the job's GITHUB_TOKEN (over Cloudflare's # 100 MB request limit on full runs), plus the docker/*-action route most ghcr.io # workflows use. __BIG__ and __BUILDX__ are the layer sizes. publish: runs-on: ubuntu-latest permissions: packages: write steps: - run: | image="diffinery.com/${GITHUB_REPOSITORY,,}" echo "${{ secrets.GITHUB_TOKEN }}" | docker login diffinery.com -u "${{ github.actor }}" --password-stdin head -c __BIG__ /dev/urandom > big.bin sha256sum big.bin | cut -d' ' -f1 > big.sha printf 'FROM scratch\nCOPY big.bin big.sha /\n' > Dockerfile docker build -q -t "$image:live" . docker push "$image:live" echo "pushed $(cat big.sha)" buildx: runs-on: ubuntu-latest permissions: packages: write steps: - run: | echo "IMAGE=diffinery.com/${GITHUB_REPOSITORY,,}" >> "$GITHUB_ENV" head -c __BUILDX__ /dev/urandom > big.bin printf 'FROM scratch\nCOPY big.bin /\n' > Dockerfile - uses: docker/setup-buildx-action@v3 - uses: docker/login-action@v3 with: registry: diffinery.com username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/build-push-action@v6 with: context: . push: true tags: ${{ env.IMAGE }}:buildx pull: needs: [publish, buildx] runs-on: ubuntu-latest permissions: packages: read steps: - run: | image="diffinery.com/${GITHUB_REPOSITORY,,}" echo "${{ secrets.GITHUB_TOKEN }}" | docker login diffinery.com -u "${{ github.actor }}" --password-stdin docker pull -q "$image:live" id=$(docker create "$image:live" true) docker cp "$id:/big.bin" big.bin && docker cp "$id:/big.sha" big.sha [ "$(sha256sum big.bin | cut -d' ' -f1)" = "$(cat big.sha)" ] && echo "registry ok $(stat -c %s big.bin) bytes" docker pull -q "$image:buildx" id=$(docker create "$image:buildx" true) docker cp "$id:/big.bin" buildx.bin echo "buildx ok $(stat -c %s buildx.bin) bytes"EOFcat > .github/workflows/tick.yml <<'EOF'on: schedule: - cron: "*/5 * * * *"jobs: tick: runs-on: ubuntu-latest steps: - run: echo "scheduled ok"EOFsed -i "s/__BIG__/$BIG/g; s/__BUILDX__/$BUILDX/g" .github/workflows/ci.ymlgit add -A && git commit -q -m "Live test"git push -q "https://x-token:${TOKEN}@${DIFFINERY#https://}/$OWNER/$REPO.git" maingit lfs ls-files##[endgroup]warning: re-init: ignored --initial-branch=mainUploading LFS objects: 100% (1/1), 3.1 MB | 0 B/s, done.627e8da21a * blob.bin
Web UI features (highlighting, settings pages, view transitions, base permission, invitations)2s
##[group]Run set -euo pipefailpage() { curl -fsS "$DIFFINERY$1" -H "Authorization: Bearer $TOKEN"; }has() { grep -qF -- "$2" <<<"$1" || { echo "::error::$3"; exit 1; }; }html=$(page "/$OWNER/$REPO/blob/main/.github/workflows/ci.yml")has "$html" '<td class="sh">' "the workflow file is not syntax highlighted"has "$html" "--shiki-dark" "highlighted code has no dark theme colours"has "$html" "@view-transition{navigation:auto}" "pages don't opt in to view transitions"has "$html" "prefers-reduced-motion:no-preference" "view transitions ignore reduced motion"has "$(page /settings/appearance)" 'name="appearance"' "Settings -> Appearance has no theme choice"has "$(page /notifications)" 'href="/settings/notifications"' "the notifications page doesn't link to notification settings"code=$(curl -sS -o /tmp/out -w '%{http_code}' -X POST "$DIFFINERY/api/v3/user/repos" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" --data "{\"name\":\"$REPO-internal\",\"visibility\":\"internal\"}")[ "$code" = 422 ] || { echo "::error::visibility internal got $code, expected 422"; curl -sS -o /dev/null -X DELETE "$DIFFINERY/api/v3/repos/$OWNER/$REPO-internal" -H "Authorization: Bearer $TOKEN"; exit 1; }org=$(page /api/v3/user/orgs | jq -r '.[0].login // empty')if [ -n "$org" ]; then perm=$(page "/api/v3/orgs/$org" | jq -r '.default_repository_permission // empty') case "$perm" in none|read|write|admin) echo "$org's base permission: $perm" ;; *) echo "::error::GET /orgs/$org has no default_repository_permission"; exit 1 ;; esac # A malformed rename is refused before anything is written (422 for owners, 403 for everyone else). code=$(curl -sS -o /tmp/out -w '%{http_code}' -X PATCH "$DIFFINERY/api/v3/orgs/$org" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" --data '{"name":123}') case "$code" in 422|403) ;; *) echo "::error::PATCH /orgs/$org with a numeric name got $code"; cat /tmp/out; exit 1 ;; esacfipage "/api/v3/repos/$OWNER/$REPO/invitations" | jq -e 'type == "array"' >/dev/null || { echo "::error::GET /repos/$OWNER/$REPO/invitations doesn't list pending invitations"; exit 1; }tools=$(curl -fsS -X POST "$DIFFINERY/mcp" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" -H "accept: application/json, text/event-stream" --data '{"jsonrpc":"2.0","id":1,"method":"tools/list"}' | jq -r '.result.tools[].name')for t in get_organization list_invitations revoke_invitation; do grep -qx "$t" <<<"$tools" || { echo "::error::the MCP server has no $t tool"; exit 1; }; doneecho "highlighting, Appearance, notification settings link, view transitions, visibility, organization and invitation checks passed"##[endgroup]ALCAN's base permission: writehighlighting, Appearance, notification settings link, view transitions, visibility, organization and invitation checks passed
Diffy placeholders, Actions settings, milestones and variables4s
##[group]Run set -euo pipefailpage() { curl -fsS "$DIFFINERY$1" -H "Authorization: Bearer $TOKEN"; }api() { curl -fsS -X "$1" "$DIFFINERY/api/v3$2" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" ${3:+--data "$3"}; }code() { curl -sS -o /tmp/out -w '%{http_code}' -X "$1" "$DIFFINERY/api/v3$2" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" ${3:+--data "$3"}; }fail() { echo "::error::$1"; cat /tmp/out 2>/dev/null || true; exit 1; }has() { grep -qF -- "$2" <<<"$1" || fail "$3"; }R="/repos/$OWNER/$REPO"has "$(page /new/import)" 'diffy/hello-world' "the importer's placeholder isn't Diffy"has "$(page "/$OWNER/$REPO/settings/actions")" 'Allow GitHub Actions to create and approve pull requests' "repository settings have no Actions page"# Milestones: created closed, checked before an issue edit is saved, shown in the response, and filterable.[ "$(api POST "$R/milestones" '{"title":"Shipped","state":"closed"}' | jq -r .state)" = closed ] || fail "POST milestones ignored state"ms=$(api POST "$R/milestones" '{"title":"Live v1"}' | jq -r .number)n=$(api POST "$R/issues" '{"title":"Planned"}' | jq -r .number)[ "$(code PATCH "$R/issues/$n" '{"title":"Renamed","milestone":999999}')" = 422 ] || fail "an unknown milestone wasn't refused with 422"[ "$(api GET "$R/issues/$n" | jq -r .title)" = Planned ] || fail "a refused milestone edit saved the title"[ "$(api PATCH "$R/issues/$n" "{\"milestone\":$ms}" | jq -r .milestone.number)" = "$ms" ] || fail "PATCH issue doesn't return the new milestone"[ "$(api GET "$R/issues?milestone=$ms&state=all" | jq -r '[.[].number] | join(",")')" = "$n" ] || fail "issues aren't filtered by milestone"loc=$(curl -sS -o /dev/null -w '%{redirect_url}' "$DIFFINERY/$OWNER/$REPO/milestone/$ms" -H "Authorization: Bearer $TOKEN")has "$loc" "/issues?milestone=$ms" "the milestone link doesn't open its issue list"# Repository variable deletes ignore letter case.[ "$(code PUT "$R/actions/variables/LIVE_CASE" '{"value":"x"}')" = 204 ] || fail "couldn't set a repository variable"[ "$(code DELETE "$R/actions/variables/live_case")" = 204 ] || fail "couldn't delete a repository variable"[ "$(code GET "$R/actions/variables/LIVE_CASE")" = 404 ] || fail "deleting a variable in lower case left it in place"org=$(page /api/v3/user/orgs | jq -r '.[0].login // empty')if [ -n "$org" ]; then # Deleting an organization variable or secret that doesn't exist is a 404 (403 for someone who isn't an admin). for path in variables secrets; do c=$(code DELETE "/orgs/$org/actions/$path/LIVE_NEVER_SET_$GITHUB_RUN_ID") case "$c" in 404|403) ;; *) fail "DELETE of a missing organization $path entry got $c" ;; esac donefiecho "Diffy placeholders, Actions settings page, milestone and variable checks passed"##[endgroup]Diffy placeholders, Actions settings page, milestone and variable checks passed
Hosted Actions jobs (and Docker) run in Cloudflare Containers1s
##[group]Run set -euo pipefailapi() { curl -fsS "$DIFFINERY/api/v3$1" -H "Authorization: Bearer $TOKEN"; }for i in $(seq 1 90); do run=$(api "/repos/$OWNER/$REPO/actions/runs?event=push" | jq -c '.workflow_runs[0] // empty') [ -n "$run" ] && [ "$(jq -r .status <<<"$run")" = completed ] && break sleep 10doneecho "$run" | jq '{id, status, conclusion}'id=$(jq -r .id <<<"$run")jobs=$(api "/repos/$OWNER/$REPO/actions/runs/$id/jobs")# placementTries: times a job went back in line (no machine, or a runner on a replaced image turned away).# restarts: times it started over because the platform replaced its runner before its steps ran.jq -r '.jobs[] | " \(.name): \(.conclusion)\(if .failure then " - " + (.failure.message // "") else "" end)\(try " (\((.completed_at | fromdateiso8601) - (.started_at | fromdateiso8601))s)" catch "")\(if (.placementTries // 0) > 0 then ", started again \(.placementTries) time(s)" else "" end)\(if (.restarts // 0) > 0 then ", restarted \(.restarts) time(s) after its runner was replaced" else "" end)"' <<<"$jobs"if [ "$(jq -r .conclusion <<<"$run")" != success ]; then for job in $(jq -r '.jobs[] | select(.conclusion == "failure" or .conclusion == "timed_out") | .id' <<<"$jobs"); do echo "::group::Last log lines of job $job" api "/repos/$OWNER/$REPO/actions/jobs/$job/logs" | tail -n 80 || true echo "::endgroup::" done echo "::error::the push run did not succeed" exit 1fiif [ -n "${DEPLOYED_RUNNER_IMAGE:-}" ]; then # Every job ran the deployed runner, so these checks test what was just deployed. stale=$(jq -r --arg want "$DEPLOYED_RUNNER_IMAGE" '.jobs[] | select(.runner.image != $want) | "\(.name) on \(.runner.image // "no image reported")"' <<<"$jobs") [ -z "$stale" ] || { echo "::error::jobs ran on a runner image other than the deployed $DEPLOYED_RUNNER_IMAGE: $(paste -sd ';' <<<"$stale")"; exit 1; } echo "every job ran on $DEPLOYED_RUNNER_IMAGE"fifor job in $(jq -r '.jobs[] | select(.name | startswith("hosted")) | .id' <<<"$jobs"); do log=$(api "/repos/$OWNER/$REPO/actions/jobs/$job/logs") grep -F "live ok" <<<"$log" | grep -F "# $REPO" || { echo "::error::job $job did not print its line (checkout or run step failed)"; exit 1; } grep -F "sudo ok 0" <<<"$log" || { echo "::error::job $job could not run sudo without a password"; exit 1; }donejob=$(jq -r '.jobs[] | select(.name == "docker") | .id' <<<"$jobs")log=$(api "/repos/$OWNER/$REPO/actions/jobs/$job/logs")grep -F "docker ok built" <<<"$log" || { echo "::error::docker build or run failed on a hosted runner"; exit 1; }grep -F "service ok +PONG" <<<"$log" || { echo "::error::the redis service container was not reachable"; exit 1; }job=$(jq -r '.jobs[] | select(.name == "pull") | .id' <<<"$jobs")log=$(api "/repos/$OWNER/$REPO/actions/jobs/$job/logs")grep -F "registry ok $BIG bytes" <<<"$log" || { echo "::error::the image pushed with a $BIG-byte layer did not come back intact from the registry"; exit 1; }grep -F "buildx ok $BUILDX bytes" <<<"$log" || { echo "::error::the image pushed by docker/build-push-action did not come back from the registry"; exit 1; }api "/users/$OWNER/packages/container/${REPO,,}/versions" | jq -c '.[] | {tags: .metadata.container.tags, size, platforms}'##[endgroup]{ "id": "c15c4f058a5c4aefae4b11c6472e38f0", "status": "completed", "conclusion": "failure"} Set up workflow: failure - Diffinery couldn't start this run (internal error). Re-run the workflow. (0s)##[group]Last log lines of job d2edb3f5d17b4159aec2472c31665053##[endgroup]##[error]the push run did not succeed##[error]Process completed with exit code 1.
An organization's packages page keeps its tabs
Git LFS round trip
Webhook delivered
Tokens can't mint tokens (API or web forms)
Agent sandbox
Agent jails (bubblewrap, no network but the allowlist proxy and the model proxy)
Pull requests and the merge queue
Import a public GitHub repository
Scheduled workflow4m 7s
##[group]Run set -euo pipefail# The cron trigger fires every 5 minutes, so allow two windows.for i in $(seq 1 75); do run=$(curl -fsS "$DIFFINERY/api/v3/repos/$OWNER/$REPO/actions/runs?event=schedule" -H "Authorization: Bearer $TOKEN" | jq -c '.workflow_runs[0] // empty') [ -n "$run" ] && [ "$(jq -r .status <<<"$run")" = completed ] && break sleep 10doneif [ -z "$run" ]; then # Whether the scheduler stopped ticking (last_tick_at is old) or ticked without starting the run # (site administrators only: another token prints the 403). echo "scheduler: $(curl -sS "$DIFFINERY/api/v3/diffinery/admin/scheduler" -H "Authorization: Bearer $TOKEN") (now $(date -u +%FT%TZ))" echo "::error::no scheduled run started" exit 1fiecho "$run" | jq '{id, event, status, conclusion}'[ "$(jq -r .conclusion <<<"$run")" = success ] || { echo "::error::the scheduled run did not succeed"; exit 1; }##[endgroup]{ "id": "a26783b4195f45c8adeb1c751f3a70f1", "event": "schedule", "status": "completed", "conclusion": "success"}
Delete the throwaway repositories2s
##[group]Run curl -sS -o /dev/null -w 'package %{http_code}\n' -X DELETE "$DIFFINERY/api/v3/users/$OWNER/packages/container/${REPO,,}" -H "Authorization: Bearer $TOKEN"curl -sS -o /dev/null -w '%{http_code}\n' -X DELETE "$DIFFINERY/api/v3/repos/$OWNER/$REPO" -H "Authorization: Bearer $TOKEN"[ -z "${PR_REPO:-}" ] || curl -sS -o /dev/null -w '%{http_code}\n' -X DELETE "$DIFFINERY/api/v3/repos/$OWNER/$PR_REPO" -H "Authorization: Bearer $TOKEN"[ -z "${IMPORT_REPO:-}" ] || curl -sS -o /dev/null -w '%{http_code}\n' -X DELETE "$DIFFINERY/api/v3/repos/$OWNER/$IMPORT_REPO" -H "Authorization: Bearer $TOKEN"##[endgroup]package 404204