75 lines
2.9 KiB
Bash
75 lines
2.9 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
# Point every app repo's release webhook at the F-Droid rebuild endpoint.
|
||
|
|
#
|
||
|
|
# Forgejo webhooks are instance state, not repo content, so they cannot be
|
||
|
|
# declared in a flake and have to be registered once per repo. This does that
|
||
|
|
# from the single source of truth — apps.nix — so adding an app means editing
|
||
|
|
# that file and re-running this, not remembering four web forms.
|
||
|
|
#
|
||
|
|
# Idempotent: a hook already pointing at the same URL is updated in place
|
||
|
|
# rather than duplicated, so re-running after a secret rotation is the
|
||
|
|
# supported way to roll the secret.
|
||
|
|
#
|
||
|
|
# The secret must be the same value the serving host reads from
|
||
|
|
# webhookSecretFile; a mismatch makes every delivery fail the HMAC check and
|
||
|
|
# the repo silently stops updating. Pipe it straight out of sops so it reaches
|
||
|
|
# neither the terminal nor the shell history:
|
||
|
|
#
|
||
|
|
# <redacted>
|
||
|
|
# FORGEJO_TOKEN=<token with repo:write on each app repo> \
|
||
|
|
# FDROID_WEBHOOK_SECRET=$(<redacted>=<redacted> \
|
||
|
|
# nix run nixpkgs#sops -- -d --extract '["fdroid_webhook_secret"]' \
|
||
|
|
# <redacted>) \
|
||
|
|
# ../fdroid-repo/bin/register-webhooks.sh
|
||
|
|
#
|
||
|
|
# Usage: register-webhooks.sh [base-url]
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
cd "$(dirname "$0")/.."
|
||
|
|
|
||
|
|
: "${FORGEJO_TOKEN:?set FORGEJO_TOKEN to a Forgejo access token with repo write access}"
|
||
|
|
# Env, not an argument: an argv is visible to every other process on the box
|
||
|
|
# for as long as the script runs.
|
||
|
|
SECRET="${FDROID_WEBHOOK_SECRET:?set FDROID_WEBHOOK_SECRET to the shared webhook secret}"
|
||
|
|
BASE="${1:-https://apps.randogoth.com}"
|
||
|
|
FORGE="${FORGE:-https://code.randogoth.com}"
|
||
|
|
URL="$BASE/hooks/rebuild-fdroid"
|
||
|
|
|
||
|
|
api() {
|
||
|
|
local method=$1 path=$2
|
||
|
|
shift 2
|
||
|
|
curl -sS -X "$method" \
|
||
|
|
-H "Authorization: token $FORGEJO_TOKEN" \
|
||
|
|
-H "Content-Type: application/json" \
|
||
|
|
"$FORGE/api/v1$path" "$@"
|
||
|
|
}
|
||
|
|
|
||
|
|
for repo in $(nix eval --json --file ./apps.nix | jq -r '.[].repo'); do
|
||
|
|
# Forgejo returns the full hook list; match on the target URL because the
|
||
|
|
# hook id is assigned by the server and is not knowable in advance.
|
||
|
|
existing=$(api GET "/repos/$repo/hooks" | jq -r --arg u "$URL" \
|
||
|
|
'.[] | select(.config.url == $u) | .id' | head -1)
|
||
|
|
|
||
|
|
payload=$(jq -n --arg url "$URL" --arg secret "$SECRET" '{
|
||
|
|
type: "gitea",
|
||
|
|
active: true,
|
||
|
|
# "release" alone: a tag push fires before CI has built anything, so a
|
||
|
|
# push-triggered rebuild would always find the old release and do nothing.
|
||
|
|
events: ["release"],
|
||
|
|
config: { url: $url, content_type: "json", secret: $secret }
|
||
|
|
}')
|
||
|
|
|
||
|
|
if [ -n "$existing" ]; then
|
||
|
|
api PATCH "/repos/$repo/hooks/$existing" -d "$payload" >/dev/null
|
||
|
|
echo "updated $repo (hook $existing) -> $URL"
|
||
|
|
else
|
||
|
|
api POST "/repos/$repo/hooks" -d "$payload" >/dev/null
|
||
|
|
echo "created $repo -> $URL"
|
||
|
|
fi
|
||
|
|
done
|
||
|
|
|
||
|
|
echo
|
||
|
|
echo "Note: the release webhook fires when the release object is created,"
|
||
|
|
echo "which in these workflows is one API call before the APK is attached."
|
||
|
|
echo "fdroid-sync waits for the upload to land, so that race is handled."
|