burrow/Scripts/forgejo-dispatch-via-host.sh
Conrad Kramer 002bd382e9
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s
Wire Forge-native release infrastructure
2026-06-07 05:51:12 -07:00

205 lines
4.9 KiB
Bash
Executable file

#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage: Scripts/forgejo-dispatch-via-host.sh <workflow> [--ref <ref>] [--inputs <json>] [--inputs-file <path>] [--return-run-info]
Dispatch a Burrow Forgejo workflow by SSHing to the live forge host and using
the managed dispatcher token from the host runtime.
Environment:
FORGEJO_BASE_URL Default: https://git.burrow.net
FORGEJO_REPO Default: hackclub/burrow
BURROW_FORGE_HOST Default: root@git.burrow.net
BURROW_FORGE_HOST_ADDRESS Optional SSH HostName override
BURROW_FORGE_SSH_KEY Optional explicit SSH private key
AGE_FORGE_SSH_KEY Optional injected SSH private key material
BURROW_FORGE_DISPATCHER_CONFIG_FILE Default: /run/agenix/burrowForgejoNscDispatcherConfig
EOF
}
base_url="${FORGEJO_BASE_URL:-https://git.burrow.net}"
repo_slug="${FORGEJO_REPO:-hackclub/burrow}"
forge_host="${BURROW_FORGE_HOST:-root@git.burrow.net}"
forge_host_address="${BURROW_FORGE_HOST_ADDRESS:-}"
dispatcher_config_file="${BURROW_FORGE_DISPATCHER_CONFIG_FILE:-/run/agenix/burrowForgejoNscDispatcherConfig}"
workflow=""
ref="main"
inputs_json=""
return_run_info="false"
while [[ $# -gt 0 ]]; do
case "$1" in
-h|--help)
usage
exit 0
;;
--ref)
ref="${2:-}"
shift 2
;;
--inputs)
inputs_json="${2:-}"
shift 2
;;
--inputs-file)
inputs_json="$(cat "${2:-}")"
shift 2
;;
--return-run-info)
return_run_info="true"
shift
;;
*)
if [[ -z "$workflow" ]]; then
workflow="$1"
shift
else
echo "Unexpected argument: $1" >&2
usage >&2
exit 64
fi
;;
esac
done
if [[ -z "$workflow" ]]; then
echo "Missing workflow name or file, for example build-release.yml." >&2
usage >&2
exit 64
fi
script_dir="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
resolved_forge_ssh_key="$("${script_dir}/resolve-forge-ssh-key.sh")"
eval "$resolved_forge_ssh_key"
ssh_key="${BURROW_FORGE_SSH_KEY}"
if [[ -n "$inputs_json" ]]; then
if ! python3 - "$inputs_json" <<'PY' >/dev/null
import json
import sys
value = json.loads(sys.argv[1])
if not isinstance(value, dict):
raise SystemExit(1)
PY
then
echo "--inputs must be a JSON object." >&2
exit 64
fi
fi
workflow_path="$(python3 - "$workflow" <<'PY'
import sys
import urllib.parse
print(urllib.parse.quote(sys.argv[1], safe=""))
PY
)"
payload="$(python3 - "$ref" "$inputs_json" "$return_run_info" <<'PY'
import json
import sys
ref = sys.argv[1]
inputs_arg = sys.argv[2]
return_run_info = sys.argv[3] == "true"
payload = {"ref": ref}
if inputs_arg:
inputs = json.loads(inputs_arg)
payload["inputs"] = {key: str(value) for key, value in inputs.items()}
if return_run_info:
payload["return_run_info"] = True
print(json.dumps(payload))
PY
)"
payload_b64="$(python3 - "$payload" <<'PY'
import base64
import sys
print(base64.b64encode(sys.argv[1].encode()).decode())
PY
)"
ssh_opts=(
-i "$ssh_key"
-o IdentitiesOnly=yes
-o UserKnownHostsFile=/dev/null
-o GlobalKnownHostsFile=/dev/null
-o StrictHostKeyChecking=accept-new
)
if [[ -n "$forge_host_address" ]]; then
ssh_opts+=(-o "HostName=$forge_host_address")
fi
response="$(
ssh "${ssh_opts[@]}" "$forge_host" bash -s -- \
"$base_url" "$repo_slug" "$workflow_path" "$dispatcher_config_file" "$payload_b64" <<'EOF'
set -euo pipefail
base_url="$1"
repo_slug="$2"
workflow_path="$3"
dispatcher_config_file="$4"
payload_b64="$5"
payload="$(python3 - "$payload_b64" <<'PY'
import base64
import sys
print(base64.b64decode(sys.argv[1]).decode())
PY
)"
if [[ ! -s "$dispatcher_config_file" ]]; then
echo "Forge dispatcher config is missing or empty: $dispatcher_config_file" >&2
exit 1
fi
runtime_token="$(
awk '
/^[A-Za-z0-9_-]+:/ { in_forgejo = ($0 ~ /^forgejo:/); next }
in_forgejo && /^[[:space:]]+token:/ {
sub(/^[[:space:]]+token:[[:space:]]*/, "")
gsub(/^"/, "")
gsub(/"$/, "")
gsub(/^\047/, "")
gsub(/\047$/, "")
print
exit
}
' "$dispatcher_config_file" | tr -d "\r\n"
)"
if [[ -z "$runtime_token" ]]; then
echo "Forge dispatcher token not found in $dispatcher_config_file" >&2
exit 1
fi
url="${base_url%/}/api/v1/repos/${repo_slug}/actions/workflows/${workflow_path}/dispatches"
response_file="$(mktemp)"
http_code="$(
curl -sS -o "$response_file" -w '%{http_code}' -X POST \
-H "Authorization: token ${runtime_token}" \
-H "Content-Type: application/json" \
--data-binary "$payload" \
"$url"
)"
if [[ "$http_code" != 2* ]]; then
cat "$response_file" >&2
rm -f "$response_file"
exit 22
fi
cat "$response_file"
rm -f "$response_file"
EOF
)"
if [[ "$return_run_info" == "true" ]]; then
printf '%s\n' "$response"
else
echo "Dispatched ${workflow} on ${ref} via ${forge_host}."
fi