Files
2026-10-17 04:10:15 -03:00

592 lines
23 KiB
Bash

#!/usr/bin/env bash
#
# backup_restore.sh
#
# Companion to setup-git-server.sh and backup_create.sh: restores a backup zip
# made by backup_create.sh (or any plain `gitea dump` archive) into a Gitea
# instance that was installed by setup-git-server.sh, keeping its layout:
#
# binary /usr/local/bin/gitea
# config /etc/gitea/app.ini
# work dir /var/lib/gitea (data/, custom/, data/gitea.db)
# repositories ~git/gitea-repositories
# database SQLite
#
# What it does:
# 1. unpacks the backup into a staging directory, checks that the installed
# Gitea is not older than the one that made it, and imports the SQL into a
# brand-new SQLite file there -- nothing on the server is touched until
# all of that has worked;
# 2. stops Gitea and moves the current data / custom / repositories aside
# (as <name>.bak.<timestamp>);
# 3. puts the backup's data, custom files, repositories and database in place;
# 4. copies the secrets (SECRET_KEY, JWT secrets, ...) from the backup's
# app.ini into the current one, so 2FA, OAuth2 and tokens keep working,
# while the domain / ports / paths of this server stay as they are;
# 5. runs 'gitea migrate', regenerates the repo hooks and authorized_keys,
# and starts Gitea again.
#
# On a fresh machine, install a Gitea version equal to (or newer than) the one
# that made the backup first (backup_create.sh prints the exact command):
# sudo ./setup-git-server.sh --version <x.y.z>
#
# Usage:
# sudo ./backup_restore.sh [options] <backup-file>
#
# Run with --help for the full list of options.
set -euo pipefail
# ----------------------------------------------------------------------------
# Defaults (same variables as setup-git-server.sh, overridable via env vars)
# ----------------------------------------------------------------------------
GITEA_USER="${GITEA_USER:-git}"
GITEA_HOME="${GITEA_HOME:-/home/${GITEA_USER}}"
GITEA_WORK_DIR="${GITEA_WORK_DIR:-/var/lib/gitea}"
GITEA_CONFIG_DIR="${GITEA_CONFIG_DIR:-/etc/gitea}"
GITEA_CONFIG="${GITEA_CONFIG_DIR}/app.ini"
GITEA_BIN="/usr/local/bin/gitea"
DUMP_FILE=""
ASSUME_YES="false"
STAGE_BASE="${STAGE_BASE:-}"
# Filled in while the script runs
DISTRO_FAMILY=""
GITEA_GROUP=""
REPO_ROOT=""
DB_PATH=""
STAGE=""
DUMP_ROOT=""
DUMP_INI=""
MANIFEST=""
BACKUP_VERSION=""
BACKUP_CREATED=""
BACKUP_HOST=""
DUMP_USERS="?"
DUMP_REPOS="?"
TS=""
SWAPPED="false"
BACKUPS=()
# ----------------------------------------------------------------------------
# Small helpers
# ----------------------------------------------------------------------------
C_GREEN='\033[0;32m'; C_YELLOW='\033[0;33m'; C_RED='\033[0;31m'; C_RESET='\033[0m'
log() { printf '%b\n' "${C_GREEN}[+]${C_RESET} $*"; }
warn() { printf '%b\n' "${C_YELLOW}[!]${C_RESET} $*" >&2; }
error_exit() { printf '%b\n' "${C_RED}[x]${C_RESET} $*" >&2; exit 1; }
cleanup() {
local rc=$?
if [[ -n "${STAGE:-}" && -d "$STAGE" ]]; then
rm -rf -- "$STAGE"
fi
if [[ $rc -ne 0 && "$SWAPPED" == "true" ]]; then
warn "The restore stopped part-way. Your previous state was kept here:"
if (( ${#BACKUPS[@]} )); then
printf ' %s\n' "${BACKUPS[@]}" >&2
fi
warn "To roll back: stop gitea, delete the restored item and rename its .bak.${TS} copy back."
fi
}
trap cleanup EXIT
print_help() {
cat <<'EOF'
Usage: sudo ./backup_restore.sh [options] <backup-file>
Restores a backup made by backup_create.sh (a .zip; plain `gitea dump`
archives in .zip, .tar, .tar.gz, .tar.bz2, .tar.xz or .tar.zst work too)
into a Gitea instance installed by setup-git-server.sh.
Options:
-y, --yes Don't ask for confirmation
--tmp-dir <dir> Where the backup is unpacked (default: the Gitea work
dir; needs free space of about twice the backup size)
-h, --help Show this help and exit
The environment variables of setup-git-server.sh (GITEA_USER, GITEA_HOME,
GITEA_WORK_DIR, GITEA_CONFIG_DIR) are honoured if you used non-default ones.
Only SQLite backups can be restored (that is what setup-git-server.sh uses).
EOF
}
require_root() {
if [[ "${EUID}" -ne 0 ]]; then
error_exit "Please run this script as root, e.g.: sudo $0"
fi
command -v systemctl &>/dev/null || error_exit "This script requires systemd."
[[ -d /run/systemd/system ]] \
|| error_exit "systemd is not running as init here (container/chroot/WSL?). Run this on a real VM or host."
}
need_arg() { # usage: need_arg "$@" -- an option that takes a value must have one
[[ $# -ge 2 ]] || error_exit "Option $1 needs a value (see --help)"
}
parse_args() {
while [[ $# -gt 0 ]]; do
case "$1" in
-y|--yes) ASSUME_YES="true"; shift ;;
--tmp-dir) need_arg "$@"; STAGE_BASE="$2"; shift 2 ;;
-h|--help) print_help; exit 0 ;;
-*) error_exit "Unknown option: $1 (see --help)" ;;
*)
[[ -z "$DUMP_FILE" ]] || error_exit "Only one backup file can be given (got '${DUMP_FILE}' and '$1')."
DUMP_FILE="$1"; shift ;;
esac
done
if [[ -z "$DUMP_FILE" ]]; then
print_help >&2
exit 1
fi
[[ -f "$DUMP_FILE" && -r "$DUMP_FILE" ]] || error_exit "Cannot read backup file: ${DUMP_FILE}"
DUMP_FILE="$(readlink -f -- "$DUMP_FILE")"
}
# ----------------------------------------------------------------------------
# app.ini helpers (section-aware, values are copied verbatim)
# ----------------------------------------------------------------------------
ini_get() { # usage: ini_get FILE SECTION KEY (SECTION "" = top of the file)
local file="$1" section="$2" key="$3"
[[ -r "$file" ]] || return 0
awk -v want="$section" -v key="$key" '
/^[[:space:]]*[;#]/ { next }
/^[[:space:]]*\[/ { s = $0; gsub(/^[[:space:]]*\[|\][[:space:]]*$/, "", s); cur = s; next }
tolower(cur) == tolower(want) {
i = index($0, "=")
if (!i) next
k = substr($0, 1, i - 1); gsub(/[[:space:]]/, "", k)
if (k == key) {
v = substr($0, i + 1)
sub(/^[[:space:]]+/, "", v); sub(/[[:space:]]+$/, "", v)
print v; exit
}
}' "$file"
}
ini_set() { # usage: ini_set FILE SECTION KEY VALUE -- fails if KEY isn't in SECTION
local file="$1" section="$2" key="$3" value="$4" out
out="$(mktemp)"
# The value goes in through the environment so '&', '\' etc. stay literal.
if ! NEW_VALUE="$value" awk -v want="$section" -v key="$key" '
/^[[:space:]]*\[/ { s = $0; gsub(/^[[:space:]]*\[|\][[:space:]]*$/, "", s); cur = s }
!done && !/^[[:space:]]*[;#]/ && tolower(cur) == tolower(want) {
i = index($0, "=")
if (i) {
k = substr($0, 1, i - 1); gsub(/[[:space:]]/, "", k)
if (k == key) { print key " = " ENVIRON["NEW_VALUE"]; done = 1; next }
}
}
{ print }
END { exit !done }' "$file" > "$out"; then
rm -f "$out"
return 1
fi
cat "$out" > "$file" # 'cat >' keeps the owner/mode of the existing file
rm -f "$out"
}
# ----------------------------------------------------------------------------
# Backup info file / version helpers
# ----------------------------------------------------------------------------
manifest_get() { # usage: manifest_get KEY -- read from gitea-backup-info.txt, never sourced
[[ -n "$MANIFEST" && -r "$MANIFEST" ]] || return 0
sed -nE "s/^$1=(.*)\$/\\1/p" "$MANIFEST" | head -n1 || true
}
installed_version() {
"$GITEA_BIN" --version 2>/dev/null \
| sed -nE 's/^Gitea version v?([0-9]+(\.[0-9]+)+).*/\1/p' \
| head -n1 || true
}
version_lt() { # usage: version_lt A B -> true if A is an older version than B
[[ "$1" != "$2" && "$(printf '%s\n%s\n' "$1" "$2" | sort -V | head -n1)" == "$1" ]]
}
# ----------------------------------------------------------------------------
# Detection helpers / dependencies
# ----------------------------------------------------------------------------
detect_distro_family() {
[[ -r /etc/os-release ]] || error_exit "Cannot read /etc/os-release; unsupported system."
# shellcheck disable=SC1091
. /etc/os-release
case "${ID:-}" in
ubuntu|debian|linuxmint|pop|raspbian) DISTRO_FAMILY="debian" ;;
fedora|rhel|centos|rocky|almalinux|nobara) DISTRO_FAMILY="fedora" ;;
arch|archarm|manjaro|endeavouros) DISTRO_FAMILY="arch" ;;
*)
case "${ID_LIKE:-}" in
*debian*) DISTRO_FAMILY="debian" ;;
*fedora*|*rhel*) DISTRO_FAMILY="fedora" ;;
*arch*) DISTRO_FAMILY="arch" ;;
*) error_exit "Unsupported distro: ${PRETTY_NAME:-${ID:-unknown}}. This script supports Arch, Debian, Ubuntu and Fedora." ;;
esac
;;
esac
}
install_dependencies() {
# Only what's missing: the sqlite3 CLI (to import the SQL dump) and unzip.
local pkgs=()
command -v unzip &>/dev/null || pkgs+=(unzip)
if ! command -v sqlite3 &>/dev/null; then
detect_distro_family
case "$DISTRO_FAMILY" in
debian) pkgs+=(sqlite3) ;;
*) pkgs+=(sqlite) ;;
esac
fi
(( ${#pkgs[@]} )) || return 0
[[ -n "$DISTRO_FAMILY" ]] || detect_distro_family
log "Installing dependencies: ${pkgs[*]}..."
case "$DISTRO_FAMILY" in
debian)
export DEBIAN_FRONTEND=noninteractive
apt-get update
apt-get install -y "${pkgs[@]}"
;;
fedora) dnf install -y "${pkgs[@]}" ;;
# Never do a partial upgrade (-Sy without -u) on Arch.
arch) pacman -Syu --noconfirm --needed "${pkgs[@]}" ;;
esac
}
run_as_gitea() {
# Run a command as the service user, from a directory it can read, with the
# same work dir the service uses.
( cd "$GITEA_WORK_DIR" && sudo -H -u "$GITEA_USER" env GITEA_WORK_DIR="$GITEA_WORK_DIR" "$@" )
}
# ----------------------------------------------------------------------------
# Restore steps
# ----------------------------------------------------------------------------
check_installation() {
[[ -x "$GITEA_BIN" ]] \
|| error_exit "Gitea is not installed at ${GITEA_BIN}. Run setup-git-server.sh first."
[[ -r "$GITEA_CONFIG" ]] \
|| error_exit "${GITEA_CONFIG} not found. Run setup-git-server.sh first."
id -u "$GITEA_USER" &>/dev/null \
|| error_exit "User '${GITEA_USER}' does not exist. Run setup-git-server.sh first."
command -v sudo &>/dev/null || error_exit "sudo is required (setup-git-server.sh installs it)."
GITEA_HOME="$(getent passwd "$GITEA_USER" | cut -d: -f6)"
GITEA_GROUP="$(id -gn "$GITEA_USER")"
local db_type
db_type="$(ini_get "$GITEA_CONFIG" database DB_TYPE | tr -d '"`')"
[[ "${db_type,,}" == "sqlite3" ]] \
|| error_exit "${GITEA_CONFIG} uses DB_TYPE '${db_type}'; this script restores into the SQLite layout of setup-git-server.sh."
REPO_ROOT="$(ini_get "$GITEA_CONFIG" repository ROOT)"
: "${REPO_ROOT:=${GITEA_HOME}/gitea-repositories}"
DB_PATH="$(ini_get "$GITEA_CONFIG" database PATH)"
: "${DB_PATH:=${GITEA_WORK_DIR}/data/gitea.db}"
log "Target: $("$GITEA_BIN" --version 2>/dev/null | head -n1 || echo gitea)"
}
extract_dump() {
: "${STAGE_BASE:=$GITEA_WORK_DIR}"
mkdir -p -- "$STAGE_BASE"
STAGE="$(mktemp -d -p "$STAGE_BASE" .restore.XXXXXX)"
mkdir -p -- "$STAGE/dump"
log "Unpacking $(basename -- "$DUMP_FILE") (this can take a while for big backups)..."
case "${DUMP_FILE,,}" in
*.zip)
unzip -q -o "$DUMP_FILE" -d "$STAGE/dump" ;;
*.tar|*.tar.gz|*.tgz|*.tar.bz2|*.tbz2|*.tar.xz|*.txz|*.tar.zst)
tar -xf "$DUMP_FILE" -C "$STAGE/dump" ;;
*)
error_exit "Unsupported archive type: ${DUMP_FILE} (expected .zip, .tar, .tar.gz, .tar.bz2, .tar.xz or .tar.zst)." ;;
esac
# The archive normally has everything at its top level; tolerate one wrapper folder.
local sql
sql="$(find "$STAGE/dump" -maxdepth 2 -name gitea-db.sql -print -quit)"
[[ -n "$sql" ]] \
|| error_exit "No gitea-db.sql in the archive -- is this a backup from backup_create.sh / 'gitea dump' (made without --skip-db)?"
DUMP_ROOT="$(dirname -- "$sql")"
}
check_backup_version() {
# Gitea can migrate a database forward, never backward -- so find out now,
# not after the current data has been moved aside.
if [[ ! -f "$DUMP_ROOT/gitea-backup-info.txt" ]]; then
warn "No gitea-backup-info.txt (not made by backup_create.sh?): can't check the Gitea version up front."
return 0
fi
MANIFEST="$DUMP_ROOT/gitea-backup-info.txt"
BACKUP_VERSION="$(manifest_get GITEA_VERSION)"
BACKUP_CREATED="$(manifest_get CREATED_UTC)"
BACKUP_HOST="$(manifest_get HOSTNAME)"
local current
current="$(installed_version)"
if [[ -z "$BACKUP_VERSION" || -z "$current" ]]; then
warn "Could not determine the Gitea versions to compare (backup: '${BACKUP_VERSION:-?}', installed: '${current:-?}')."
return 0
fi
if version_lt "$current" "$BACKUP_VERSION"; then
error_exit "This backup was made with Gitea ${BACKUP_VERSION}, but ${current} is installed, and a database can't be migrated to an older release.
Install the same or a newer version first: sudo ./setup-git-server.sh --version ${BACKUP_VERSION}
Nothing was changed."
fi
if version_lt "$BACKUP_VERSION" "$current"; then
log "Backup is from Gitea ${BACKUP_VERSION}; its database will be migrated to ${current}."
fi
}
inspect_dump() {
local f
for f in "$DUMP_ROOT/app.ini" "$DUMP_ROOT/data/conf/app.ini"; do
if [[ -f "$f" ]]; then DUMP_INI="$f"; break; fi
done
if [[ -n "$DUMP_INI" ]]; then
local src_db_type
src_db_type="$(ini_get "$DUMP_INI" database DB_TYPE | tr -d '"`')"
if [[ -n "$src_db_type" && "${src_db_type,,}" != "sqlite3" ]]; then
error_exit "The backup comes from a '${src_db_type}' database. Only SQLite backups can be restored by this script."
fi
else
warn "No app.ini in the backup: SECRET_KEY can't be carried over, so 2FA secrets and similar will not work after the restore."
fi
# Dumps made by old Gitea versions keep the repositories in a nested zip.
if [[ ! -d "$DUMP_ROOT/repos" && -f "$DUMP_ROOT/gitea-repo.zip" ]]; then
log "Unpacking the nested gitea-repo.zip..."
unzip -q -o "$DUMP_ROOT/gitea-repo.zip" -d "$DUMP_ROOT/repos"
fi
[[ -d "$DUMP_ROOT/repos" ]] || warn "The backup contains no repositories directory."
}
stage_database() {
log "Importing the SQL dump into a fresh SQLite file (nothing on the server is touched yet)..."
sqlite3 -bail "$STAGE/gitea.db" < "$DUMP_ROOT/gitea-db.sql" \
|| error_exit "Importing gitea-db.sql into SQLite failed -- see the error above. Nothing was changed."
DUMP_USERS="$(sqlite3 "$STAGE/gitea.db" 'SELECT COUNT(*) FROM "user";' 2>/dev/null)" \
|| error_exit "The imported database has no 'user' table -- this doesn't look like a Gitea backup. Nothing was changed."
DUMP_REPOS="$(sqlite3 "$STAGE/gitea.db" 'SELECT COUNT(*) FROM repository;' 2>/dev/null || echo '?')"
}
confirm_restore() {
local origin=""
if [[ -n "$BACKUP_CREATED" || -n "$BACKUP_HOST" ]]; then
origin="Made on ${BACKUP_HOST:-?} at ${BACKUP_CREATED:-?} (Gitea ${BACKUP_VERSION:-?})."
fi
cat <<EOF
The backup contains ${DUMP_USERS} user account(s) and ${DUMP_REPOS} repositories.
${origin}
This will REPLACE the current Gitea data on this machine:
database ${DB_PATH}
data ${GITEA_WORK_DIR}/data
custom files ${GITEA_WORK_DIR}/custom
repositories ${REPO_ROOT}
(the current ones are kept next to them as <name>.bak.<timestamp>)
The accounts made by setup-git-server.sh (e.g. the initial admin) are replaced
by the backup's accounts -- you'll log in with the credentials from the old server.
EOF
if [[ "$ASSUME_YES" == "true" ]]; then
return 0
fi
[[ -t 0 ]] || error_exit "Not running interactively; re-run with --yes to confirm."
local answer=""
read -r -p "Continue? [y/N] " answer
[[ "$answer" =~ ^[Yy]([Ee][Ss])?$ ]] || error_exit "Aborted; nothing was changed."
}
stop_service() {
if systemctl is-active --quiet gitea 2>/dev/null; then
log "Stopping Gitea..."
systemctl stop gitea
fi
}
replace_dir() { # usage: replace_dir <staged dir> <destination>
local src="$1" dest="$2"
if [[ -d "$dest" && -n "$(ls -A -- "$dest")" ]]; then
mv -- "$dest" "${dest}.bak.${TS}"
BACKUPS+=("${dest}.bak.${TS}")
elif [[ -d "$dest" ]]; then
rmdir -- "$dest"
fi
mkdir -p -- "$(dirname -- "$dest")"
if [[ -d "$src" ]]; then
mv -- "$src" "$dest"
else
mkdir -p -- "$dest"
fi
}
swap_in_files() {
TS="$(date +%s)"
SWAPPED="true"
log "Moving the current data aside and putting the restored data in place..."
cp -p -- "$GITEA_CONFIG" "${GITEA_CONFIG}.bak.${TS}"
BACKUPS+=("${GITEA_CONFIG}.bak.${TS}")
# Old dumps keep their config under data/conf (the current app.ini stays in
# charge). Sessions, indexes and queues are caches Gitea rebuilds itself and
# can be incompatible between versions, so they are not carried over.
rm -rf -- "$DUMP_ROOT/data/conf" "$DUMP_ROOT/data/sessions" \
"$DUMP_ROOT/data/indexers" "$DUMP_ROOT/data/queues"
# In this layout the database lives in data/ and is moved aside with it;
# if app.ini points somewhere else, set the old file aside separately.
if [[ "$DB_PATH" != "${GITEA_WORK_DIR}/data/"* && -f "$DB_PATH" ]]; then
mv -- "$DB_PATH" "${DB_PATH}.bak.${TS}"
BACKUPS+=("${DB_PATH}.bak.${TS}")
fi
replace_dir "$DUMP_ROOT/data" "${GITEA_WORK_DIR}/data"
replace_dir "$DUMP_ROOT/custom" "${GITEA_WORK_DIR}/custom"
replace_dir "$DUMP_ROOT/repos" "$REPO_ROOT"
mkdir -p -- "$(dirname -- "$DB_PATH")"
rm -f -- "$DB_PATH" "${DB_PATH}-wal" "${DB_PATH}-shm"
mv -- "$STAGE/gitea.db" "$DB_PATH"
}
restore_secrets() {
[[ -n "$DUMP_INI" ]] || return 0
log "Carrying the secrets over from the backup's app.ini..."
# Keep the old config around for reference (mailer, OAuth, etc. settings).
install -m 640 -o root -g "$GITEA_GROUP" "$DUMP_INI" "${GITEA_CONFIG}.from-dump"
# SECRET_KEY encrypts 2FA secrets and other values stored in the database,
# so it must be the one the backup was made with. The JWT secrets and
# INTERNAL_TOKEN are carried over too so tokens issued before keep working.
local pair section key value
for pair in security:SECRET_KEY security:INTERNAL_TOKEN server:LFS_JWT_SECRET oauth2:JWT_SECRET; do
section="${pair%%:*}"
key="${pair##*:}"
value="$(ini_get "$DUMP_INI" "$section" "$key")"
if [[ -z "$value" ]]; then
warn "No ${key} in the backup's app.ini (maybe set through a *_URI option); keeping the current value."
continue
fi
ini_set "$GITEA_CONFIG" "$section" "$key" "$value" \
|| warn "Could not set ${key} in [${section}] of ${GITEA_CONFIG}; set it by hand from ${GITEA_CONFIG}.from-dump."
done
}
fix_permissions() {
log "Fixing ownership and permissions..."
chown -R "${GITEA_USER}:${GITEA_GROUP}" "${GITEA_WORK_DIR}/data" "${GITEA_WORK_DIR}/custom" "$REPO_ROOT"
chmod 750 "${GITEA_WORK_DIR}/data" "${GITEA_WORK_DIR}/custom" "$REPO_ROOT"
chmod 640 "$DB_PATH"
install -d -m 700 -o "$GITEA_USER" -g "$GITEA_GROUP" "${GITEA_HOME}/.ssh"
if [[ ! -f "${GITEA_HOME}/.ssh/authorized_keys" ]]; then
install -m 600 -o "$GITEA_USER" -g "$GITEA_GROUP" /dev/null "${GITEA_HOME}/.ssh/authorized_keys"
fi
if command -v restorecon &>/dev/null; then
restorecon -R "$GITEA_WORK_DIR" "$GITEA_CONFIG_DIR" "$GITEA_HOME" &>/dev/null || true
fi
}
migrate_and_regenerate() {
# Brings the restored schema up to the installed Gitea version.
log "Migrating the database to this Gitea version..."
run_as_gitea "$GITEA_BIN" --config "$GITEA_CONFIG" migrate \
|| error_exit "'gitea migrate' failed. If it says the database is newer than this Gitea, reinstall a newer version with setup-git-server.sh --version <x.y.z> and run this restore again."
# The hooks inside every repo point at the old server's binary/config paths.
log "Regenerating repository hooks..."
run_as_gitea "$GITEA_BIN" --config "$GITEA_CONFIG" admin regenerate hooks \
|| warn "Regenerating hooks failed; pushes may not work. Retry: sudo -u ${GITEA_USER} ${GITEA_BIN} --config ${GITEA_CONFIG} admin regenerate hooks"
# Rebuilds ~/.ssh/authorized_keys from the keys stored in the database.
log "Regenerating authorized_keys from the restored SSH keys..."
run_as_gitea "$GITEA_BIN" --config "$GITEA_CONFIG" admin regenerate keys \
|| warn "Regenerating keys failed; SSH pushes may not work. Retry: sudo -u ${GITEA_USER} ${GITEA_BIN} --config ${GITEA_CONFIG} admin regenerate keys"
}
start_service() {
log "Starting Gitea..."
systemctl start gitea
local port
port="$(ini_get "$GITEA_CONFIG" server HTTP_PORT)"
: "${port:=3000}"
for _ in $(seq 1 60); do
if curl -fsS -o /dev/null "http://127.0.0.1:${port}/api/healthz" 2>/dev/null; then
log "Gitea is up."
return 0
fi
sleep 1
done
warn "Gitea did not answer on port ${port} within 60s. Last log lines:"
journalctl -u gitea -n 30 --no-pager >&2 || true
error_exit "Gitea failed to start -- fix the problem shown above (full log: journalctl -u gitea)."
}
print_summary() {
local domain http_port
domain="$(ini_get "$GITEA_CONFIG" server DOMAIN)"
http_port="$(ini_get "$GITEA_CONFIG" server HTTP_PORT)"
cat <<EOF
============================================================================
Restore finished: ${DUMP_USERS} user(s), ${DUMP_REPOS} repositories
============================================================================
Web UI http://${domain}:${http_port}/
Logins the accounts and passwords from the old server
Things worth checking:
- Clone/push over SSH once, e.g.: git ls-remote git@${domain}:<user>/<repo>.git
- Settings that are NOT carried over (domain, ports, mailer, OAuth, ...)
stay as set up on this server. The old config is saved at:
${GITEA_CONFIG}.from-dump
Your previous state was kept; delete it once you've verified the restore:
EOF
if (( ${#BACKUPS[@]} )); then
printf ' %s\n' "${BACKUPS[@]}"
fi
echo "============================================================================"
}
# ----------------------------------------------------------------------------
# Main
# ----------------------------------------------------------------------------
main() {
require_root
install_dependencies
check_installation
extract_dump
check_backup_version
inspect_dump
stage_database
confirm_restore
stop_service
swap_in_files
restore_secrets
fix_permissions
migrate_and_regenerate
start_service
print_summary
}
# Only run when executed, not when sourced (handy for testing single functions).
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
parse_args "$@"
main
fi