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.

75 lines · 4.6 KB · executableRaw History
1#!/usr/bin/env bash
2# Prints the BOOTSTRAP_ADMINS list this deploy gives the Worker (the only thing it prints on stdout).
3# Run by scripts/bootstrap.sh, which passes it to scripts/render-config.mjs as --admins.
4#
5# The emails in the list become site administrators when their account is created, or when an
6# account still waiting for an invite signs in through /auth/access (src/lib/directory.ts). Admin
7# status is then kept in D1, so the list only matters for accounts that don't exist yet.
8#
9# Where the list comes from, first match wins:
10# 1. BOOTSTRAP_ADMINS in the environment, when it isn't empty (scripts/setup.sh asks for it).
11# 2. BOOTSTRAP_ADMINS under vars in apps/diffinery/wrangler.jsonc, when it isn't empty (a copy
12# that keeps its list in the file). The repository leaves it empty.
13# 3. The list the deployed Worker has now, read from its plain-text vars:
14# GET /accounts/{account_id}/workers/scripts/diffinery/settings
15# https://developers.cloudflare.com/api/resources/workers/subresources/scripts/subresources/script_and_version_settings/methods/get/
16# so a deploy that doesn't set it (the Deploy workflow, a re-run of setup.sh) keeps the admins it had.
17# 4. None: a brand-new instance without a list. It warns loudly: nobody becomes a site
18# administrator until a deploy sets the list.
19# If the Worker's settings can't be read (other than "no such Worker"), it stops instead of
20# deploying an empty list in place of the one the Worker has. The emails are never printed to
21# the log, only how many there are and where they came from.
22#
23# Requires: node; and CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID, curl and jq for step 3.
24set -euo pipefail
25
26ROOT="$(cd "$(dirname "$0")/.." && pwd)"
27WORKER=diffinery
28say() { echo "$*" >&2; } # stdout carries only the list
29warn() { echo "::warning::$*" >&2; }
30count() { tr ',' '\n' <<<"$1" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep -c . || true; }
31emit() { # emit LIST SOURCE
32 say "BOOTSTRAP_ADMINS: $(count "$1") address(es), from $2"
33 printf '%s\n' "$1"
34 exit 0
35}
36none() {
37 warn "BOOTSTRAP_ADMINS is empty: $1, so nobody becomes a site administrator on first sign-in. Set BOOTSTRAP_ADMINS (comma-separated emails) and deploy again, for example: BOOTSTRAP_ADMINS=you@example.com scripts/setup.sh"
38 printf '\n'
39 exit 0
40}
41trimmed() { sed 's/^[[:space:]]*//;s/[[:space:]]*$//' <<<"$1"; }
42
43# 1. The environment.
44FROM_ENV="$(trimmed "${BOOTSTRAP_ADMINS:-}")"
45[[ -z "$FROM_ENV" ]] || emit "$FROM_ENV" "the BOOTSTRAP_ADMINS environment variable"
46
47# 2. wrangler.jsonc.
48FROM_FILE="$(node --input-type=module -e '
49 import { readFileSync } from "node:fs";
50 import { pathToFileURL } from "node:url";
51 const { parseJsonc } = await import(pathToFileURL(process.argv[1]).href);
52 const cfg = parseJsonc(readFileSync(process.argv[2], "utf8"));
53 process.stdout.write(String(cfg.vars?.BOOTSTRAP_ADMINS ?? ""));
54' "$ROOT/scripts/jsonc.mjs" "$ROOT/apps/diffinery/wrangler.jsonc")"
55FROM_FILE="$(trimmed "$FROM_FILE")"
56[[ -z "$FROM_FILE" ]] || emit "$FROM_FILE" "apps/diffinery/wrangler.jsonc"
57
58# 3. The deployed Worker.
59if [[ -z "${CLOUDFLARE_API_TOKEN:-}" || -z "${CLOUDFLARE_ACCOUNT_ID:-}" ]]; then
60 none "it isn't in the environment or wrangler.jsonc, and without CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID the deployed Worker's list can't be read"
61fi
62SETTINGS="$(curl -sS --retry 4 --retry-all-errors --retry-delay 3 "https://api.cloudflare.com/client/v4/accounts/$CLOUDFLARE_ACCOUNT_ID/workers/scripts/$WORKER/settings" -H "Authorization: Bearer $CLOUDFLARE_API_TOKEN" || true)"
63if jq -e '.success == true' >/dev/null 2>&1 <<<"$SETTINGS"; then
64 LIVE="$(jq -r '[.result.bindings[]? | select(.type == "plain_text" and .name == "BOOTSTRAP_ADMINS") | .text][0] // empty' <<<"$SETTINGS")"
65 LIVE="$(trimmed "$LIVE")"
66 [[ -z "$LIVE" ]] || emit "$LIVE" "the deployed $WORKER Worker (kept as it is)"
67 none "it isn't in the environment, wrangler.jsonc or the deployed $WORKER Worker"
68fi
69if jq -e 'any(.errors[]?; .code == 10007)' >/dev/null 2>&1 <<<"$SETTINGS"; then
70 # 10007: no such Worker yet (the first deploy; wrangler's WORKER_NOT_FOUND_ERR_CODE).
71 none "it isn't in the environment or wrangler.jsonc, and the $WORKER Worker isn't deployed yet"
72fi
73ERRORS="$(jq -r '[.errors[]? | "\(.code): \(.message)"] | join("; ")' <<<"$SETTINGS" 2>/dev/null || true)"
74echo "::error::Could not read the $WORKER Worker's settings to keep its BOOTSTRAP_ADMINS, so the deploy stops rather than replace the list with an empty one. The token needs Account → Workers Scripts → Edit. Or set BOOTSTRAP_ADMINS for this deploy. Cloudflare said: ${ERRORS:-no answer}" >&2
75exit 1