diff --git a/examples/crash_reporter/backend/scripts/gitea/README.md b/examples/crash_reporter/backend/scripts/gitea/README.md index d36bf15..ad72210 100644 --- a/examples/crash_reporter/backend/scripts/gitea/README.md +++ b/examples/crash_reporter/backend/scripts/gitea/README.md @@ -345,6 +345,80 @@ curl -sS -o /dev/null -w '%{http_code}\n' \ git ls-remote "https://apps.f0xx.org/app/androidcast_project/git/foxx/android_cast.git" HEAD ``` +## Troubleshooting + +### 1) Mirror synced once, then nothing (1h+ stale) + +Common cause: repo was created via **`git push --mirror` fallback**, not API **pull mirror** — Gitea shows the repo but **`mirror: false`** and cron never pulls again. + +```bash +sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirror_sync.sh +``` + +If `api mirror=false`: + +1. Delete `AndroidCast/android_cast` in Gitea (mirror copy only). +2. Re-attach with API migrate (must succeed): + +```bash +GITEA_OWNER=AndroidCast sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh git://10.7.0.10/android_cast +``` + +Ensure `app.ini`: + +```ini +[cron.update_mirrors] +ENABLED = true +SCHEDULE = @every 10m +RUN_AT_START = true + +[migrations] +ALLOW_LOCALNETWORKS = true +``` + +Manual sync / stuck queue: + +```bash +sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_sync_mirror.sh android_cast +# Admin UI -> Monitor -> Queues -> Remove All (if log says "already in queue") +sudo rc-service gitea restart +``` + +### 2) Default branch `next` in UI + +Per-repo (Gitea UI): **Settings → Repository → Default Branch → `next`**. + +Or script (all repos under org): + +```bash +GITEA_OWNER=AndroidCast GITEA_DEFAULT_BRANCH=next \ + sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_set_default_branch.sh +``` + +Run after mirror has fetched `next` from upstream. Each developer can still pick another branch locally; this only changes the **web UI default**. + +### 3) Register says “user exists”, login fails (`foxx`) + +`gitea_migrate_org_setup.sh` **pre-creates** `foxx` — do **not** use **Register**. The account has an admin-set random password (`must-change-password`). + +**Site admin** sets password: + +```bash +sudo ./examples/crash_reporter/backend/scripts/gitea/gitea_user_admin.sh reset-password foxx 'choose-a-password' +``` + +Or Gitea UI: **Site Administration → Users → foxx → Edit**. + +Then **Sign In** (not Register). Add SSH key under **Settings → SSH / GPG Keys**. + +If self-registration is intended instead: delete `foxx` in admin UI and use **Register** once, or set in `app.ini`: + +```ini +[service] +DISABLE_REGISTRATION = false +REGISTER_EMAIL_CONFIRM = false +``` + ## Related docs - [docs/INFRA.md](../../../../docs/INFRA.md) — `git://f0xx.org/android_cast`, disk layout diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirror_sync.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirror_sync.sh new file mode 100755 index 0000000..14d0be5 --- /dev/null +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_diagnose_mirror_sync.sh @@ -0,0 +1,120 @@ +#!/bin/ash +# +# Why Gitea pull mirrors stopped syncing (one-time migrate vs cron vs queue). +# +# Usage: +# sudo ./gitea_diagnose_mirror_sync.sh +# GITEA_OWNER=AndroidCast GITEA_REPO=android_cast sudo ./gitea_diagnose_mirror_sync.sh +# +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" + +GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}" +GITEA_REPO="${GITEA_REPO:-android_cast}" +GITEA_MIRROR_URL="${GITEA_MIRROR_URL:-git://10.7.0.10/android_cast}" + +log() { printf '[gitea-mirror-diag] %s\n' "$*"; } +warn() { printf '[gitea-mirror-diag] WARN: %s\n' "$*" >&2; } + +json_bool() { + _json="$1" + _key="$2" + printf '%s' "$_json" | tr -d '\n' | grep -o "\"$_key\":[^,}]*" | head -1 | sed 's/.*://;s/ //g' +} + +json_str() { + _json="$1" + _key="$2" + printf '%s' "$_json" | tr -d '\n' | grep -o "\"$_key\":\"[^\"]*\"" | head -1 | sed 's/.*:"//;s/"$//' +} + +gitea_load_config || exit 1 +gitea_ensure_cli_access || exit 1 + +log "ini=$GITEA_APP_INI" +log "repo=$GITEA_OWNER/$GITEA_REPO upstream=$GITEA_MIRROR_URL" + +# --- upstream reachability --- +if git ls-remote "$GITEA_MIRROR_URL" HEAD >/dev/null 2>&1; then + log "upstream reachable from this host (git ls-remote OK)" +else + warn "upstream NOT reachable — scheduled pull mirrors will fail (check git daemon, ALLOW_LOCALNETWORKS)" +fi + +# --- repo mirror flags (API) --- +_meta="$(gitea_repo_get_api "$GITEA_OWNER" "$GITEA_REPO" 2>/dev/null)" || _meta="" +if [ -z "$_meta" ]; then + warn "could not read repo via API" +else + _mirror="$(json_bool "$_meta" "mirror")" + _interval="$(json_str "$_meta" "mirror_interval")" + _default="$(json_str "$_meta" "default_branch")" + _updated="$(json_str "$_meta" "updated_at")" + log "api mirror=$_mirror mirror_interval=$_interval default_branch=$_default updated_at=$_updated" + if [ "$_mirror" != "true" ]; then + warn "repo is NOT a pull mirror — only the initial push happened; auto-sync will never run" + warn "fix: delete $GITEA_OWNER/$GITEA_REPO in Gitea UI, re-run:" + warn " sudo ./gitea_attach_remote.sh $GITEA_MIRROR_URL" + warn " (API migrate must succeed — not git-push fallback)" + fi +fi + +# --- app.ini mirror cron hints --- +for _ini in "$GITEA_APP_INI" /etc/gitea/app.ini; do + [ -r "$_ini" ] || continue + log "config $_ini:" + awk ' + /^\[/ { sec = $0; gsub(/^\[|\]$/, "", sec) } + /^(ENABLED|SCHEDULE|MIN_INTERVAL|DEFAULT_INTERVAL|PULL_LIMIT|ALLOW_LOCALNETWORKS)/ { + print " " sec " " $0 + } + ' "$_ini" | grep -E 'cron\.update_mirrors|^\s*\[mirror\]|migrations' || true +done + +# --- sqlite mirror row (next_update) if available --- +if command -v sqlite3 >/dev/null 2>&1; then + _db="${GITEA_DB_PATH:-}" + [ -n "$_db" ] || _db="$GITEA_APP_DATA/gitea.db" + [ -r "$_db" ] || _db="/var/lib/gitea/db/gitea.db" + if [ -r "$_db" ]; then + log "mirror table (repo_id, interval, next_update_unix, updated_unix):" + sqlite3 -header -column "$_db" " + SELECT m.repo_id, m.interval, m.next_update_unix, m.updated_unix, r.name + FROM mirror m + JOIN repository r ON r.id = m.repo_id + WHERE r.lower_name = lower('$GITEA_REPO') + LIMIT 5; + " 2>/dev/null || warn "mirror SQL query failed" + fi +fi + +# --- gitea.log mirror errors --- +for _logf in "$GITEA_LOG_ROOT/gitea.log" /var/log/gitea/gitea.log; do + [ -r "$_logf" ] || continue + log "recent mirror errors in $_logf:" + grep -E 'mirror|already in queue|update_mirrors' "$_logf" 2>/dev/null | tail -n 8 || true + break +done + +cat < Monitor -> Queues -> Remove All (mirror) + 4) Ensure app.ini: + [cron.update_mirrors] + ENABLED = true + SCHEDULE = @every 10m + RUN_AT_START = true + then: sudo rc-service gitea restart + 5) FE push trigger: fe-android_cast-post-receive.example + +EOF 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 5d7071c..b16519d 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh @@ -207,6 +207,37 @@ gitea_list_repos_api() { | awk -F'"' '/"name":/ { print $4 }' } +gitea_repo_get_api() { + _owner="$1" + _repo="$2" + command -v curl >/dev/null 2>&1 || return 1 + _token="${GITEA_ADMIN_TOKEN:-}" + [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1 + _api="$(gitea_api_base)" + curl -sS "$_api/repos/$_owner/$_repo" \ + -H "Authorization: token $_token" +} + +gitea_repo_set_default_branch_api() { + _owner="$1" + _repo="$2" + _branch="$3" + command -v curl >/dev/null 2>&1 || return 1 + _token="${GITEA_ADMIN_TOKEN:-}" + [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1 + _api="$(gitea_api_base)" + _payload="{\"default_branch\":\"$_branch\"}" + _http="$(curl -sS -o /dev/null -w '%{http_code}' \ + -X PATCH "$_api/repos/$_owner/$_repo" \ + -H "Authorization: token $_token" \ + -H "Content-Type: application/json" \ + -d "$_payload")" + case "$_http" in + 200|204) return 0 ;; + *) return 1 ;; + esac +} + gitea_sync_mirror_api() { _owner="$1" _repo="$2" diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_set_default_branch.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_set_default_branch.sh new file mode 100755 index 0000000..7507b17 --- /dev/null +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_set_default_branch.sh @@ -0,0 +1,54 @@ +#!/bin/ash +# +# Set default branch (next) on Gitea repos for UI clone/browse defaults. +# +# Usage: +# sudo ./gitea_set_default_branch.sh +# GITEA_DEFAULT_BRANCH=next GITEA_OWNER=AndroidCast sudo ./gitea_set_default_branch.sh +# +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" + +GITEA_OWNER="${GITEA_OWNER:-${GITEA_ORG:-AndroidCast}}" +GITEA_DEFAULT_BRANCH="${GITEA_DEFAULT_BRANCH:-next}" +# Space-separated repo names; empty = all listed under owner. +GITEA_REPO_NAMES="${GITEA_REPO_NAMES:-}" + +log() { printf '[gitea-default-branch] %s\n' "$*"; } +warn() { printf '[gitea-default-branch] WARN: %s\n' "$*" >&2; } + +gitea_load_config || exit 1 +gitea_ensure_cli_access || exit 1 + +_repos="" +if [ -n "$GITEA_REPO_NAMES" ]; then + _repos="$GITEA_REPO_NAMES" +else + _repos="$(gitea_list_repos_api "$GITEA_OWNER" 2>/dev/null)" || _repos="" +fi + +if [ -z "$(echo "$_repos" | tr -d ' ')" ]; then + warn "no repos under $GITEA_OWNER" + exit 1 +fi + +_ok=0 +_fail=0 +for _repo in $_repos; do + [ -n "$_repo" ] || continue + if gitea_repo_set_default_branch_api "$GITEA_OWNER" "$_repo" "$GITEA_DEFAULT_BRANCH"; then + log "$GITEA_OWNER/$_repo -> default branch $GITEA_DEFAULT_BRANCH" + _ok=$((_ok + 1)) + else + warn "failed $GITEA_OWNER/$_repo (branch $GITEA_DEFAULT_BRANCH may not exist yet — sync mirror first)" + _fail=$((_fail + 1)) + fi +done + +log "done ok=$_ok fail=$_fail" +[ "$_fail" -eq 0 ] diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_user_admin.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_user_admin.sh new file mode 100755 index 0000000..3b1c859 --- /dev/null +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_user_admin.sh @@ -0,0 +1,49 @@ +#!/bin/ash +# +# Gitea user admin helpers (foxx pre-created by migrate — not self-registration). +# +# Usage: +# sudo ./gitea_user_admin.sh list +# sudo ./gitea_user_admin.sh reset-password foxx 'NewSecurePassword' +# sudo ./gitea_user_admin.sh must-change-password foxx +# +set -eu + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=gitea_ini.sh +. "$SCRIPT_DIR/gitea_ini.sh" + +log() { printf '[gitea-user] %s\n' "$*"; } +die() { printf '[gitea-user] ERROR: %s\n' "$*" >&2; exit 1; } + +gitea_load_config || exit 1 +gitea_ensure_cli_access || exit 1 + +_cmd="${1:-}" +_user="${2:-}" +_pass="${3:-}" + +case "$_cmd" in + list) + gitea_cli "admin" "user" "list" + ;; + reset-password) + [ -n "$_user" ] && [ -n "$_pass" ] || die "usage: $0 reset-password USER PASSWORD" + gitea_cli "admin" "user" "change-password" \ + "--username" "$_user" \ + "--password" "$_pass" + log "password set for $_user (use Sign In, not Register)" + ;; + must-change-password) + [ -n "$_user" ] || die "usage: $0 must-change-password USER" + gitea_cli "admin" "user" "must-change-password" \ + "--username" "$_user" + log "$_user must change password on next login" + ;; + -h|--help|help) + sed -n '2,12p' "$0" + ;; + *) + die "unknown command: $_cmd (try --help)" + ;; +esac