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.

108 lines · 5.6 KBRaw History
1name: D1 read replication
2
3# Checks the D1 Sessions API with read replication on a throwaway database, then
4# (when asked) turns read replication on for the production database.
5# Uses the repository secrets CLOUDFLARE_API_TOKEN (D1 and Workers Scripts: Edit)
6# and CLOUDFLARE_ACCOUNT_ID.
7on:
8 workflow_dispatch:
9 inputs:
10 enable_production:
11 description: Turn on read replication for the production database after the check passes
12 type: boolean
13 default: false
14
15permissions:
16 contents: read
17
18jobs:
19 check:
20 name: Check sessions on a test database
21 runs-on: ubuntu-latest
22 timeout-minutes: 20
23 env:
24 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
25 CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
26 NAME: diffinery-d1-probe-${{ github.run_id }}
27 steps:
28 - uses: actions/checkout@v4
29 - uses: pnpm/action-setup@v4
30 - uses: actions/setup-node@v4
31 with:
32 node-version: 22
33 cache: pnpm
34 - run: pnpm install --frozen-lockfile
35 - name: Create a test database with read replication
36 run: |
37 set -euo pipefail
38 API="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID"
39 auth=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json")
40 id=$(curl -sS -X POST "$API/d1/database" "${auth[@]}" --data "{\"name\":\"$NAME\"}" | jq -r '.result.uuid // empty')
41 [ -n "$id" ] || { echo "::error::could not create the test database"; exit 1; }
42 echo "D1_ID=$id" >> "$GITHUB_ENV"
43 curl -sS -X PUT "$API/d1/database/$id" "${auth[@]}" --data '{"read_replication":{"mode":"auto"}}' | jq -e '.success' >/dev/null
44 mode=$(curl -sS "$API/d1/database/$id" "${auth[@]}" | jq -r '.result.read_replication.mode')
45 echo "read replication on the test database: $mode"
46 [ "$mode" = auto ] || { echo "::error::read replication did not turn on"; exit 1; }
47 - name: Deploy the probe Worker
48 working-directory: scripts/d1-probe
49 run: |
50 set -euo pipefail
51 cat > wrangler.json <<JSON
52 {"name":"$NAME","main":"worker.js","compatibility_date":"2026-10-01","workers_dev":true,
53 "d1_databases":[{"binding":"DB","database_name":"$NAME","database_id":"$D1_ID"}]}
54 JSON
55 W=(pnpm --dir "$GITHUB_WORKSPACE/apps/diffinery" exec wrangler)
56 "${W[@]}" d1 execute "$NAME" --remote -c "$PWD/wrangler.json" --command "CREATE TABLE probe (k TEXT PRIMARY KEY, v TEXT NOT NULL)"
57 "${W[@]}" deploy -c "$PWD/wrangler.json"
58 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')
59 [ -n "$sub" ] || { echo "::error::the account has no workers.dev subdomain"; exit 1; }
60 echo "PROBE_URL=https://$NAME.$sub.workers.dev" >> "$GITHUB_ENV"
61 - name: Read your own writes through bookmarks
62 run: |
63 set -uo pipefail
64 for i in $(seq 1 30); do
65 body=$(curl -sS "$PROBE_URL/check?rounds=25") && jq -e . >/dev/null <<<"$body" && break
66 sleep 5
67 done
68 echo "$body" | jq .
69 jq -e '.ok' >/dev/null <<<"$body" || { echo "::error::a read after a bookmark missed the write, or a query failed"; exit 1; }
70 echo "every read from a bookmark saw its write ($(jq -r '.replicaReads' <<<"$body") served by replicas)"
71 - name: Remove the probe and the test database
72 if: always()
73 run: |
74 cd scripts/d1-probe
75 [ -f wrangler.json ] && pnpm --dir "$GITHUB_WORKSPACE/apps/diffinery" exec wrangler delete -c "$PWD/wrangler.json" --force || true
76 [ -n "${D1_ID:-}" ] && curl -sS -X DELETE "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/d1/database/$D1_ID" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" | jq -c '{success}' || true
77
78 enable:
79 name: Turn on read replication in production
80 needs: check
81 if: ${{ inputs.enable_production }}
82 runs-on: ubuntu-latest
83 environment:
84 name: production
85 url: https://diffinery.com
86 env:
87 CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
88 CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
89 steps:
90 - name: Enable and verify
91 run: |
92 set -euo pipefail
93 API="https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID"
94 auth=(-H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" -H "Content-Type: application/json")
95 id=$(curl -sS "$API/d1/database?name=diffinery&per_page=100" "${auth[@]}" | jq -r '.result[]? | select(.name=="diffinery") | .uuid' | head -n1)
96 [ -n "$id" ] || { echo "::error::production database not found"; exit 1; }
97 curl -sS -X PUT "$API/d1/database/$id" "${auth[@]}" --data '{"read_replication":{"mode":"auto"}}' | jq -e '.success' >/dev/null
98 mode=$(curl -sS "$API/d1/database/$id" "${auth[@]}" | jq -r '.result.read_replication.mode')
99 echo "read replication on diffinery: $mode"
100 [ "$mode" = auto ] || { echo "::error::read replication did not turn on"; exit 1; }
101 - name: Check the site still reads and writes
102 run: |
103 set -euo pipefail
104 for p in /healthz /login /sitemap.xml; do
105 code=$(curl -sS -o /dev/null -w '%{http_code}' "https://diffinery.com$p"); echo "$p -> $code"; [ "$code" = 200 ]
106 done
107 bm=$(curl -sS -D - -o /dev/null https://diffinery.com/sitemap.xml | tr -d '\r' | sed -n 's/^x-d1-bookmark: //Ip')
108 echo "x-d1-bookmark: $bm"; [ -n "$bm" ]