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.

186 lines · 8.7 KBRaw History
1name: Live test
2
3# End-to-end checks against the real diffinery.com after each deploy: hosted
4# Actions jobs in Cloudflare Containers (with Docker builds and services), agent sandboxes, webhooks, scheduled
5# workflows and Git LFS. Each run works in a throwaway private repository that it
6# deletes at the end.
7#
8# Needs the repository secret DIFFINERY_LIVE_TOKEN: a Diffinery token (Settings ->
9# Tokens) with the admin scope and access to all repositories.
10on:
11 workflow_dispatch:
12 workflow_run:
13 workflows: [Deploy]
14 types: [completed]
15 branches: [main]
16
17permissions:
18 contents: read
19
20concurrency:
21 group: live-test
22 cancel-in-progress: false
23
24jobs:
25 live:
26 name: Containers, sandboxes, webhooks, schedules and LFS
27 if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
28 runs-on: ubuntu-latest
29 timeout-minutes: 30
30 env:
31 DIFFINERY: https://diffinery.com
32 TOKEN: ${{ secrets.DIFFINERY_LIVE_TOKEN }}
33 REPO: live-${{ github.run_id }}-${{ github.run_attempt }}
34 steps:
35 - name: Check the token
36 id: token
37 run: |
38 if [ -z "$TOKEN" ]; then
39 echo "::warning::DIFFINERY_LIVE_TOKEN is not set, so the live test is skipped. Create an admin token at $DIFFINERY/settings/tokens and save it as a repository secret."
40 echo "skip=true" >> "$GITHUB_OUTPUT"
41 exit 0
42 fi
43 login=$(curl -fsS "$DIFFINERY/api/v3/user" -H "Authorization: Bearer $TOKEN" | jq -r .login)
44 echo "OWNER=$login" >> "$GITHUB_ENV"
45 echo "testing as $login in $login/$REPO"
46
47 - name: Create a throwaway repository with a webhook
48 if: steps.token.outputs.skip != 'true'
49 run: |
50 set -euo pipefail
51 api() { curl -fsS -X "$1" "$DIFFINERY/api/v3$2" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" ${3:+--data "$3"}; }
52 api POST /user/repos "{\"name\":\"$REPO\",\"private\":true}" >/dev/null
53 api POST "/repos/$OWNER/$REPO/hooks" "{\"config\":{\"url\":\"$DIFFINERY/healthz\"},\"events\":[\"push\"]}" >/dev/null
54 echo "created $OWNER/$REPO"
55
56 - name: Push workflows and an LFS file
57 if: steps.token.outputs.skip != 'true'
58 run: |
59 set -euo pipefail
60 mkdir -p src/.github/workflows && cd src
61 git init -q -b main
62 git config user.name "Live test" && git config user.email "live-test@diffinery.com"
63 git lfs install --local >/dev/null
64 git lfs track "*.bin" >/dev/null
65 head -c 3145728 /dev/urandom > blob.bin
66 sha256sum blob.bin | cut -d' ' -f1 > ../blob.sha
67 echo "# $REPO" > README.md
68 cat > .github/workflows/ci.yml <<'EOF'
69 on: push
70 jobs:
71 hosted:
72 runs-on: ubuntu-latest
73 strategy:
74 matrix:
75 n: [1, 2]
76 steps:
77 - uses: actions/checkout@v4
78 - run: echo "live ok ${{ '${{' }} matrix.n }} $(cat README.md)"
79 docker:
80 runs-on: ubuntu-latest
81 services:
82 cache:
83 image: redis:7-alpine
84 ports: ["6379"]
85 options: --health-cmd "redis-cli ping" --health-interval 1s --health-retries 30
86 steps:
87 - run: |
88 printf 'FROM alpine:3.20\nRUN echo built > /msg\nCMD ["cat", "/msg"]\n' > Dockerfile
89 docker build -q -t live-image .
90 echo "docker ok $(docker run --rm live-image)"
91 exec 3<>/dev/tcp/127.0.0.1/${{ '${{' }} job.services.cache.ports['6379'] }}
92 printf 'PING\r\n' >&3
93 read -r line <&3
94 echo "service ok ${line%$'\r'}"
95 EOF
96 cat > .github/workflows/tick.yml <<'EOF'
97 on:
98 schedule:
99 - cron: "*/5 * * * *"
100 jobs:
101 tick:
102 runs-on: ubuntu-latest
103 steps:
104 - run: echo "scheduled ok"
105 EOF
106 git add -A && git commit -q -m "Live test"
107 git push -q "https://x-token:${TOKEN}@${DIFFINERY#https://}/$OWNER/$REPO.git" main
108 git lfs ls-files
109
110 - name: Hosted Actions jobs (and Docker) run in Cloudflare Containers
111 if: steps.token.outputs.skip != 'true'
112 run: |
113 set -euo pipefail
114 api() { curl -fsS "$DIFFINERY/api/v3$1" -H "Authorization: Bearer $TOKEN"; }
115 for i in $(seq 1 90); do
116 run=$(api "/repos/$OWNER/$REPO/actions/runs?event=push" | jq -c '.workflow_runs[0] // empty')
117 [ -n "$run" ] && [ "$(jq -r .status <<<"$run")" = completed ] && break
118 sleep 10
119 done
120 echo "$run" | jq '{id, status, conclusion}'
121 [ "$(jq -r .conclusion <<<"$run")" = success ] || { echo "::error::the push run did not succeed"; exit 1; }
122 id=$(jq -r .id <<<"$run")
123 jobs=$(api "/repos/$OWNER/$REPO/actions/runs/$id/jobs")
124 for job in $(jq -r '.jobs[] | select(.name | startswith("hosted")) | .id' <<<"$jobs"); do
125 log=$(api "/repos/$OWNER/$REPO/actions/jobs/$job/logs")
126 grep -F "live ok" <<<"$log" | grep -F "# $REPO" || { echo "::error::job $job did not print its line (checkout or run step failed)"; exit 1; }
127 done
128 job=$(jq -r '.jobs[] | select(.name == "docker") | .id' <<<"$jobs")
129 log=$(api "/repos/$OWNER/$REPO/actions/jobs/$job/logs")
130 grep -F "docker ok built" <<<"$log" || { echo "::error::docker build or run failed on a hosted runner"; exit 1; }
131 grep -F "service ok +PONG" <<<"$log" || { echo "::error::the redis service container was not reachable"; exit 1; }
132
133 - name: Git LFS round trip
134 if: steps.token.outputs.skip != 'true'
135 run: |
136 set -euo pipefail
137 git clone -q "https://x-token:${TOKEN}@${DIFFINERY#https://}/$OWNER/$REPO.git" clone
138 [ "$(sha256sum clone/blob.bin | cut -d' ' -f1)" = "$(cat blob.sha)" ] || { echo "::error::the LFS file came back different"; exit 1; }
139 echo "LFS file matches ($(stat -c %s clone/blob.bin) bytes)"
140
141 - name: Webhook delivered
142 if: steps.token.outputs.skip != 'true'
143 run: |
144 set -euo pipefail
145 for i in $(seq 1 30); do
146 hook=$(curl -fsS "$DIFFINERY/api/v3/repos/$OWNER/$REPO/hooks" -H "Authorization: Bearer $TOKEN" | jq -c '.[0]')
147 [ "$(jq -r '.lastDeliveryAt // empty' <<<"$hook")" != "" ] && break
148 sleep 5
149 done
150 echo "$hook" | jq '{url, lastStatus, lastDeliveryAt}'
151 [ "$(jq -r '.lastDeliveryAt // empty' <<<"$hook")" != "" ] || { echo "::error::the push webhook was never delivered"; exit 1; }
152
153 - name: Agent sandbox
154 if: steps.token.outputs.skip != 'true'
155 run: |
156 set -euo pipefail
157 base="$DIFFINERY/api/v3/repos/$OWNER/$REPO/diffinery/sandboxes"
158 id=$(curl -fsS -X POST "$base" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" --data '{}' | jq -r .id)
159 echo "sandbox $id"
160 for i in $(seq 1 40); do
161 out=$(curl -sS -X POST "$base/$id/exec" -H "Authorization: Bearer $TOKEN" -H "content-type: application/json" --data '{"cmd":"cat README.md && git lfs ls-files | wc -l"}' || true)
162 jq -e '.exitCode == 0' >/dev/null 2>&1 <<<"$out" && break
163 sleep 6
164 done
165 echo "$out" | jq '{exitCode, stdout}'
166 curl -sS -X POST "$base/$id/close" -H "Authorization: Bearer $TOKEN" >/dev/null || true
167 jq -e --arg r "# $REPO" '.exitCode == 0 and (.stdout | contains($r))' >/dev/null <<<"$out" || { echo "::error::the sandbox did not run the command in the repository"; exit 1; }
168
169 - name: Scheduled workflow
170 if: steps.token.outputs.skip != 'true'
171 run: |
172 set -euo pipefail
173 # The cron trigger fires every 5 minutes, so allow two windows.
174 for i in $(seq 1 75); do
175 run=$(curl -fsS "$DIFFINERY/api/v3/repos/$OWNER/$REPO/actions/runs?event=schedule" -H "Authorization: Bearer $TOKEN" | jq -c '.workflow_runs[0] // empty')
176 [ -n "$run" ] && [ "$(jq -r .status <<<"$run")" = completed ] && break
177 sleep 10
178 done
179 [ -n "$run" ] || { echo "::error::no scheduled run started"; exit 1; }
180 echo "$run" | jq '{id, event, status, conclusion}'
181 [ "$(jq -r .conclusion <<<"$run")" = success ] || { echo "::error::the scheduled run did not succeed"; exit 1; }
182
183 - name: Delete the throwaway repository
184 if: always() && steps.token.outputs.skip != 'true'
185 run: |
186 curl -sS -o /dev/null -w '%{http_code}\n' -X DELETE "$DIFFINERY/api/v3/repos/$OWNER/$REPO" -H "Authorization: Bearer $TOKEN"