#!/usr/bin/env bash # Initialize a single Git monorepo directory with the same tree layout as # proxmox .gitmodules (clone each remote into path, drop nested .git). # # Optional: create empty repo on Gitea under an org and push (needs GITEA_TOKEN). # # Usage: # MONOREPO_ROOT=/path/to/mirror/dir ./scripts/maintenance/init-monorepo-from-proxmox-gitmodules.sh # GITEA_TOKEN=... GITEA_ORG=DBIS ./scripts/maintenance/init-monorepo-from-proxmox-gitmodules.sh --create-remote --push # # Env: # PROXMOX_ROOT — repo with .gitmodules (default: parent of scripts/maintenance/../../) # MONOREPO_ROOT — target directory (required, or default: $HOME/projects/proxmox-submodules-mirror — NOT Complete Credential product repo) # GITEA_URL — default https://gitea.d-bis.org # GITEA_ORG — default DBIS (override e.g. d-bis if needed) # GITEA_TOKEN — for API create + HTTPS push # SHALLOW — if 1, git clone --depth 1 (default 1) set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROXMOX_ROOT="${PROXMOX_ROOT:-$(cd "$SCRIPT_DIR/../.." && pwd)}" MONOREPO_ROOT="${MONOREPO_ROOT:-$HOME/projects/proxmox-submodules-mirror}" GITEA_URL="${GITEA_URL:-https://gitea.d-bis.org}" GITEA_ORG="${GITEA_ORG:-DBIS}" SHALLOW="${SHALLOW:-1}" CREATE_REMOTE=0 DO_PUSH=0 for arg in "$@"; do case "$arg" in --create-remote) CREATE_REMOTE=1 ;; --push) DO_PUSH=1 ;; -h|--help) sed -n '1,25p' "$0" exit 0 ;; esac done GITMODULES="${PROXMOX_ROOT}/.gitmodules" [[ -f "$GITMODULES" ]] || { echo "ERROR: missing $GITMODULES" >&2; exit 1; } mkdir -p "$MONOREPO_ROOT" cd "$MONOREPO_ROOT" if [[ -n "$(ls -A "$MONOREPO_ROOT" 2>/dev/null)" ]] && [[ ! -d "$MONOREPO_ROOT/.git" ]]; then echo "ERROR: $MONOREPO_ROOT is not empty and not a git repo. Use an empty dir or rm -rf contents." >&2 exit 1 fi manifest="$MONOREPO_ROOT/MONOREPO_SOURCES.txt" : > "$manifest" echo "# pathurl — generated from $GITMODULES" >> "$manifest" clone_one() { local rel_path="$1" url="$2" local dest="$MONOREPO_ROOT/$rel_path" echo "[clone] $rel_path <= $url" if [[ -e "$dest" ]]; then # Parent repo may have created an empty placeholder (e.g. nested submodule path) if [[ -z "$(find "$dest" -mindepth 1 -maxdepth 1 2>/dev/null | head -1)" ]]; then rmdir "$dest" 2>/dev/null || rm -rf "$dest" else echo " skip: non-empty $dest" printf '%s\t%s\n' "$rel_path" "$url" >> "$manifest" return 0 fi fi mkdir -p "$(dirname "$dest")" local depth_args=() [[ "$SHALLOW" == "1" ]] && depth_args=(--depth 1) GIT_TERMINAL_PROMPT=0 git clone "${depth_args[@]}" "$url" "$dest" rm -rf "$dest/.git" printf '%s\t%s\n' "$rel_path" "$url" >> "$manifest" } # Deepest paths first so nested submodule dirs (e.g. dbis_core/.../arbitrage) are not masked by parent clone. pairs_tmp="$(mktemp)" while read -r key path; do [[ "$key" =~ ^submodule\.(.+)\.path$ ]] || continue name="${BASH_REMATCH[1]}" url="$(git config -f "$GITMODULES" --get "submodule.${name}.url" 2>/dev/null || true)" [[ -n "$url" && -n "$path" ]] || continue slashes="${path//[^\/]/}" printf '%s\t%s\t%s\n' "${#slashes}" "$path" "$url" done < <(git config -f "$GITMODULES" --get-regexp '^submodule\..*\.path$') | sort -t $'\t' -k1,1nr > "$pairs_tmp" while IFS=$'\t' read -r _depth path url; do clone_one "$path" "$url" || echo "WARN: clone failed for $path ($url)" >&2 done < "$pairs_tmp" rm -f "$pairs_tmp" if [[ ! -d "$MONOREPO_ROOT/.git" ]]; then git init -b main "$MONOREPO_ROOT" fi cd "$MONOREPO_ROOT" cat > README.md </dev/null; then echo "Nothing new to commit." else git commit -m "Initial monorepo: import proxmox submodule remotes as directories" fi REPO_SLUG="$(basename "$MONOREPO_ROOT")" if [[ "$CREATE_REMOTE" == "1" ]]; then [[ -n "${GITEA_TOKEN:-}" ]] || { echo "ERROR: GITEA_TOKEN required for --create-remote" >&2; exit 1; } api="$GITEA_URL/api/v1/orgs/${GITEA_ORG}/repos" code="$(curl -sS -o /tmp/gitea-create-repo.json -w '%{http_code}' \ -H "Authorization: token ${GITEA_TOKEN}" \ -H 'Content-Type: application/json' \ -d "{\"name\":\"${REPO_SLUG}\",\"private\":true,\"description\":\"Monorepo from proxmox .gitmodules\"}" \ "$api")" || true if [[ "$code" == "201" ]]; then echo "Created Gitea repo ${GITEA_ORG}/${REPO_SLUG}" elif [[ "$code" == "409" ]]; then echo "Repo ${GITEA_ORG}/${REPO_SLUG} already exists (409). Continuing." else echo "Gitea API HTTP $code — body:" >&2 cat /tmp/gitea-create-repo.json >&2 || true exit 1 fi fi if [[ "$DO_PUSH" == "1" ]]; then [[ -n "${GITEA_TOKEN:-}" ]] || { echo "ERROR: GITEA_TOKEN required for --push (HTTPS)" >&2; exit 1; } remote_url="${GITEA_URL}/${GITEA_ORG}/${REPO_SLUG}.git" # embed token for one-shot push (user can switch to SSH later) auth_url="$(echo "$remote_url" | sed "s#https://#https://oauth2:${GITEA_TOKEN}@#")" git remote remove origin 2>/dev/null || true git remote add origin "$remote_url" git push -u "$auth_url" main echo "Pushed to $remote_url (set credential helper or SSH remote for future pushes)." fi echo "Done. Monorepo at $MONOREPO_ROOT"