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.

46 lines · 3.1 KB · executableRaw History
1#!/usr/bin/env bash
2# Waits after `wrangler deploy` until the container rollouts it started reach their last step.
3# `wrangler deploy` returns as soon as a rollout starts. Even in the last step, an instance the
4# rollout hasn't updated yet starts jobs on the old image, so the Live test waits until jobs
5# report the deployed image. Running containers keep the old image until they exit
6# (rollout_active_grace_period), so the rollouts don't need to complete.
7#
8# Run from the repository root with CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID and
9# RUNNER_IMAGE (scripts/runner-image.sh) set. Gives up with a warning after
10# ROLLOUT_WAIT_MINUTES (default 10) rather than failing the deploy.
11set -euo pipefail
12: "${CLOUDFLARE_API_TOKEN:?}" "${CLOUDFLARE_ACCOUNT_ID:?}" "${RUNNER_IMAGE:?RUNNER_IMAGE is required (scripts/runner-image.sh)}"
13api() { curl -fsS --retry 4 --retry-all-errors --retry-delay 3 "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/containers$1" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN"; }
14deadline=$(( $(date +%s) + ${ROLLOUT_WAIT_MINUTES:-10} * 60 ))
15last=""
16while :; do
17 if ! apps=$(api /applications); then
18 echo "::warning::could not list the container applications, so the deploy doesn't wait for the rollout"
19 exit 0
20 fi
21 pending=""
22 states=""
23 for row in $(jq -r '.result[] | select(.active_rollout_id != null) | "\(.id)/\(.active_rollout_id)/\(.name)"' <<<"$apps"); do
24 IFS=/ read -r app rollout name <<<"$row"
25 r=$(api "/applications/$app/rollouts/$rollout" | jq -c .result) || continue
26 # Only the rollouts of this deploy's image (matched by its tag, a hash of the runner).
27 [[ "$(jq -r '.target_configuration.image // ""' <<<"$r")" == *":${RUNNER_IMAGE##*:}" ]] || continue
28 # Steps started out of all steps, from the steps themselves (progress.current_step is 1-based in practice).
29 state=$(jq -r '(.steps // []) as $s | "\(.status), step \([$s[] | select(.status != "pending")] | length) of \($s | length) \($s | last | .status // ""), \(.progress.updated_instances // "?")/\(.progress.total_instances // "?") instances updated"' <<<"$r")
30 states+="$name: $state; "
31 # Done once the rollout ended, or its last step is under way.
32 jq -e '(.status | IN("completed", "reverted", "replaced")) or ((.steps // []) | length > 0 and (last | .status | IN("progressing", "completed")))' <<<"$r" >/dev/null || pending+="$name "
33 done
34 [ "$states" = "$last" ] || { echo "${states:-no container rollout for $RUNNER_IMAGE}"; last=$states; }
35 if [ -z "$pending" ]; then
36 # This job's container started on the old image before the deploy; it is still running,
37 # which shows a rollout leaves running jobs alone (rollout_active_grace_period).
38 [ -z "$states" ] || echo "the rollout reached its last step and this job, on an old-image container, is still running"
39 exit 0
40 fi
41 if [ "$(date +%s)" -ge "$deadline" ]; then
42 echo "::warning::the container rollout of ${pending% } has not reached its last step after ${ROLLOUT_WAIT_MINUTES:-10} minutes; new jobs may still start on the old image"
43 exit 0
44 fi
45 sleep 10
46done