#!/bin/bash # detect-platform.sh - Detect git platform (Gitea or GitHub) for current repo # Usage: source detect-platform.sh && detect_platform # or: ./detect-platform.sh (prints platform name) detect_platform() { local remote_url remote_url=$(git remote get-url origin 2>/dev/null) if [[ -z "$remote_url" ]]; then echo "error: not a git repository or no origin remote" >&2 return 1 fi # Check for GitHub if [[ "$remote_url" == *"github.com"* ]]; then PLATFORM="github" export PLATFORM echo "github" return 0 fi # Check for common Gitea indicators # Gitea URLs typically don't contain github.com, gitlab.com, bitbucket.org if [[ "$remote_url" != *"gitlab.com"* ]] && \ [[ "$remote_url" != *"bitbucket.org"* ]]; then # Assume Gitea for self-hosted repos PLATFORM="gitea" export PLATFORM echo "gitea" return 0 fi PLATFORM="unknown" export PLATFORM echo "unknown" return 1 } get_repo_info() { local remote_url remote_url=$(git remote get-url origin 2>/dev/null) if [[ -z "$remote_url" ]]; then echo "error: not a git repository or no origin remote" >&2 return 1 fi # Extract owner/repo from URL # Handles: git@host:owner/repo.git, https://host/owner/repo.git, https://host/owner/repo local repo_path if [[ "$remote_url" == git@* ]]; then repo_path="${remote_url#*:}" else repo_path="${remote_url#*://}" repo_path="${repo_path#*/}" fi # Remove .git suffix if present repo_path="${repo_path%.git}" echo "$repo_path" } get_repo_owner() { local repo_info repo_info=$(get_repo_info) echo "${repo_info%%/*}" } get_repo_name() { local repo_info repo_info=$(get_repo_info) echo "${repo_info##*/}" } get_repo_slug() { get_repo_info } gitea_url_matches_host() { local url="${1:-}" host="${2:-}" [[ -n "$url" && -n "$host" ]] || return 1 [[ "${url%/}" == "https://$host" || "${url%/}" == "http://$host" || "${url%/}" == *"//$host" ]] } get_gitea_service_for_host() { local host="$1" local cred_file="${MOSAIC_CREDENTIALS_FILE:-$HOME/src/jarvis-brain/credentials.json}" case "$host" in git.mosaicstack.dev) echo "mosaicstack" return 0 ;; git.uscllc.com) echo "usc" return 0 ;; esac [[ -f "$cred_file" ]] || return 1 command -v jq >/dev/null 2>&1 || return 1 jq -r --arg host "$host" ' .gitea // {} | to_entries[] | select((.value.url // "" | sub("/+$"; "")) | test("https?://" + $host + "$")) | .key ' "$cred_file" | head -n 1 } find_tea_login_for_host() { local host="$1" local logins_json command -v tea >/dev/null 2>&1 || return 1 logins_json=$(tea login list --output json 2>/dev/null) || return 1 TEA_LOGINS_JSON="$logins_json" python3 - "$host" <<'PY' import json import os import sys from urllib.parse import urlparse host = sys.argv[1] try: logins = json.loads(os.environ.get("TEA_LOGINS_JSON", "[]")) except Exception: raise SystemExit(1) for login in logins if isinstance(logins, list) else []: url = str(login.get("url") or login.get("URL") or "") name = str(login.get("name") or login.get("Name") or "") parsed = urlparse(url) if parsed.hostname == host and name: print(name) raise SystemExit(0) raise SystemExit(1) PY } tea_login_matches_host() { local login_name="$1" host="$2" local logins_json command -v tea >/dev/null 2>&1 || return 1 logins_json=$(tea login list --output json 2>/dev/null) || return 1 TEA_LOGINS_JSON="$logins_json" python3 - "$login_name" "$host" <<'PY' import json import os import sys from urllib.parse import urlparse login_name, host = sys.argv[1], sys.argv[2] try: logins = json.loads(os.environ.get("TEA_LOGINS_JSON", "[]")) except Exception: raise SystemExit(1) for login in logins if isinstance(logins, list) else []: url = str(login.get("url") or login.get("URL") or "") name = str(login.get("name") or login.get("Name") or "") parsed = urlparse(url) if name == login_name and parsed.hostname == host: raise SystemExit(0) raise SystemExit(1) PY } get_gitea_login_for_host() { local host="${1:-}" local login if [[ -z "$host" ]]; then host=$(get_remote_host) || return 1 fi if [[ -n "${GITEA_LOGIN:-}" ]]; then if tea_login_matches_host "$GITEA_LOGIN" "$host"; then echo "$GITEA_LOGIN" return 0 fi fi login=$(find_tea_login_for_host "$host" || true) if [[ -n "$login" ]]; then echo "$login" return 0 fi return 1 } get_default_tea_login() { local logins_json command -v tea >/dev/null 2>&1 || return 1 logins_json=$(tea login list --output json 2>/dev/null) || return 1 TEA_LOGINS_JSON="$logins_json" python3 - <<'PY' import json import os try: logins = json.loads(os.environ.get("TEA_LOGINS_JSON", "[]")) except Exception: raise SystemExit(1) if not isinstance(logins, list) or not logins: raise SystemExit(1) for login in logins: if not isinstance(login, dict): continue is_default = str(login.get("default") or login.get("Default") or "").lower() name = str(login.get("name") or login.get("Name") or "") if name and is_default == "true": print(name) raise SystemExit(0) for login in logins: if not isinstance(login, dict): continue name = str(login.get("name") or login.get("Name") or "") if name: print(name) raise SystemExit(0) raise SystemExit(1) PY } get_gitea_login_for_repo_override() { local login if [[ -n "${GITEA_LOGIN:-}" ]]; then echo "$GITEA_LOGIN" return 0 fi login=$(get_default_tea_login || true) if [[ -n "$login" ]]; then echo "$login" return 0 fi return 1 } get_host_from_url() { local url="${1:-}" [[ -n "$url" ]] || return 1 python3 - "$url" <<'PY' import sys from urllib.parse import urlparse parsed = urlparse(sys.argv[1]) if parsed.hostname: print(parsed.hostname) raise SystemExit(0) raise SystemExit(1) PY } get_gitea_api_host_for_repo_override() { if [[ -n "${GITEA_HOST:-}" ]]; then echo "$GITEA_HOST" return 0 fi get_host_from_url "${GITEA_URL:-}" } get_gitea_repo_args() { local repo host login repo=$(get_repo_slug) || return 1 host=$(get_remote_host) || return 1 login=$(get_gitea_login_for_host "$host") || return 1 printf -- '--repo %q --login %q' "$repo" "$login" } get_gitea_login() { get_gitea_login_for_host "$(get_remote_host)" } get_remote_host() { local remote_url remote_url=$(git remote get-url origin 2>/dev/null || true) if [[ -z "$remote_url" ]]; then return 1 fi if [[ "$remote_url" =~ ^https?://([^/]+)/ ]]; then local host="${BASH_REMATCH[1]}" echo "${host##*@}" return 0 fi if [[ "$remote_url" =~ ^git@([^:]+): ]]; then echo "${BASH_REMATCH[1]}" return 0 fi return 1 } # Resolve a Gitea API token for the given host. # Priority: Mosaic credential loader → GITEA_TOKEN env → ~/.git-credentials get_gitea_token() { local host="$1" local script_dir script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" local cred_loader="$script_dir/../_lib/credentials.sh" # 1. Mosaic credential loader (host → service mapping, run in subshell to avoid polluting env) if [[ -f "$cred_loader" ]]; then local token token=$( # shellcheck source=/dev/null source "$cred_loader" # Host-specific wrapper resolution must not inherit caller/global GITEA_*. # load_credentials intentionally preserves existing env vars for interactive use, # but metadata/merge wrappers need credentials matching the remote host. unset GITEA_TOKEN GITEA_URL case "$host" in git.mosaicstack.dev) load_credentials gitea-mosaicstack 2>/dev/null ;; git.uscllc.com) load_credentials gitea-usc 2>/dev/null ;; *) local matched=false for svc in gitea-mosaicstack gitea-usc; do unset GITEA_TOKEN GITEA_URL load_credentials "$svc" 2>/dev/null || continue if [[ "${GITEA_URL:-}" == "https://$host" || "${GITEA_URL:-}" == "http://$host" || "${GITEA_URL:-}" == *"//$host" ]]; then matched=true break fi done if [[ "$matched" != true ]]; then unset GITEA_TOKEN GITEA_URL fi ;; esac echo "${GITEA_TOKEN:-}" ) if [[ -n "$token" ]]; then echo "$token" return 0 fi fi # 2. GITEA_TOKEN env var (only when GITEA_URL, if present, matches the remote host) if [[ -n "${GITEA_TOKEN:-}" ]]; then if [[ -z "${GITEA_URL:-}" || "${GITEA_URL:-}" == "https://$host" || "${GITEA_URL:-}" == "http://$host" || "${GITEA_URL:-}" == *"//$host" ]]; then echo "$GITEA_TOKEN" return 0 fi fi # 3. ~/.git-credentials file local creds="$HOME/.git-credentials" if [[ -f "$creds" ]]; then local token token=$(grep -F "$host" "$creds" 2>/dev/null | sed -n 's#https\?://[^@]*:\([^@/]*\)@.*#\1#p' | head -n 1) if [[ -n "$token" ]]; then echo "$token" return 0 fi fi return 1 } # Resolve HTTPS basic auth credentials for a Gitea host from ~/.git-credentials. # Prints "username:password" for direct curl -u consumption. Callers must not log it. get_gitea_basic_auth() { local host="$1" local creds="$HOME/.git-credentials" if [[ ! -f "$creds" ]]; then return 1 fi python3 - "$host" "$creds" <<'PY' import sys from pathlib import Path from urllib.parse import unquote, urlparse host = sys.argv[1] creds = Path(sys.argv[2]) for line in creds.read_text(encoding="utf-8").splitlines(): parsed = urlparse(line.strip()) if parsed.hostname != host: continue username = unquote(parsed.username or "") password = unquote(parsed.password or "") if username and password: print(f"{username}:{password}") raise SystemExit(0) raise SystemExit(1) PY } # If script is run directly (not sourced), output the platform if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then detect_platform fi