diff --git a/examples/crash_reporter/backend/scripts/gitea/README.md b/examples/crash_reporter/backend/scripts/gitea/README.md index d43d1cd..42bcfd1 100644 --- a/examples/crash_reporter/backend/scripts/gitea/README.md +++ b/examples/crash_reporter/backend/scripts/gitea/README.md @@ -48,19 +48,34 @@ Would you like to reset Gitea repositories state to the default (empty)? [y/N] After backup (and optional reset), point Gitea at the real remote: ```bash -export GITEA_ADMIN_TOKEN='your-admin-pat' - -# defaults: git://f0xx.org/android_cast → foxx/android_cast -./gitea_attach_remote.sh +./gitea_attach_remote.sh git://10.7.0.10/android_cast +# or: GITEA_MIRROR_URL=git://f0xx.org/android_cast ./gitea_attach_remote.sh ``` +### Do I need `GITEA_ADMIN_TOKEN`? + +**Usually no** when run **as root on the BE**. The script tries `gitea admin user create-repo` and `gitea admin user generate-access-token` before push. + +**Optional** if CLI fails — create in Gitea UI: **Avatar → Settings → Applications → Generate New Token** (scopes: All), then: + +```bash +export GITEA_ADMIN_TOKEN='gitea_…' +./gitea_attach_remote.sh git://10.7.0.10/android_cast +``` + +**Why auth?** Gitea rejects anonymous `git push`. The token (or CLI-generated token) proves write access to `foxx/android_cast`. + +**Prerequisite:** user **`foxx`** must exist in Gitea (the script creates the repo, not the user). + +**`remote: Not found`** = repo did not exist yet and nothing created it. Re-sync the updated script and re-run. + +Local push URL is `http://127.0.0.1:3000/foxx/android_cast.git` (no `/app/.../git/` — nginx strips subpath on :3000). + Override: ```bash -GITEA_MIRROR_URL='git://f0xx.org/android_cast' \ -GITEA_OWNER='foxx' \ -GITEA_REPO='android_cast' \ -GITEA_MIRROR_INTERVAL='10m' \ +GITEA_MIRROR_URL='git://10.7.0.10/android_cast' \ +GITEA_OWNER='foxx' GITEA_REPO='android_cast' \ ./gitea_attach_remote.sh ``` 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 c75e67f..47ed72d 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_attach_remote.sh @@ -3,19 +3,17 @@ # Attach canonical git:// remote to Gitea as a pull mirror (no NFS/SSHFS shared folder). # Run on Alpine BE after gitea_collect_backup.sh (optional reset) and Gitea is running. # -# Default remote matches docs/INFRA.md: git://f0xx.org/android_cast -# # Usage: -# export GITEA_ADMIN_TOKEN='...' # admin personal access token (recommended) # ./gitea_attach_remote.sh +# ./gitea_attach_remote.sh git://10.7.0.10/android_cast +# GITEA_MIRROR_URL=git://f0xx.org/android_cast ./gitea_attach_remote.sh # -# GITEA_MIRROR_URL=git://f0xx.org/android_cast \ -# GITEA_OWNER=foxx GITEA_REPO=android_cast \ -# ./gitea_attach_remote.sh +# GITEA_ADMIN_TOKEN is optional on BE when run as root: the script can create a repo +# and a short-lived push token via `gitea admin` CLI. Set a token only if CLI fails. # # Methods (auto): -# 1) Gitea API /repos/migrate with mirror=true (fastest ongoing sync in UI) -# 2) git clone --mirror + git push --mirror to local Gitea HTTP remote +# 1) Gitea API /repos/migrate with mirror=true (ongoing pull sync in UI) +# 2) gitea admin create-repo + git clone --mirror + git push --mirror # set -eu @@ -30,6 +28,19 @@ GITEA_MIRROR_INTERVAL="${GITEA_MIRROR_INTERVAL:-10m}" GITEA_PRIVATE="${GITEA_PRIVATE:-false}" WORK_DIR="${WORK_DIR:-/tmp/gitea-attach-$$}" +# First positional argument = mirror URL (convenience). +if [ $# -ge 1 ] && [ -n "$1" ]; then + case "$1" in + -h|--help) + sed -n '2,20p' "$0" + exit 0 + ;; + *) + GITEA_MIRROR_URL="$1" + ;; + esac +fi + log() { printf '[gitea-attach] %s\n' "$*"; } warn() { printf '[gitea-attach] WARN: %s\n' "$*" >&2; } die() { printf '[gitea-attach] ERROR: %s\n' "$*" >&2; exit 1; } @@ -38,29 +49,21 @@ need_cmd() { command -v "$1" >/dev/null 2>&1 || die "missing command: $1" } -gitea_api_base() { - _url="$GITEA_ROOT_URL" - case "$_url" in - */) _url="${_url%/}" ;; - esac - printf '%s/api/v1' "$_url" -} - -gitea_local_http_base() { - printf 'http://127.0.0.1:%s' "${GITEA_HTTP_PORT:-3000}" -} - -# ROOT_URL may include subpath; derive path prefix for local git push URL. -gitea_subpath_prefix() { +# Public clone URL (via ROOT_URL + subpath). Local :3000 git/API omit subpath (nginx strips it). +gitea_public_repo_url() { _root="$GITEA_ROOT_URL" - _root="${_root#http://}" - _root="${_root#https://}" - _root="${_root#*@}" # drop user@ if any - _root="${_root#*/}" # drop host case "$_root" in - */*) printf '/%s' "${_root%/}" ;; - *) printf '' ;; + */) _root="${_root%/}" ;; esac + printf '%s/%s/%s.git' "$_root" "$GITEA_OWNER" "$GITEA_REPO" +} + +gitea_local_git_url() { + printf 'http://127.0.0.1:%s/%s/%s.git' "${GITEA_HTTP_PORT:-3000}" "$GITEA_OWNER" "$GITEA_REPO" +} + +gitea_api_base() { + printf '%s' "${GITEA_LOCAL_API:-http://127.0.0.1:3000/api/v1}" } test_remote_reachable() { @@ -76,12 +79,73 @@ test_remote_reachable() { return 1 } +resolve_push_token() { + if [ -n "${GITEA_ADMIN_TOKEN:-}" ]; then + printf '%s' "$GITEA_ADMIN_TOKEN" + return 0 + fi + _token="" + _token="$(gitea_cli "admin" "user" "generate-access-token" \ + "--username" "$GITEA_OWNER" \ + "--token-name" "attach-$$" \ + "--scopes" "all" \ + "--raw" 2>/dev/null)" && [ -n "$_token" ] && printf '%s' "$_token" && return 0 + _token="$(gitea_cli "admin" "user" "generate-access-token" \ + "--username" "$GITEA_OWNER" \ + "--token-name" "attach-$$" \ + "--raw" 2>/dev/null)" && [ -n "$_token" ] && printf '%s' "$_token" && return 0 + return 1 +} + +create_repo_via_cli() { + log "creating empty repo $GITEA_OWNER/$GITEA_REPO via gitea admin CLI..." + if gitea_cli "admin" "user" "create-repo" \ + "--username" "$GITEA_OWNER" \ + "--name" "$GITEA_REPO" \ + "--private=$GITEA_PRIVATE" 2>/dev/null; then + log "repo created (admin user create-repo)" + return 0 + fi + if gitea_cli "admin" "create-repository" \ + "--owner" "$GITEA_OWNER" \ + "--name" "$GITEA_REPO" \ + "--private=$GITEA_PRIVATE" 2>/dev/null; then + log "repo created (admin create-repository)" + return 0 + fi + warn "gitea admin create-repo failed — user '$GITEA_OWNER' must exist (create in Gitea UI first)" + return 1 +} + +create_repo_via_api() { + [ -n "${GITEA_ADMIN_TOKEN:-}" ] || return 1 + need_cmd curl + _api="$(gitea_api_base)" + _http="$(curl -sS -o /tmp/gitea-create-repo.json -w '%{http_code}' \ + -X POST "$_api/admin/users/$GITEA_OWNER/repos" \ + -H "Authorization: token $GITEA_ADMIN_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"name\":\"$GITEA_REPO\",\"private\":$GITEA_PRIVATE}")" + if [ "$_http" = "201" ] || [ "$_http" = "200" ]; then + log "repo created via API" + return 0 + fi + if [ "$_http" = "409" ]; then + log "repo already exists (API 409)" + return 0 + fi + warn "API create repo HTTP $_http" + cat /tmp/gitea-create-repo.json >&2 2>/dev/null || true + return 1 +} + gitea_owner_uid() { need_cmd curl - [ -n "${GITEA_ADMIN_TOKEN:-}" ] || return 1 + _token="${GITEA_ADMIN_TOKEN:-}" + [ -n "$_token" ] || _token="$(resolve_push_token)" || return 1 _api="$(gitea_api_base)" _uid="$(curl -sS "$_api/users/$GITEA_OWNER" \ - -H "Authorization: token $GITEA_ADMIN_TOKEN" \ + -H "Authorization: token $_token" \ | awk -F'"' '/"id"/ { print $4; exit }')" [ -n "$_uid" ] || return 1 printf '%s' "$_uid" @@ -89,9 +153,10 @@ gitea_owner_uid() { migrate_via_api() { need_cmd curl - [ -n "${GITEA_ADMIN_TOKEN:-}" ] || return 1 + _token="${GITEA_ADMIN_TOKEN:-}" + [ -n "$_token" ] || _token="$(resolve_push_token)" || return 1 - _uid="$(gitea_owner_uid)" || die "could not resolve uid for owner $GITEA_OWNER (check token and user exists)" + _uid="$(gitea_owner_uid)" || die "could not resolve uid for owner $GITEA_OWNER" _api="$(gitea_api_base)" _payload=$(cat <&2 return 1 } +ensure_gitea_repo_exists() { + create_repo_via_cli || create_repo_via_api || true +} + mirror_via_git() { need_cmd git mkdir -p "$WORK_DIR" @@ -144,32 +211,19 @@ mirror_via_git() { rm -rf "$_mirror" git clone --mirror "$GITEA_MIRROR_URL" "$_mirror" - _prefix="$(gitea_subpath_prefix)" - _push_url="${GITEA_PUSH_URL:-$(gitea_local_http_base)${_prefix}/${GITEA_OWNER}/${GITEA_REPO}.git}" + ensure_gitea_repo_exists - log "creating empty repo in Gitea (if needed) via API or assume exists" - if [ -n "${GITEA_ADMIN_TOKEN:-}" ]; then - _api="$(gitea_api_base)" - curl -sS -o /dev/null -w '' \ - -X POST "$_api/admin/users/$GITEA_OWNER/repos" \ - -H "Authorization: token $GITEA_ADMIN_TOKEN" \ - -H "Content-Type: application/json" \ - -d "{\"name\":\"$GITEA_REPO\",\"private\":$GITEA_PRIVATE}" \ - 2>/dev/null || true - fi + _push_base="$(gitea_local_git_url)" + _token="$(resolve_push_token)" || die "need credentials: export GITEA_ADMIN_TOKEN=... or run as root with gitea CLI" - log "git push --mirror -> $_push_url" - if [ -n "${GITEA_ADMIN_TOKEN:-}" ]; then - _auth_url="$(gitea_local_http_base)${_prefix}/${GITEA_OWNER}/${GITEA_REPO}.git" - _auth_url="${_auth_url#http://}" - git -C "$_mirror" push --mirror "http://${GITEA_ADMIN_TOKEN}@${_auth_url}" - else - warn "GITEA_ADMIN_TOKEN unset — push may fail; set token or configure git credentials" - git -C "$_mirror" push --mirror "$_push_url" - fi + _auth_url="${_push_base#http://}" + _push_url="http://${_token}@${_auth_url}" + + log "git push --mirror -> $_push_base (localhost, no subpath)" + git -C "$_mirror" push --mirror "$_push_url" log "one-time mirror push complete" - warn "for ongoing sync, enable pull mirror in Gitea UI or re-run API migrate method" + warn "for automatic updates, re-run with API migrate or enable pull mirror in Gitea UI" rm -rf "$WORK_DIR" } @@ -183,8 +237,22 @@ Ongoing sync (no shared mount): • Or cron on BE (as git user): */15 * * * * gitea -c $GITEA_APP_INI admin repo-sync-mirrors -Clone URL for developers: - ${GITEA_ROOT_URL}${GITEA_OWNER}/${GITEA_REPO}.git +Clone URL (public): + $(gitea_public_repo_url) + +EOF +} + +print_token_help() { + cat <<'EOF' + +About GITEA_ADMIN_TOKEN (optional on BE): + • What: a Personal Access Token for your Gitea admin/owner user. + • Where: log in to Gitea UI → top-right avatar → Settings → Applications + → Generate New Token → name it e.g. "attach-script" → scopes: all (or repo write). + • Why: HTTP git push and REST API require authentication. Without a token the script + tries `gitea admin user generate-access-token` on the server (works when run as root). + • You do NOT need a token if `gitea admin create-repo` + generate-access-token succeed. EOF } @@ -193,13 +261,13 @@ EOF gitea_load_config || exit 1 -log "Gitea ROOT_URL=$GITEA_ROOT_URL" +log "Gitea ROOT_URL=$GITEA_ROOT_URL (public)" +log "local API=$(gitea_api_base) local git=$(gitea_local_git_url)" log "target repo: $GITEA_OWNER/$GITEA_REPO" log "mirror from: $GITEA_MIRROR_URL" if ! test_remote_reachable "$GITEA_MIRROR_URL"; then - warn "remote not reachable from this host" - warn "ensure git-daemon client access to f0xx.org:9418 or set GITEA_MIRROR_URL to https/ssh URL" + warn "remote not reachable from this host — check git:// daemon / firewall" fi if command -v rc-service >/dev/null 2>&1; then @@ -216,6 +284,9 @@ if migrate_via_api; then fi log "falling back to git clone --mirror + push --mirror" -mirror_via_git +if ! mirror_via_git; then + print_token_help + exit 1 +fi enable_mirror_sync_cron_hint -log "done (git mirror push — configure pull mirror in UI for automatic updates)" +log "done (git mirror push)" diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_ini.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_ini.sh index 6c784b4..0d1caec 100755 --- a/examples/crash_reporter/backend/scripts/gitea/gitea_ini.sh +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_ini.sh @@ -70,6 +70,18 @@ gitea_load_config() { export GITEA_APP_INI GITEA_REPO_ROOT GITEA_APP_DATA GITEA_ROOT_URL GITEA_HTTP_PORT export GITEA_DOMAIN GITEA_DB_TYPE GITEA_DB_HOST GITEA_DB_NAME GITEA_DB_USER GITEA_DB_PASS export GITEA_DB_PATH GITEA_LFS_PATH GITEA_LOG_ROOT + + # External URL (ROOT_URL) includes nginx subpath; direct :3000 API/git do not. + GITEA_HTTP_SUBPATH="${GITEA_HTTP_SUBPATH:-}" + if [ -z "$GITEA_HTTP_SUBPATH" ] && [ -n "$GITEA_ROOT_URL" ]; then + _p="${GITEA_ROOT_URL#*://}" + _p="${_p#*/}" + case "$_p" in + */*) GITEA_HTTP_SUBPATH="/${_p%/}" ;; + esac + fi + GITEA_LOCAL_API="${GITEA_LOCAL_API:-http://127.0.0.1:${GITEA_HTTP_PORT}/api/v1}" + export GITEA_HTTP_SUBPATH GITEA_LOCAL_API } # Must be absolute, no .., and under allowed prefixes (Gitea-owned paths only).