tools: archive coulomb Gitea org for read-only mirror policy
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s

Script uses Gitea API to set archived=true on all org repos after Forgejo
cutover (RAIL-HO-WP-0005). Ran 2026-07-08: 79/79 archived on coulombcore.
This commit is contained in:
codex 2026-07-08 13:11:43 +02:00
parent 5f7b63490a
commit 608cc742a5

112
tools/gitea-set-readonly.sh Executable file
View file

@ -0,0 +1,112 @@
#!/usr/bin/env bash
# Archive all coulomb org repos on coulombcore Gitea (read-only mirror policy).
# Per RAIL-HO-WP-0005 / forgejo-tier3 playbook org-wide operator action.
set -euo pipefail
CONF="${GITEA_CONF:-$HOME/.railiance_gitea.conf}"
API="${GITEA_API:-https://gitea.coulomb.social}"
ORG="${GITEA_ORG:-coulomb}"
DRY_RUN="${DRY_RUN:-0}"
LIMIT="${PAGE_LIMIT:-50}"
usage() {
cat <<'USAGE'
Usage: gitea-set-readonly.sh [repo ...]
With no args: archive every repo in org coulomb (idempotent).
With slugs: archive only those coulomb/<slug> repos.
Environment:
GITEA_CONF Token file (default: ~/.railiance_gitea.conf)
GITEA_API HTTPS API base (default: https://gitea.coulomb.social)
DRY_RUN=1 Print actions only
USAGE
}
if [[ -f "$CONF" ]]; then
# shellcheck disable=SC1090
source "$CONF"
fi
: "${GITEA_TOKEN:?Set GITEA_TOKEN or provide $CONF}"
auth=(-H "Authorization: token ${GITEA_TOKEN}")
fetch_all_repos() {
local page=1
local out=/tmp/gitea-readonly-repos.jsonl
: >"$out"
while true; do
local http body
body=$(mktemp)
http=$(curl -sS --max-time 30 -o "$body" -w '%{http_code}' \
"${auth[@]}" "${API}/api/v1/orgs/${ORG}/repos?limit=${LIMIT}&page=${page}")
if [[ "$http" != "200" ]]; then
echo "FAIL list page=${page} http=${http}" >&2
cat "$body" >&2
exit 1
fi
local count
count=$(python3 -c "import json; print(len(json.load(open('$body'))))")
python3 -c "import json; [print(r['name']) for r in json.load(open('$body'))]" >>"$out"
rm -f "$body"
[[ "$count" -lt "$LIMIT" ]] && break
page=$((page + 1))
done
sort -u "$out"
}
archive_repo() {
local name="$1"
local http archived
http=$(curl -sS --max-time 30 -o /tmp/gitea-get.json -w '%{http_code}' \
"${auth[@]}" "${API}/api/v1/repos/${ORG}/${name}")
if [[ "$http" == "404" ]]; then
echo "SKIP ${name} (not found)"
return 0
fi
if [[ "$http" != "200" ]]; then
echo "FAIL ${name} GET http=${http}" >&2
return 1
fi
archived=$(python3 -c "import json; print(json.load(open('/tmp/gitea-get.json')).get('archived', False))")
if [[ "$archived" == "True" ]]; then
echo "OK ${name} (already archived)"
return 0
fi
if [[ "$DRY_RUN" == "1" ]]; then
echo "DRY ${name} -> archived"
return 0
fi
http=$(curl -sS --max-time 30 -o /tmp/gitea-patch.json -w '%{http_code}' \
-X PATCH "${auth[@]}" -H "Content-Type: application/json" \
-d '{"archived":true}' "${API}/api/v1/repos/${ORG}/${name}")
if [[ "$http" == "200" ]]; then
echo "ARCH ${name}"
return 0
fi
echo "FAIL ${name} PATCH http=${http}" >&2
cat /tmp/gitea-patch.json >&2
return 1
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
if [[ $# -gt 0 ]]; then
mapfile -t TARGETS < <(printf '%s\n' "$@")
else
mapfile -t TARGETS < <(fetch_all_repos)
fi
echo "=== Gitea read-only: archive ${#TARGETS[@]} repo(s) in ${ORG} ==="
echo "API: ${API}"
failed=0
for name in "${TARGETS[@]}"; do
archive_repo "$name" || failed=$((failed + 1))
done
echo "=== Done (failed=${failed}) ==="
[[ "$failed" -eq 0 ]]