From 37504abcb279bbd480e23606a11b0d14bd76cb15 Mon Sep 17 00:00:00 2001 From: Anton Afanasyeu Date: Wed, 10 Jun 2026 22:18:28 +0200 Subject: [PATCH] gitea pain --- .../backend/scripts/gitea/README.md | 40 +++- .../fe-android_cast-post-receive.example | 2 +- .../scripts/gitea/gitea_attach_remote.sh | 16 +- .../scripts/gitea/gitea_diagnose_mirrors.sh | 4 +- .../scripts/gitea/gitea_migrate_org_setup.sh | 41 ++++ .../scripts/gitea/gitea_migrate_project.sh | 26 ++- .../backend/scripts/gitea/gitea_mirror_lib.sh | 8 +- .../scripts/gitea/gitea_mirror_submodules.sh | 10 +- .../backend/scripts/gitea/gitea_org_lib.sh | 206 ++++++++++++++++++ .../gitea/gitea_submodule_local_rewrite.sh | 2 +- .../scripts/gitea/gitea_sync_mirror.sh | 2 +- 11 files changed, 338 insertions(+), 19 deletions(-) create mode 100755 examples/crash_reporter/backend/scripts/gitea/gitea_migrate_org_setup.sh create mode 100644 examples/crash_reporter/backend/scripts/gitea/gitea_org_lib.sh diff --git a/examples/crash_reporter/backend/scripts/gitea/README.md b/examples/crash_reporter/backend/scripts/gitea/README.md index 93e02a6..64a17ef 100644 --- a/examples/crash_reporter/backend/scripts/gitea/README.md +++ b/examples/crash_reporter/backend/scripts/gitea/README.md @@ -154,6 +154,44 @@ DEFAULT_MIRROR_INTERVAL = 10m MIRROR_UPDATE_INTERVAL = 10m ``` +## AndroidCast organization (team repos) + +Project mirrors live under the **`AndroidCast`** org (not personal `admin/`): + +`https://apps.f0xx.org/app/androidcast_project/git/AndroidCast` + +| Role | Who | Access | +|------|-----|--------| +| **Site admin** | Gitea user `admin` | Instance users/settings + org **Owners** | +| **Org Owners** | `admin`, `foxx` | Full control of all `AndroidCast/*` repos | +| **Future members** | Add to org teams in UI | Read/write per team (create e.g. Developers later) | + +**One-time org setup** (idempotent; org already created in UI is fine): + +```bash +sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_migrate_org_setup.sh +``` + +This will: + +- Ensure org **AndroidCast** exists (skip create if you made it in UI) +- Create Gitea user **`foxx`** if missing (`--must-change-password` — set password or SSH key in UI later) +- Add **`admin`** and **`foxx`** to org **Owners** team (org-admin on all repos) +- **Transfer** existing `admin/android_cast`, `admin/libvpx`, … → `AndroidCast/*` when present + +Then migrate mirrors under the org: + +```bash +cd /var/www/.../androidcast_project/android_cast +GITEA_OWNER=AndroidCast sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_migrate_project.sh . +``` + +Defaults: `GITEA_OWNER=AndroidCast`, `GITEA_ORG_OWNERS="admin foxx"`, `GITEA_TRANSFER_FROM=admin`. + +Clone URLs become: + +`https://apps.f0xx.org/app/androidcast_project/git/AndroidCast/android_cast.git` + ## Full project migration (main + external submodules) **`gitea_migrate_project.sh`** — one script for BE: @@ -192,7 +230,7 @@ sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_migrate_project.sh . sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh .gitmodules ``` -Expect **4 repos** under `admin`: `android_cast`, `libvpx`, `opus`, `speex`. If migrate summary shows `submodules ok=0` and a WARN about empty discovery, `.gitmodules` is missing on the BE checkout — the updated migrate script falls back to `submodules.defaults.conf`. +Expect **4 repos** under **`AndroidCast`**: `android_cast`, `libvpx`, `opus`, `speex`. If migrate summary shows `submodules ok=0` and a WARN about empty discovery, `.gitmodules` is missing on the BE checkout — the updated migrate script falls back to `submodules.defaults.conf`. In Gitea UI, use **Explore** or **Site Administration → Repositories** if the profile counter hides mirror repos. diff --git a/examples/crash_reporter/backend/scripts/gitea/fe-android_cast-post-receive.example b/examples/crash_reporter/backend/scripts/gitea/fe-android_cast-post-receive.example index a0bdd7b..7833a8d 100644 --- a/examples/crash_reporter/backend/scripts/gitea/fe-android_cast-post-receive.example +++ b/examples/crash_reporter/backend/scripts/gitea/fe-android_cast-post-receive.example @@ -13,7 +13,7 @@ # --- B) curl Gitea API on BE loopback via FE→BE nginx/internal URL --- # Create a long-lived token in Gitea UI (admin, scope: write repository). -# GITEA_SYNC_CMD='curl -fsS -X POST -H "Authorization: token YOUR_TOKEN" http://artc0.intra.raptor.org:3000/api/v1/repos/admin/android_cast/mirror-sync' +# GITEA_SYNC_CMD='curl -fsS -X POST -H "Authorization: token YOUR_TOKEN" http://artc0.intra.raptor.org:3000/api/v1/repos/AndroidCast/android_cast/mirror-sync' # --- C) curl public apps URL (only if you add a protected internal endpoint) --- # GITEA_SYNC_CMD='curl -fsS -X POST -H "Authorization: Bearer ..." https://apps.f0xx.org/.../gitea_mirror_sync' diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh index 442d426..1c77a10 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh @@ -20,9 +20,13 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=gitea_ini.sh . "$SCRIPT_DIR/gitea_ini.sh" +# shellcheck source=gitea_mirror_lib.sh +. "$SCRIPT_DIR/gitea_mirror_lib.sh" +# shellcheck source=gitea_org_lib.sh +. "$SCRIPT_DIR/gitea_org_lib.sh" GITEA_MIRROR_URL="${GITEA_MIRROR_URL:-git://f0xx.org/android_cast}" -GITEA_OWNER="${GITEA_OWNER:-admin}" +GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}" GITEA_REPO="${GITEA_REPO:-android_cast}" GITEA_MIRROR_INTERVAL="${GITEA_MIRROR_INTERVAL:-10m}" GITEA_PRIVATE="${GITEA_PRIVATE:-false}" @@ -80,11 +84,15 @@ test_remote_reachable() { } resolve_gitea_owner() { - if gitea_user_exists "$GITEA_OWNER"; then - log "Gitea owner user: $GITEA_OWNER" + if [ "${GITEA_SKIP_ORG_SETUP:-0}" -eq 0 ]; then + gitea_org_ensure_androidcast "$GITEA_ORG" 2>/dev/null || true + GITEA_OWNER="${GITEA_OWNER:-$GITEA_ORG}" + fi + if gitea_owner_exists "$GITEA_OWNER"; then + log "Gitea repo owner: $GITEA_OWNER" return 0 fi - warn "Gitea has no user '$GITEA_OWNER' (not Linux/FE login — Gitea accounts are separate)" + warn "Gitea has no owner '$GITEA_OWNER' (user or org — not Linux/FE login)" log "Gitea users on this instance:" gitea_list_usernames | while read -r _u; do [ -n "$_u" ] && log " - $_u" diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirrors.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirrors.sh index 2737dad..8345248 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirrors.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirrors.sh @@ -17,7 +17,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=gitea_submodule_lib.sh . "$SCRIPT_DIR/gitea_submodule_lib.sh" -GITEA_OWNER="${GITEA_OWNER:-admin}" +GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}" GITEA_REF="${GITEA_REF:-HEAD}" GITEA_SUBMODULES_CONF="${GITEA_SUBMODULES_CONF:-$SCRIPT_DIR/submodules.defaults.conf}" @@ -92,7 +92,7 @@ fi cat </dev/null | while read -r _r; do + [ -n "$_r" ] && gitea_org_log " $GITEA_ORG/$_r" +done + +cat </dev/null || echo 0)" - log "total listed: $_count (expect android_cast + libvpx + opus + speex)" + log "total listed: $_count (expect android_cast + libvpx + opus + speex under $GITEA_ORG)" } print_fast_sync_guide() { @@ -212,6 +215,13 @@ while [ $# -gt 0 ]; do ;; --skip-main) GITEA_SKIP_MAIN=1; shift ;; --skip-submodules) GITEA_SKIP_SUBMODULES=1; shift ;; + --skip-org) GITEA_SKIP_ORG_SETUP=1; shift ;; + --org-setup-only) + gitea_load_config || exit 1 + gitea_ensure_cli_access || exit 1 + gitea_org_ensure_androidcast "$GITEA_ORG" || exit 1 + exit 0 + ;; --ref) shift GITEA_REF="${1:-HEAD}" @@ -230,10 +240,16 @@ done gitea_load_config || exit 1 gitea_ensure_cli_access || exit 1 -if ! gitea_user_exists "$GITEA_OWNER"; then +if [ "$GITEA_SKIP_ORG_SETUP" -eq 0 ]; then + log "org setup: $GITEA_ORG (owners: $GITEA_ORG_OWNERS)" + gitea_org_ensure_androidcast "$GITEA_ORG" || warn "org setup incomplete — continue with owner=$GITEA_OWNER" + GITEA_OWNER="${GITEA_OWNER:-$GITEA_ORG}" +fi + +if ! gitea_owner_exists "$GITEA_OWNER"; then _fb="$(gitea_pick_owner_username)" - [ -n "$_fb" ] || die "no Gitea user; set GITEA_OWNER=admin" - warn "GITEA_OWNER=$GITEA_OWNER not found; using $_fb" + [ -n "$_fb" ] || die "no Gitea owner; set GITEA_OWNER=$GITEA_ORG or create org in UI" + warn "owner $GITEA_OWNER not found; using $_fb" GITEA_OWNER="$_fb" fi diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh index 0672c1f..269588a 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh @@ -161,7 +161,13 @@ gitea_list_repos_api() { _token="${GITEA_ADMIN_TOKEN:-}" [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1 _api="$(gitea_api_base)" - curl -sS "$_api/users/$_owner/repos?limit=50" \ + _list_url="$_api/users/$_owner/repos?limit=50" + _http="$(curl -sS -o /dev/null -w '%{http_code}' "$_api/orgs/$_owner" \ + -H "Authorization: token $_token")" + if [ "$_http" = "200" ]; then + _list_url="$_api/orgs/$_owner/repos?limit=50" + fi + curl -sS "$_list_url" \ -H "Authorization: token $_token" \ | awk -F'"' '/"name":/ { print $4 }' } diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh index 84dfc03..54647cd 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh @@ -19,8 +19,10 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" . "$SCRIPT_DIR/gitea_ini.sh" # shellcheck source=gitea_mirror_lib.sh . "$SCRIPT_DIR/gitea_mirror_lib.sh" +# shellcheck source=gitea_org_lib.sh +. "$SCRIPT_DIR/gitea_org_lib.sh" -GITEA_OWNER="${GITEA_OWNER:-admin}" +GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}" GITEA_MIRROR_INTERVAL="${GITEA_MIRROR_INTERVAL:-24h}" GITEA_PRIVATE="${GITEA_PRIVATE:-false}" GITEA_SUBMODULES_CONF="${GITEA_SUBMODULES_CONF:-$SCRIPT_DIR/submodules.defaults.conf}" @@ -148,9 +150,11 @@ if [ $# -ge 1 ] && [ -n "$1" ]; then esac fi -if ! gitea_user_exists "$GITEA_OWNER"; then +gitea_org_ensure_androidcast "$GITEA_ORG" 2>/dev/null || true +GITEA_OWNER="${GITEA_OWNER:-$GITEA_ORG}" +if ! gitea_owner_exists "$GITEA_OWNER"; then _fb="$(gitea_pick_owner_username)" - [ -n "$_fb" ] || die "no Gitea user; set GITEA_OWNER=admin (or create admin in UI)" + [ -n "$_fb" ] || die "no Gitea owner; set GITEA_OWNER=$GITEA_ORG" warn "GITEA_OWNER=$GITEA_OWNER not found; using $_fb" GITEA_OWNER="$_fb" fi diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_org_lib.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_org_lib.sh new file mode 100644 index 0000000..2193c18 --- /dev/null +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_org_lib.sh @@ -0,0 +1,206 @@ +#!/bin/ash +# Gitea organization, team, and user helpers (AndroidCast team layout). +# shellcheck shell=sh +# Requires: . gitea_ini.sh && gitea_load_config && . gitea_mirror_lib.sh + +GITEA_ORG="${GITEA_ORG:-AndroidCast}" +GITEA_ORG_FULL_NAME="${GITEA_ORG_FULL_NAME:-Android Cast}" +GITEA_ORG_VISIBILITY="${GITEA_ORG_VISIBILITY:-public}" +# Space-separated Gitea logins with org Owner (admin) team membership. +GITEA_ORG_OWNERS="${GITEA_ORG_OWNERS:-admin foxx}" +# Transfer mirrors from a personal account when re-homing under the org. +GITEA_TRANSFER_FROM="${GITEA_TRANSFER_FROM:-admin}" + +gitea_org_log() { printf '[gitea-org] %s\n' "$*"; } +gitea_org_warn() { printf '[gitea-org] WARN: %s\n' "$*" >&2; } + +gitea_api_token() { + _token="${GITEA_ADMIN_TOKEN:-}" + [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1 + printf '%s' "$_token" +} + +gitea_org_exists() { + _org="$1" + command -v curl >/dev/null 2>&1 || return 1 + _token="$(gitea_api_token)" || return 1 + _api="$(gitea_api_base)" + _http="$(curl -sS -o /dev/null -w '%{http_code}' \ + "$_api/orgs/$_org" \ + -H "Authorization: token $_token")" + [ "$_http" = "200" ] +} + +gitea_owner_exists() { + _name="$1" + gitea_org_exists "$_name" && return 0 + gitea_user_exists "$_name" +} + +gitea_org_create_api() { + _org="$1" + _full="${2:-$GITEA_ORG_FULL_NAME}" + command -v curl >/dev/null 2>&1 || return 1 + _token="$(gitea_api_token)" || return 1 + _api="$(gitea_api_base)" + _payload=$(cat </dev/null; then + return 0 + fi + if gitea_cli "admin" "user" "create" \ + "--username" "$_user" \ + "--email" "$_email" \ + "--random-password" \ + "--must-change-password" 2>/dev/null; then + return 0 + fi + gitea_org_warn "could not create user $_user via CLI — create in Gitea UI" + return 1 +} + +gitea_org_owners_team_id() { + _org="$1" + command -v curl >/dev/null 2>&1 || return 1 + _token="$(gitea_api_token)" || return 1 + _api="$(gitea_api_base)" + _id="$(curl -sS "$_api/orgs/$_org/teams" \ + -H "Authorization: token $_token" \ + | awk -F'"' ' + /"id":/ { id = $4 } + /"name": "Owners"/ { print id; exit } + /"name":"Owners"/ { print id; exit } + ')" + [ -n "$_id" ] || return 1 + printf '%s' "$_id" +} + +gitea_team_add_member_api() { + _team_id="$1" + _user="$2" + command -v curl >/dev/null 2>&1 || return 1 + _token="$(gitea_api_token)" || return 1 + _api="$(gitea_api_base)" + _http="$(curl -sS -o /dev/null -w '%{http_code}' \ + -X PUT "$_api/teams/$_team_id/members/$_user" \ + -H "Authorization: token $_token")" + case "$_http" in + 204|200|201) return 0 ;; + 422) return 0 ;; + *) return 1 ;; + esac +} + +gitea_org_add_owners() { + _org="$1" + _team_id="$(gitea_org_owners_team_id "$_org")" || { + gitea_org_warn "Owners team not found for org $_org" + return 1 + } + for _u in $GITEA_ORG_OWNERS; do + [ -n "$_u" ] || continue + gitea_user_create_if_missing "$_u" || true + if gitea_user_exists "$_u"; then + if gitea_team_add_member_api "$_team_id" "$_u"; then + gitea_org_log "Owners team: $_u" + else + gitea_org_warn "could not add $_u to Owners (may already be member)" + fi + fi + done + return 0 +} + +gitea_transfer_repo_api() { + _from="$1" + _repo="$2" + _to="$3" + command -v curl >/dev/null 2>&1 || return 1 + gitea_repo_exists_api "$_from" "$_repo" || return 1 + _token="$(gitea_api_token)" || return 1 + _api="$(gitea_api_base)" + _payload="{\"new_owner\":\"$_to\",\"team_ids\":[]}" + _http="$(curl -sS -o /tmp/gitea-transfer-$$.json -w '%{http_code}' \ + -X POST "$_api/repos/$_from/$_repo/transfer" \ + -H "Authorization: token $_token" \ + -H "Content-Type: application/json" \ + -d "$_payload")" + case "$_http" in + 200|201|202) return 0 ;; + 409|422) + gitea_repo_exists_api "$_to" "$_repo" && return 0 + return 1 + ;; + *) return 1 ;; + esac +} + +# Move known project mirrors from GITEA_TRANSFER_FROM → org (no-op if absent). +gitea_org_transfer_legacy_repos() { + _org="$1" + _from="${2:-$GITEA_TRANSFER_FROM}" + [ "$_from" = "$_org" ] && return 0 + for _repo in android_cast libvpx opus speex wireguard-android; do + if gitea_repo_exists_api "$_from" "$_repo"; then + gitea_org_log "transfer $_from/$_repo -> $_org/$_repo" + gitea_transfer_repo_api "$_from" "$_repo" "$_org" \ + && gitea_org_log " transferred" \ + || gitea_org_warn " transfer failed" + fi + done +} + +# Org + Owners team + foxx/admin. Idempotent. +gitea_org_ensure_androidcast() { + _org="${1:-$GITEA_ORG}" + + if gitea_org_exists "$_org"; then + gitea_org_log "org exists: $_org" + else + gitea_org_log "creating org $_org" + gitea_org_create_api "$_org" || { + gitea_org_warn "org create failed — create $_org in Gitea UI first" + return 1 + } + fi + + gitea_org_add_owners "$_org" + gitea_org_transfer_legacy_repos "$_org" "$GITEA_TRANSFER_FROM" + return 0 +} diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_submodule_local_rewrite.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_submodule_local_rewrite.sh index 3a513c7..b104a5d 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_submodule_local_rewrite.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_submodule_local_rewrite.sh @@ -12,7 +12,7 @@ set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" -GITEA_OWNER="${GITEA_OWNER:-admin}" +GITEA_OWNER="${GITEA_OWNER:-AndroidCast}" GITEA_ROOT_URL="${GITEA_ROOT_URL:-https://apps.f0xx.org/app/androidcast_project/git}" GITEA_SUBMODULES_CONF="${GITEA_SUBMODULES_CONF:-$SCRIPT_DIR/submodules.defaults.conf}" diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_sync_mirror.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_sync_mirror.sh index 05a6502..cbf9f32 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_sync_mirror.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_sync_mirror.sh @@ -15,7 +15,7 @@ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" # shellcheck source=gitea_mirror_lib.sh . "$SCRIPT_DIR/gitea_mirror_lib.sh" -GITEA_OWNER="${GITEA_OWNER:-admin}" +GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}" log() { printf '[gitea-sync] %s\n' "$*"; } warn() { printf '[gitea-sync] WARN: %s\n' "$*" >&2; }