From bc6b7cc17b4c80121db2a504faf9f4751ca13378 Mon Sep 17 00:00:00 2001 From: Anton Afanasyeu Date: Wed, 10 Jun 2026 21:43:04 +0200 Subject: [PATCH] gitea-related --- .../backend/scripts/gitea/gitea_mirror_lib.sh | 171 +++++++++++++++ .../scripts/gitea/gitea_mirror_submodules.sh | 200 ++++++++++++++++++ .../scripts/gitea/submodules.defaults.conf | 11 + 3 files changed, 382 insertions(+) create mode 100755 examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh create mode 100755 examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh create mode 100644 examples/crash_reporter/backend/scripts/gitea/submodules.defaults.conf diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh new file mode 100755 index 0000000..1d945ee --- /dev/null +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_lib.sh @@ -0,0 +1,171 @@ +#!/bin/ash +# Shared pull-mirror helpers for Gitea BE scripts. +# shellcheck shell=sh +# Requires: . "$SCRIPT_DIR/gitea_ini.sh" && gitea_load_config + +gitea_public_repo_url_for() { + _owner="$1" + _repo="$2" + _root="$GITEA_ROOT_URL" + case "$_root" in + */) _root="${_root%/}" ;; + esac + printf '%s/%s/%s.git' "$_root" "$_owner" "$_repo" +} + +gitea_local_git_url_for() { + printf 'http://127.0.0.1:%s/%s/%s.git' "${GITEA_HTTP_PORT:-3000}" "$1" "$2" +} + +gitea_api_base() { + printf '%s' "${GITEA_LOCAL_API:-http://127.0.0.1:3000/api/v1}" +} + +gitea_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" "mirror-$$" \ + "--scopes" "all" \ + "--raw" 2>/dev/null)" && [ -n "$_token" ] && printf '%s' "$_token" && return 0 + _admin="$(gitea_pick_owner_username)" + if [ -n "$_admin" ] && [ "$_admin" != "$GITEA_OWNER" ]; then + _token="$(gitea_cli "admin" "user" "generate-access-token" \ + "--username" "$_admin" \ + "--token-name" "mirror-$$" \ + "--scopes" "all" \ + "--raw" 2>/dev/null)" && [ -n "$_token" ] && printf '%s' "$_token" && return 0 + fi + return 1 +} + +gitea_owner_uid_for() { + _owner="$1" + 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)" + _uid="$(curl -sS "$_api/users/$_owner" \ + -H "Authorization: token $_token" \ + | awk -F'"' '/"id"/ { print $4; exit }')" + [ -n "$_uid" ] || return 1 + printf '%s' "$_uid" +} + +gitea_create_repo_cli() { + _owner="$1" + _repo="$2" + _private="${3:-false}" + if gitea_cli "admin" "user" "create-repo" \ + "--username" "$_owner" \ + "--name" "$_repo" \ + "--private=$_private" 2>/dev/null; then + return 0 + fi + gitea_cli "admin" "create-repository" \ + "--owner" "$_owner" \ + "--name" "$_repo" \ + "--private=$_private" 2>/dev/null +} + +gitea_migrate_mirror_api() { + _owner="$1" + _repo="$2" + _clone_url="$3" + _interval="${4:-10m}" + _private="${5:-false}" + _desc="${6:-Pull mirror of $_clone_url}" + + command -v curl >/dev/null 2>&1 || return 1 + _token="${GITEA_ADMIN_TOKEN:-}" + [ -n "$_token" ] || _token="$(gitea_resolve_push_token)" || return 1 + _uid="$(gitea_owner_uid_for "$_owner")" || return 1 + + _api="$(gitea_api_base)" + _payload=$(cat </dev/null 2>&1 || return 1 + _mirror="$_work_dir/${_repo}.mirror.git" + rm -rf "$_mirror" + git clone --mirror "$_clone_url" "$_mirror" || return 1 + gitea_create_repo_cli "$_owner" "$_repo" "${GITEA_PRIVATE:-false}" || true + _push_base="$(gitea_local_git_url_for "$_owner" "$_repo")" + _token="$(gitea_resolve_push_token)" || return 1 + _auth_url="${_push_base#http://}" + git -C "$_mirror" push --mirror "http://${_token}@${_auth_url}" || return 1 + rm -rf "$_mirror" + return 0 +} + +gitea_sync_mirror_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 -o /dev/null -w '%{http_code}' \ + -X POST "$_api/repos/$_owner/$_repo/mirror-sync" \ + -H "Authorization: token $_token" | grep -q '^20' +} + +# Mirror one upstream into Gitea (API migrate, else git push). Returns 0 on success. +gitea_mirror_pull_repo() { + _clone_url="$1" + _repo="$2" + _owner="${3:-$GITEA_OWNER}" + _interval="${4:-${GITEA_MIRROR_INTERVAL:-10m}}" + _work_dir="${5:-/tmp/gitea-mirror-$$}" + + _rc=0 + gitea_migrate_mirror_api "$_owner" "$_repo" "$_clone_url" "$_interval" "${GITEA_PRIVATE:-false}" \ + && return 0 + _api_rc=$? + if [ "$_api_rc" -eq 2 ]; then + gitea_sync_mirror_api "$_owner" "$_repo" && return 0 + return 0 + fi + mkdir -p "$_work_dir" + gitea_mirror_push_git "$_owner" "$_repo" "$_clone_url" "$_work_dir" || _rc=1 + return "$_rc" +} diff --git a/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh new file mode 100755 index 0000000..c0f7929 --- /dev/null +++ b/examples/crash_reporter/backend/scripts/gitea/gitea_mirror_submodules.sh @@ -0,0 +1,200 @@ +#!/bin/ash +# +# Mirror android_cast git submodules into Gitea as pull mirrors, then print a +# .gitmodules snippet pointing at Gitea URLs (commit that on canonical git). +# +# Usage (on Alpine BE as root or GITEA RUN_USER): +# sudo ./gitea_mirror_submodules.sh +# sudo ./gitea_mirror_submodules.sh /path/to/android_cast/.gitmodules +# GITEA_SUBMODULES_CONF=./extra.list sudo ./gitea_mirror_submodules.sh +# +# Requires [migrations] allowlist in app.ini for upstream hosts, e.g.: +# ALLOW_LOCALNETWORKS = true +# ALLOWED_DOMAINS = *,github.com,*.github.com,chromium.googlesource.com,git.zx2c4.com +# +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:-admin}" +GITEA_MIRROR_INTERVAL="${GITEA_MIRROR_INTERVAL:-24h}" +GITEA_PRIVATE="${GITEA_PRIVATE:-false}" +GITEA_SUBMODULES_CONF="${GITEA_SUBMODULES_CONF:-$SCRIPT_DIR/submodules.defaults.conf}" +WORK_DIR="${WORK_DIR:-/tmp/gitea-submodules-$$}" +SNIPPET_OUT="${SNIPPET_OUT:-$WORK_DIR/gitmodules.gitea-snippet.ini}" + +log() { printf '[gitea-submodules] %s\n' "$*"; } +warn() { printf '[gitea-submodules] WARN: %s\n' "$*" >&2; } +die() { printf '[gitea-submodules] ERROR: %s\n' "$*" >&2; exit 1; } + +# Parse .gitmodules -> repo|url|path|branch lines (repo = basename of path). +parse_gitmodules_file() { + _file="$1" + [ -r "$_file" ] || return 1 + awk ' + function trim(s) { + sub(/^[ \t]+/, "", s); sub(/[ \t]+$/, "", s) + return s + } + /^[ \t]*path[ \t]*=/ { + p = $0; sub(/^[^=]*=/, "", p); path = trim(p) + n = path; sub(".*/", "", n) + next + } + /^[ \t]*url[ \t]*=/ { + u = $0; sub(/^[^=]*=/, "", u); url = trim(u) + next + } + /^[ \t]*branch[ \t]*=/ { + b = $0; sub(/^[^=]*=/, "", b); branch = trim(b) + next + } + /^\[/ { + if (path != "" && url != "" && n != "") { + if (branch != "") print n "|" url "|" path "|" branch + else print n "|" url "|" path + } + path = ""; url = ""; branch = ""; n = "" + } + END { + if (path != "" && url != "" && n != "") { + if (branch != "") print n "|" url "|" path "|" branch + else print n "|" url "|" path + } + } + ' "$_file" +} + +load_submodule_entries() { + _gitmodules="${1:-}" + if [ -n "$_gitmodules" ] && [ -r "$_gitmodules" ]; then + log "reading submodules from $_gitmodules" + parse_gitmodules_file "$_gitmodules" + return 0 + fi + if [ -r "$GITEA_SUBMODULES_CONF" ]; then + log "reading submodules from $GITEA_SUBMODULES_CONF" + grep -v '^[[:space:]]*#' "$GITEA_SUBMODULES_CONF" | grep -v '^[[:space:]]*$' + return 0 + fi + return 1 +} + +write_gitmodules_snippet() { + _out="$1" + mkdir -p "$(dirname "$_out")" + : > "$_out" + while read -r line; do + [ -n "$line" ] || continue + _repo="${line%%|*}" + _rest="${line#*|}" + _url="${_rest%%|*}" + _rest2="${_rest#*|}" + _path="${_rest2%%|*}" + _branch="${_rest2#*|}" + case "$_branch" in + "$_path") _branch="" ;; + esac + _gitea_url="$(gitea_public_repo_url_for "$GITEA_OWNER" "$_repo")" + { + printf '[submodule "%s"]\n' "$_path" + printf '\tpath = %s\n' "$_path" + printf '\turl = %s\n' "$_gitea_url" + [ -n "$_branch" ] && printf '\tbranch = %s\n' "$_branch" + printf '\n' + } >> "$_out" + done < "$WORK_DIR/entries.list" \ + || die "no submodule list (pass .gitmodules path or use $GITEA_SUBMODULES_CONF)" + +if command -v rc-service >/dev/null 2>&1; then + rc-service gitea status 2>/dev/null | grep -qi started || rc-service gitea start 2>/dev/null || true +fi + +log "owner=$GITEA_OWNER interval=$GITEA_MIRROR_INTERVAL ini=$GITEA_APP_INI" +_ok=0 +_fail=0 + +while IFS= read -r line; do + [ -n "$line" ] || continue + _repo="${line%%|*}" + _rest="${line#*|}" + _upstream="${_rest%%|*}" + _path="${_rest#*|}" + _path="${_path%%|*}" + + log "mirror $_repo <- $_upstream" + if git ls-remote "$_upstream" HEAD >/dev/null 2>&1; then + log " upstream reachable" + else + warn " upstream not reachable from BE (mirror may still fail)" + fi + + if gitea_mirror_pull_repo "$_upstream" "$_repo" "$GITEA_OWNER" "$GITEA_MIRROR_INTERVAL" "$WORK_DIR"; then + log " OK -> $(gitea_public_repo_url_for "$GITEA_OWNER" "$_repo")" + _ok=$((_ok + 1)) + else + warn " FAILED $_repo" + _fail=$((_fail + 1)) + fi +done < "$WORK_DIR/entries.list" + +write_gitmodules_snippet "$SNIPPET_OUT" + +log "mirrors ok=$_ok failed=$_fail" +log "Wrote .gitmodules snippet: $SNIPPET_OUT" +printf '\n' +cat "$SNIPPET_OUT" +printf '\n' + +_main_repo_url="$(gitea_public_repo_url_for "$GITEA_OWNER" "android_cast")" +cat <