Partnership/gitea_ssh_setup.sh: generates ed25519 keypair, registers public key with Gitea API (POST /api/v1/user/keys), tests SSH connection, writes ~/.ssh/config entry. Idempotent — skips steps already done. --force regenerates and re-registers. Resolves Gitea endpoint from container IP first, falls back to GITEA_DOMAIN. common.sh: alias HOST*_GITEA_API_TOKEN → GITEA_API_TOKEN in detect_hosts(). Add to detect_hosts() doc comment. master.conf: add GITEA_HTTP_PORT=3000 for API endpoint construction. host1.conf: add HOST1_GITEA_API_TOKEN (fill in from Gitea Settings → Applications). Add my-Gitea.xml to HOST1_PARTNERSHIP_AUTH_STACK — onboard pushes it to HOST2. host2.conf: add Gitea to FALLBACK_HOST2_COVERS_HOST1_TIER1 — starts immediately when HOST1 goes down, making the source of truth reachable independently of HOST1's auth stack.
470 lines
19 KiB
Bash
Executable File
470 lines
19 KiB
Bash
Executable File
#!/bin/bash
|
|
# ==============================================================================================
|
|
# ============================= Gitea SSH Setup ================================================
|
|
# ==============================================================================================
|
|
#
|
|
# PURPOSE
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# Generates an SSH keypair for Gitea authentication and registers the public
|
|
# key with Gitea via the API. Run after the Gitea container is up — typically
|
|
# called during onboard setup or when re-keying a server.
|
|
#
|
|
# Idempotent: skips key generation if key already exists and skips registration
|
|
# if the same public key is already registered in Gitea. Use --force to
|
|
# regenerate and re-register.
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL MODEL
|
|
# ==============================================================================================
|
|
#
|
|
# 1. Resolve Gitea API endpoint
|
|
# Container running locally → use container IP + GITEA_HTTP_PORT
|
|
# Container not local → fall back to GITEA_DOMAIN
|
|
#
|
|
# 2. Validate GITEA_API_TOKEN is set
|
|
# Token needed for API calls — create once in Gitea UI (see CONFIGURATION)
|
|
#
|
|
# 3. Generate SSH keypair (ed25519) at GITEA_SSH_KEY
|
|
# Skipped if key already exists — use --force to regenerate
|
|
#
|
|
# 4. Check if public key already registered in Gitea
|
|
# Compares fingerprints — skips POST if already registered
|
|
#
|
|
# 5. Register public key via POST /api/v1/user/keys
|
|
# Title: "unraid-{host_id}" (e.g. unraid-host1)
|
|
#
|
|
# 6. Test SSH connection to Gitea
|
|
# ssh -i GITEA_SSH_KEY -p SSH_PORT git@<gitea_host>
|
|
# Success confirms key is accepted and auth works end-to-end
|
|
#
|
|
# 7. Write ~/.ssh/config Host entry for gitea
|
|
# Skipped if entry already exists — use --force to overwrite
|
|
#
|
|
# ==============================================================================================
|
|
# OPERATIONAL SAFEGUARDS
|
|
# ==============================================================================================
|
|
#
|
|
# Root Required
|
|
# ssh-keygen and ~/.ssh/ operations require consistent permissions.
|
|
#
|
|
# Single Instance Lock
|
|
# acquire_lock prevents concurrent runs.
|
|
#
|
|
# Token Validated Before Any API Call
|
|
# If HOST*_GITEA_API_TOKEN is empty, exits with a clear message pointing to
|
|
# Gitea Settings → Applications → Generate Token (scope: write:user).
|
|
#
|
|
# Key Never Clobbered Without --force
|
|
# Existing key at GITEA_SSH_KEY is left untouched unless --force is passed.
|
|
# Prevents accidentally invalidating working auth.
|
|
#
|
|
# Duplicate Registration Safe
|
|
# Checks existing keys before POST — will not create duplicate entries in Gitea.
|
|
#
|
|
# ==============================================================================================
|
|
# CONFIGURATION
|
|
# ==============================================================================================
|
|
#
|
|
# host*.conf
|
|
#
|
|
# HOST*_GITEA_API_TOKEN
|
|
# Personal access token for the Gitea user who owns the repository.
|
|
# Create in Gitea: Settings → Applications → Generate Token → scope: write:user
|
|
# Aliased by detect_hosts() → GITEA_API_TOKEN.
|
|
#
|
|
# master.conf
|
|
#
|
|
# GITEA_CONTAINER Container name for local IP detection (default: "Gitea")
|
|
# GITEA_DOMAIN Domain fallback if container is remote or not running
|
|
# GITEA_SSH_KEY Path for the SSH keypair (default: /root/.ssh/unraid_gitea)
|
|
# SSH_PORT Gitea SSH port (default: 221)
|
|
# GITEA_HTTP_PORT Gitea API port (default: 3000)
|
|
#
|
|
# ==============================================================================================
|
|
# STATE FILES
|
|
# ==============================================================================================
|
|
#
|
|
# ~/.ssh/config Updated with Host entry for gitea on successful setup
|
|
# GITEA_SSH_KEY Private key (permissions: 600)
|
|
# GITEA_SSH_KEY.pub Public key — registered with Gitea
|
|
#
|
|
# ==============================================================================================
|
|
# RUNTIME MODES
|
|
# ==============================================================================================
|
|
#
|
|
# gitea_ssh_setup.sh
|
|
# Generate key if missing, register with Gitea, test connection.
|
|
#
|
|
# gitea_ssh_setup.sh --force
|
|
# Regenerate key and re-register even if already set up.
|
|
#
|
|
# gitea_ssh_setup.sh --status
|
|
# Show key state, fingerprint, registration status, SSH test. No changes.
|
|
#
|
|
# gitea_ssh_setup.sh --dry-run
|
|
# Show what would be done without generating, registering, or writing files.
|
|
#
|
|
# gitea_ssh_setup.sh --log
|
|
# Verbose output for each step.
|
|
#
|
|
# ==============================================================================================
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
SCRIPTS_ROOT="$SCRIPT_DIR/.."
|
|
|
|
source "$SCRIPTS_ROOT/load_config.sh"
|
|
|
|
# ── Parse --force before parse_args ───────────────────────────────────────────────────────────
|
|
FORCE=false
|
|
FILTERED_ARGS=()
|
|
|
|
for arg in "$@"; do
|
|
case "$arg" in
|
|
--force) FORCE=true ;;
|
|
*) FILTERED_ARGS+=("$arg") ;;
|
|
esac
|
|
done
|
|
|
|
parse_args "${FILTERED_ARGS[@]}"
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Setup ━━━
|
|
# ==============================================================================================
|
|
if [[ "$EUID" -ne 0 ]]; then
|
|
error "Must be run as root"
|
|
exit 1
|
|
fi
|
|
|
|
validate_unraid_cmd \
|
|
"/usr/local/emhttp/plugins/dynamix/scripts/notify" \
|
|
"" "" \
|
|
"unRAID notify script" || warn "unRAID notify script not found — native notifications disabled"
|
|
|
|
acquire_lock
|
|
|
|
detect_hosts
|
|
|
|
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"
|
|
[[ "$FORCE" == true ]] && warn "FORCE mode — existing key and registration will be replaced"
|
|
|
|
log "Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
log "SSH key: $GITEA_SSH_KEY"
|
|
log "SSH port: $SSH_PORT"
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Resolve Gitea API Endpoint ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR Gitea API Endpoint ━━━"
|
|
|
|
GITEA_HOST=""
|
|
GITEA_API=""
|
|
|
|
# Try local container first
|
|
CONTAINER_IP=$(docker inspect --format '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
|
|
"$GITEA_CONTAINER" 2>/dev/null | awk 'NF{print $1; exit}')
|
|
|
|
if [[ -n "$CONTAINER_IP" ]]; then
|
|
GITEA_HOST="$CONTAINER_IP"
|
|
GITEA_API="http://${CONTAINER_IP}:${GITEA_HTTP_PORT}/api/v1"
|
|
log "Gitea container running locally: $CONTAINER_IP:$GITEA_HTTP_PORT"
|
|
elif [[ -n "$GITEA_DOMAIN" ]]; then
|
|
GITEA_HOST="$GITEA_DOMAIN"
|
|
GITEA_API="https://${GITEA_DOMAIN}/api/v1"
|
|
warn "Gitea container not running locally — using domain: $GITEA_DOMAIN"
|
|
else
|
|
error "Cannot resolve Gitea API endpoint"
|
|
error "Gitea container ($GITEA_CONTAINER) is not running and GITEA_DOMAIN is not set in master.conf"
|
|
error "Start the Gitea container first, or set GITEA_DOMAIN"
|
|
exit 1
|
|
fi
|
|
|
|
log "API endpoint: $GITEA_API"
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Validate API Token ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_SHIELD API Token ━━━"
|
|
|
|
# Alias: detect_hosts() maps HOST*_GITEA_API_TOKEN → GITEA_API_TOKEN
|
|
if [[ -z "${GITEA_API_TOKEN:-}" ]]; then
|
|
error "HOST${MY_ID#HOST}_GITEA_API_TOKEN is not set in host${MY_ID#HOST}.conf"
|
|
error ""
|
|
error "Create a token in Gitea:"
|
|
error " 1. Log into Gitea"
|
|
error " 2. Top-right profile → Settings → Applications"
|
|
error " 3. Generate New Token → name: unraid-setup → scope: write:user"
|
|
error " 4. Copy the token (shown once) → paste into HOST${MY_ID#HOST}_GITEA_API_TOKEN in host${MY_ID#HOST}.conf"
|
|
exit 1
|
|
fi
|
|
|
|
# Test token against the API
|
|
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
|
|
-H "Authorization: token $GITEA_API_TOKEN" \
|
|
"$GITEA_API/user" 2>/dev/null)
|
|
|
|
if [[ "$HTTP_CODE" == "200" ]]; then
|
|
log "API token valid ✅"
|
|
elif [[ "$HTTP_CODE" == "401" ]]; then
|
|
error "API token rejected (HTTP 401) — token may be expired or have wrong scope"
|
|
error "Regenerate the token in Gitea: Settings → Applications → Generate Token → scope: write:user"
|
|
exit 1
|
|
else
|
|
error "Gitea API not reachable (HTTP $HTTP_CODE) at $GITEA_API"
|
|
error "Check that the Gitea container is healthy and the port is correct (GITEA_HTTP_PORT=$GITEA_HTTP_PORT)"
|
|
exit 1
|
|
fi
|
|
|
|
# ── Status mode exits here after full state display ───────────────────────────────────────────
|
|
if [[ "$SHOW_STATUS" == true ]]; then
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
|
|
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
echo "$ICON_GEAR API endpoint: $GITEA_API"
|
|
echo ""
|
|
|
|
if [[ -f "${GITEA_SSH_KEY}.pub" ]]; then
|
|
FINGERPRINT=$(ssh-keygen -lf "${GITEA_SSH_KEY}.pub" 2>/dev/null | awk '{print $2}')
|
|
echo " $ICON_GEAR Key file: ${GITEA_SSH_KEY}.pub"
|
|
echo " $ICON_GEAR Fingerprint: $FINGERPRINT"
|
|
|
|
# Check registration
|
|
REGISTERED_KEYS=$(curl -s \
|
|
-H "Authorization: token $GITEA_API_TOKEN" \
|
|
"$GITEA_API/user/keys" 2>/dev/null)
|
|
LOCAL_PUB=$(awk '{print $1" "$2}' "${GITEA_SSH_KEY}.pub")
|
|
if echo "$REGISTERED_KEYS" | grep -qF "$LOCAL_PUB"; then
|
|
echo " $ICON_GEAR Registered: YES ✅"
|
|
else
|
|
echo " $ICON_GEAR Registered: NO — run without --status to register"
|
|
fi
|
|
else
|
|
echo " $ICON_GEAR Key file: NOT FOUND at $GITEA_SSH_KEY"
|
|
fi
|
|
|
|
# SSH connection test
|
|
echo ""
|
|
SSH_OUTPUT=$(ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
|
|
-o StrictHostKeyChecking=no -o ConnectTimeout=5 \
|
|
git@"$GITEA_HOST" 2>&1 || true)
|
|
if echo "$SSH_OUTPUT" | grep -q "successfully authenticated"; then
|
|
echo " $ICON_DONE SSH test: connected ✅"
|
|
echo " $ICON_INFO $(echo "$SSH_OUTPUT" | grep "successfully authenticated")"
|
|
else
|
|
echo " $ICON_ERROR SSH test: failed"
|
|
echo " $ICON_INFO $SSH_OUTPUT"
|
|
fi
|
|
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━"
|
|
exit 0
|
|
fi
|
|
|
|
START=$(date +%s)
|
|
SETUP_SUCCESS=true
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Generate SSH Keypair ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR SSH Keypair ━━━"
|
|
|
|
KEY_TITLE="unraid-$(echo "$MY_ID" | tr '[:upper:]' '[:lower:]')"
|
|
KEY_COMMENT="$KEY_TITLE@$(hostname)"
|
|
|
|
if [[ -f "$GITEA_SSH_KEY" ]] && [[ "$FORCE" == false ]]; then
|
|
FINGERPRINT=$(ssh-keygen -lf "${GITEA_SSH_KEY}.pub" 2>/dev/null | awk '{print $2}')
|
|
log "Key already exists — skipping generation (use --force to regenerate)"
|
|
log " Path: $GITEA_SSH_KEY"
|
|
log " Fingerprint: $FINGERPRINT"
|
|
else
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would generate ed25519 key at $GITEA_SSH_KEY"
|
|
warn "DRY RUN — key comment: $KEY_COMMENT"
|
|
else
|
|
mkdir -p "$(dirname "$GITEA_SSH_KEY")"
|
|
chmod 700 "$(dirname "$GITEA_SSH_KEY")"
|
|
|
|
# Remove existing if --force
|
|
[[ "$FORCE" == true ]] && rm -f "$GITEA_SSH_KEY" "${GITEA_SSH_KEY}.pub"
|
|
|
|
if ssh-keygen -t ed25519 -f "$GITEA_SSH_KEY" -C "$KEY_COMMENT" -N "" -q; then
|
|
chmod 600 "$GITEA_SSH_KEY"
|
|
chmod 644 "${GITEA_SSH_KEY}.pub"
|
|
FINGERPRINT=$(ssh-keygen -lf "${GITEA_SSH_KEY}.pub" 2>/dev/null | awk '{print $2}')
|
|
warn "SSH key generated ✅"
|
|
log " Path: $GITEA_SSH_KEY"
|
|
log " Fingerprint: $FINGERPRINT"
|
|
else
|
|
error "Failed to generate SSH key"
|
|
SETUP_SUCCESS=false
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Register Public Key with Gitea ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_LINK Register Key with Gitea ━━━"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would POST public key to $GITEA_API/user/keys"
|
|
warn "DRY RUN — key title: $KEY_TITLE"
|
|
elif [[ "$SETUP_SUCCESS" == true ]]; then
|
|
LOCAL_PUB=$(cat "${GITEA_SSH_KEY}.pub")
|
|
LOCAL_PUB_SHORT=$(awk '{print $1" "$2}' "${GITEA_SSH_KEY}.pub")
|
|
|
|
# Check if already registered
|
|
REGISTERED_KEYS=$(curl -s \
|
|
-H "Authorization: token $GITEA_API_TOKEN" \
|
|
"$GITEA_API/user/keys" 2>/dev/null)
|
|
|
|
if [[ "$FORCE" == false ]] && echo "$REGISTERED_KEYS" | grep -qF "$LOCAL_PUB_SHORT"; then
|
|
log "Public key already registered in Gitea — skipping (use --force to re-register)"
|
|
else
|
|
# If --force, delete the existing key with this title first
|
|
if [[ "$FORCE" == true ]]; then
|
|
EXISTING_ID=$(echo "$REGISTERED_KEYS" | \
|
|
python3 -c "import json,sys; keys=json.load(sys.stdin); [print(k['id']) for k in keys if k.get('title','')=='$KEY_TITLE']" 2>/dev/null)
|
|
if [[ -n "$EXISTING_ID" ]]; then
|
|
curl -s -X DELETE \
|
|
-H "Authorization: token $GITEA_API_TOKEN" \
|
|
"$GITEA_API/user/keys/$EXISTING_ID" >/dev/null 2>&1
|
|
log "Removed existing key '$KEY_TITLE' (ID: $EXISTING_ID)"
|
|
fi
|
|
fi
|
|
|
|
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
|
-H "Authorization: token $GITEA_API_TOKEN" \
|
|
-H "Content-Type: application/json" \
|
|
-d "{\"key\":$(echo "$LOCAL_PUB" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read().strip()))'),\"read_only\":false,\"title\":\"$KEY_TITLE\"}" \
|
|
"$GITEA_API/user/keys" 2>/dev/null)
|
|
|
|
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
|
|
BODY=$(echo "$RESPONSE" | head -n -1)
|
|
|
|
if [[ "$HTTP_CODE" == "201" ]]; then
|
|
KEY_ID=$(echo "$BODY" | python3 -c "import json,sys; print(json.load(sys.stdin)['id'])" 2>/dev/null)
|
|
warn "Public key registered in Gitea ✅ (ID: $KEY_ID, title: $KEY_TITLE)"
|
|
elif [[ "$HTTP_CODE" == "422" ]]; then
|
|
warn "Key already registered under a different title — skipping"
|
|
log "Response: $BODY"
|
|
else
|
|
error "Failed to register key (HTTP $HTTP_CODE)"
|
|
log "Response: $BODY"
|
|
SETUP_SUCCESS=false
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ SSH Connection Test ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR SSH Connection Test ━━━"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would test: ssh -i $GITEA_SSH_KEY -p $SSH_PORT git@$GITEA_HOST"
|
|
else
|
|
SSH_OUTPUT=$(ssh -i "$GITEA_SSH_KEY" -p "$SSH_PORT" \
|
|
-o StrictHostKeyChecking=no \
|
|
-o ConnectTimeout=10 \
|
|
git@"$GITEA_HOST" 2>&1 || true)
|
|
|
|
if echo "$SSH_OUTPUT" | grep -q "successfully authenticated"; then
|
|
AUTHED_AS=$(echo "$SSH_OUTPUT" | grep "successfully authenticated" | sed 's/.*authenticated as //')
|
|
warn "SSH connection successful ✅ — authenticated as $AUTHED_AS"
|
|
else
|
|
error "SSH connection test failed"
|
|
log "Output: $SSH_OUTPUT"
|
|
warn "Key may not yet be active — Gitea sometimes takes a moment to pick up new keys"
|
|
warn "Run --status in 30 seconds to recheck"
|
|
SETUP_SUCCESS=false
|
|
fi
|
|
fi
|
|
|
|
# ==============================================================================================
|
|
# ━━━ SSH Config Entry ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━ $ICON_GEAR SSH Config Entry ━━━"
|
|
|
|
SSH_CONFIG="$HOME/.ssh/config"
|
|
ENTRY_MARKER="Host gitea-${MY_ID,,}"
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — would add Host entry to $SSH_CONFIG:"
|
|
warn " Host gitea-${MY_ID,,}"
|
|
warn " HostName $GITEA_HOST"
|
|
warn " Port $SSH_PORT"
|
|
warn " User git"
|
|
warn " IdentityFile $GITEA_SSH_KEY"
|
|
elif grep -qF "$ENTRY_MARKER" "$SSH_CONFIG" 2>/dev/null && [[ "$FORCE" == false ]]; then
|
|
log "SSH config entry already exists — skipping (use --force to update)"
|
|
else
|
|
if [[ "$FORCE" == true ]]; then
|
|
# Remove old entry block if present
|
|
if grep -qF "$ENTRY_MARKER" "$SSH_CONFIG" 2>/dev/null; then
|
|
python3 - "$SSH_CONFIG" "$ENTRY_MARKER" <<'PYEOF'
|
|
import sys
|
|
path, marker = sys.argv[1], sys.argv[2]
|
|
with open(path) as f:
|
|
lines = f.readlines()
|
|
out, skip = [], False
|
|
for line in lines:
|
|
if line.strip() == marker:
|
|
skip = True
|
|
elif skip and line.startswith('Host '):
|
|
skip = False
|
|
if not skip:
|
|
out.append(line)
|
|
with open(path, 'w') as f:
|
|
f.writelines(out)
|
|
PYEOF
|
|
log "Removed old SSH config entry"
|
|
fi
|
|
fi
|
|
|
|
mkdir -p "$(dirname "$SSH_CONFIG")"
|
|
{
|
|
echo ""
|
|
echo "Host gitea-${MY_ID,,}"
|
|
echo " HostName $GITEA_HOST"
|
|
echo " Port $SSH_PORT"
|
|
echo " User git"
|
|
echo " IdentityFile $GITEA_SSH_KEY"
|
|
} >> "$SSH_CONFIG"
|
|
chmod 600 "$SSH_CONFIG"
|
|
log "SSH config entry added: Host gitea-${MY_ID,,} ✅"
|
|
fi
|
|
|
|
END=$(date +%s)
|
|
|
|
# ==============================================================================================
|
|
# ━━━ Summary ━━━
|
|
# ==============================================================================================
|
|
echo ""
|
|
echo "━━━━━ $ICON_SUMMARY GITEA SSH SETUP SUMMARY ━━━━━"
|
|
echo "$ICON_HOST Identity: $MY_ID ($LOCAL_SERVER_NAME)"
|
|
echo "$ICON_GEAR Key: $GITEA_SSH_KEY"
|
|
echo "$ICON_LINK Gitea: $GITEA_HOST:$SSH_PORT"
|
|
echo "$ICON_GEAR Key title: $KEY_TITLE"
|
|
echo "$ICON_TIME Duration: $(format_duration $(( END - START )))"
|
|
echo ""
|
|
|
|
if [[ "$DRY_RUN" == true ]]; then
|
|
warn "DRY RUN — no changes made"
|
|
elif [[ "$SETUP_SUCCESS" == true ]]; then
|
|
echo "$ICON_DONE Status: done ✅"
|
|
echo ""
|
|
echo "Git remote push:"
|
|
echo " GIT_SSH_COMMAND='ssh -i $GITEA_SSH_KEY -p $SSH_PORT' git push"
|
|
else
|
|
echo "$ICON_ERROR Status: SETUP HAD ERRORS — check output above"
|
|
notify "Gitea SSH setup errors on $(hostname) ($MY_ID)" \
|
|
"Gitea SSH Setup" "warning"
|
|
exit 1
|
|
fi
|
|
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|