railiance-apps/tools/forgejo-operator-bootstrap.sh
tegwick 3a9236148a
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 3s
Add Forgejo T05 verify, operator bootstrap, and security hardening
Introduce forgejo-verify acceptance checks, idempotent operator bootstrap,
runner registration SOPS capture helper, and session/security Helm values.
2026-07-07 22:09:53 +02:00

104 lines
No EOL
3.7 KiB
Bash
Executable file

#!/usr/bin/env bash
# Idempotent operator account bootstrap for Forgejo (RAIL-HO-WP-0005 T05).
# Does not commit secrets — uses API token from file or env.
set -euo pipefail
FORGEJO_API="${FORGEJO_API:-https://forgejo.coulomb.social/api/v1}"
FORGEJO_ORG="${FORGEJO_ORG:-coulomb}"
FORGEJO_OPERATOR_USER="${FORGEJO_OPERATOR_USER:-tegwick}"
FORGEJO_OPERATOR_EMAIL="${FORGEJO_OPERATOR_EMAIL:-bernd.worsch@gmail.com}"
FORGEJO_OPERATOR_SSH_KEY="${FORGEJO_OPERATOR_SSH_KEY:-$HOME/.ssh/id_gitea.pub}"
FORGEJO_OPERATOR_SSH_TITLE="${FORGEJO_OPERATOR_SSH_TITLE:-workstation-automation}"
TOKEN_FILE="${FORGEJO_TOKEN_FILE:-/tmp/forgejo-tegwick-api-token}"
if [[ -n "${FORGEJO_ADMIN_TOKEN:-}" ]]; then
TOKEN="${FORGEJO_ADMIN_TOKEN}"
elif [[ -f "${TOKEN_FILE}" ]]; then
TOKEN="$(cat "${TOKEN_FILE}")"
else
echo "Set FORGEJO_ADMIN_TOKEN or create ${TOKEN_FILE}" >&2
exit 1
fi
AUTH=(-H "Authorization: token ${TOKEN}")
user_status() {
curl -sS -o /dev/null -w '%{http_code}' "${AUTH[@]}" "${FORGEJO_API}/users/${FORGEJO_OPERATOR_USER}"
}
echo "==> Forgejo operator bootstrap: ${FORGEJO_OPERATOR_USER}"
case "$(user_status)" in
200)
echo " user exists"
;;
404)
echo " creating user ${FORGEJO_OPERATOR_USER}"
FORGEJO_OPERATOR_USER="${FORGEJO_OPERATOR_USER}" \
FORGEJO_OPERATOR_EMAIL="${FORGEJO_OPERATOR_EMAIL}" \
curl -fsS -X POST "${FORGEJO_API}/admin/users" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-d "$(python3 -c "
import json, os
print(json.dumps({
'username': os.environ['FORGEJO_OPERATOR_USER'],
'email': os.environ['FORGEJO_OPERATOR_EMAIL'],
'password': os.urandom(18).hex(),
'must_change_password': False,
'send_notify': False,
}))
")" >/dev/null
;;
*)
echo "Unexpected user lookup status for ${FORGEJO_OPERATOR_USER}" >&2
exit 1
;;
esac
# Promote to site admin
curl -fsS -X PATCH "${FORGEJO_API}/admin/users/${FORGEJO_OPERATOR_USER}" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-d '{"admin":true,"prohibit_login":false}' >/dev/null
echo " admin=true"
# Org Owners team membership
team_id="$(curl -fsS "${AUTH[@]}" "${FORGEJO_API}/orgs/${FORGEJO_ORG}/teams" \
| python3 -c "import sys,json; teams=json.load(sys.stdin); print(next((t['id'] for t in teams if t.get('name')=='Owners'), ''))")"
if [[ -n "${team_id}" ]]; then
curl -fsS -X PUT "${AUTH[@]}" \
"${FORGEJO_API}/teams/${team_id}/members/${FORGEJO_OPERATOR_USER}" >/dev/null 2>&1 \
&& echo " org ${FORGEJO_ORG} Owners team" \
|| echo " (already in Owners team or API skipped)"
fi
# SSH key for git+ssh
if [[ ! -f "${FORGEJO_OPERATOR_SSH_KEY}" ]]; then
echo "Missing SSH public key: ${FORGEJO_OPERATOR_SSH_KEY}" >&2
exit 1
fi
existing="$(curl -fsS "${AUTH[@]}" "${FORGEJO_API}/users/${FORGEJO_OPERATOR_USER}/keys" \
| FORGEJO_OPERATOR_SSH_KEY="${FORGEJO_OPERATOR_SSH_KEY}" python3 -c "
import json, os, sys
want = open(os.environ['FORGEJO_OPERATOR_SSH_KEY']).read().strip()
keys = json.load(sys.stdin)
print(any((k.get('key') or '').strip() == want for k in keys))
")"
if [[ "${existing}" == "True" ]]; then
echo " SSH key already present"
else
FORGEJO_OPERATOR_SSH_TITLE="${FORGEJO_OPERATOR_SSH_TITLE}" \
FORGEJO_OPERATOR_SSH_KEY="${FORGEJO_OPERATOR_SSH_KEY}" \
curl -fsS -X POST "${FORGEJO_API}/admin/users/${FORGEJO_OPERATOR_USER}/keys" "${AUTH[@]}" \
-H "Content-Type: application/json" \
-d "$(python3 -c "
import json, os
print(json.dumps({
'title': os.environ['FORGEJO_OPERATOR_SSH_TITLE'],
'key': open(os.environ['FORGEJO_OPERATOR_SSH_KEY']).read().strip(),
}))
")" >/dev/null
echo " SSH key added (${FORGEJO_OPERATOR_SSH_TITLE})"
fi
echo "==> Operator bootstrap complete: ${FORGEJO_OPERATOR_USER}"
echo " Test: ssh forgejo-remote # expect Hi there, ${FORGEJO_OPERATOR_USER}!"