mirror of
https://github.com/acedanger/shell.git
synced 2025-12-06 07:50:11 -08:00
225 lines
7.1 KiB
Bash
Executable File
225 lines
7.1 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
# backup-gitea.sh - Backup Gitea, Postgres, and Runner
|
|
# Enhanced for NAS support and Runner integration
|
|
|
|
set -e
|
|
|
|
# Colors for output
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[0;33m'
|
|
RED='\033[0;31m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# ==========================================
|
|
# 1. CONFIGURATION
|
|
# ==========================================
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
COMPOSE_DIR="/home/acedanger/docker/gitea"
|
|
|
|
BACKUP_DIR="/home/acedanger/backups/gitea"
|
|
NAS_DIR="/mnt/share/media/backups/gitea"
|
|
COMPOSE_FILE="$COMPOSE_DIR/docker-compose.yml"
|
|
LOG_FILE="$SCRIPT_DIR/logs/gitea-backup.log"
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Ensure directories exist
|
|
mkdir -p "$(dirname "$LOG_FILE")"
|
|
mkdir -p "$BACKUP_DIR"
|
|
|
|
# Load .env variables from the COMPOSE_DIR to ensure DB credentials match
|
|
if [ -f "$COMPOSE_DIR/.env" ]; then
|
|
export $(grep -v '^#' "$COMPOSE_DIR/.env" | xargs)
|
|
fi
|
|
|
|
# Logging function (Fixed to interpret colors correctly)
|
|
log() {
|
|
# Print to console with colors (interpreting escapes with -e)
|
|
echo -e "$(date '+%Y-%m-%d %H:%M:%S') - $1"
|
|
# Strip colors for the log file to keep it clean
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | sed 's/\x1b\[[0-9;]*m//g' >> "$LOG_FILE"
|
|
}
|
|
|
|
# Display usage information
|
|
usage() {
|
|
echo "Usage: $0 [OPTIONS]"
|
|
echo ""
|
|
echo "Backup Gitea data, Runner, and PostgreSQL database"
|
|
echo "Options:"
|
|
echo " -h, --help Show this help message"
|
|
echo " -l, --list List available local backups"
|
|
echo " -n, --no-nas Skip copying to NAS (Local only)"
|
|
echo ""
|
|
}
|
|
|
|
# Check dependencies
|
|
check_dependencies() {
|
|
if ! command -v docker &> /dev/null; then
|
|
log "${RED}Error: docker is not installed.${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Verify the compose file exists where we expect it
|
|
if [ ! -f "$COMPOSE_FILE" ]; then
|
|
log "${RED}Error: Docker compose file not found at: $COMPOSE_FILE${NC}"
|
|
log "${YELLOW}Please update the COMPOSE_DIR variable in this script.${NC}"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# List available backups
|
|
list_backups() {
|
|
echo -e "${BLUE}=== Available Gitea Backups ===${NC}"
|
|
ls -lh "$BACKUP_DIR"/*.tar.gz 2>/dev/null || echo "No backups found."
|
|
}
|
|
|
|
# ==========================================
|
|
# 2. BACKUP LOGIC
|
|
# ==========================================
|
|
perform_backup() {
|
|
local SKIP_NAS=$1
|
|
|
|
log "Starting backup process..."
|
|
|
|
# Switch context to the directory where Gitea is actually running
|
|
cd "$COMPOSE_DIR" || { log "${RED}Could not change to directory $COMPOSE_DIR${NC}"; exit 1; }
|
|
|
|
# PRE-FLIGHT CHECK: Is the DB actually running?
|
|
if ! docker compose ps --services --filter "status=running" | grep -q "db"; then
|
|
log "${RED}CRITICAL ERROR: The 'db' service is not running in $COMPOSE_DIR${NC}"
|
|
log "${YELLOW}Docker sees these running services:$(docker compose ps --services --filter "status=running" | xargs)${NC}"
|
|
log "Aborting backup to prevent empty files."
|
|
exit 1
|
|
fi
|
|
|
|
# Create a temporary staging directory for this specific backup
|
|
TEMP_BACKUP_PATH="$BACKUP_DIR/temp_$DATE"
|
|
mkdir -p "$TEMP_BACKUP_PATH"
|
|
|
|
# 1. Backup Database
|
|
log "Step 1/5: Dumping PostgreSQL database..."
|
|
# Using -T to disable TTY allocation (fixes some cron issues)
|
|
if docker compose exec -T db pg_dump -U "${POSTGRES_USER:-gitea}" "${POSTGRES_DB:-gitea}" > "$TEMP_BACKUP_PATH/database.sql"; then
|
|
echo -e "${GREEN}Database dump successful.${NC}"
|
|
else
|
|
log "${RED}Database dump failed!${NC}"
|
|
rm -rf "$TEMP_BACKUP_PATH"
|
|
exit 1
|
|
fi
|
|
|
|
# 2. Backup Gitea Data
|
|
log "Step 2/5: Backing up Gitea data volume..."
|
|
docker run --rm \
|
|
--volumes-from gitea \
|
|
-v "$TEMP_BACKUP_PATH":/backup \
|
|
alpine tar czf /backup/gitea_data.tar.gz -C /data .
|
|
|
|
# 3. Backup Runner Data
|
|
log "Step 3/5: Backing up Runner data..."
|
|
# Check if runner exists before backing up to avoid errors if you removed it
|
|
if docker compose ps --services | grep -q "runner"; then
|
|
docker run --rm \
|
|
--volumes-from gitea-runner \
|
|
-v "$TEMP_BACKUP_PATH":/backup \
|
|
alpine tar czf /backup/runner_data.tar.gz -C /data .
|
|
else
|
|
log "${YELLOW}Runner service not found, skipping runner backup.${NC}"
|
|
fi
|
|
|
|
# 4. Config Files & Restore Script
|
|
log "Step 4/5: Archiving configurations and generating restore script..."
|
|
cp "$COMPOSE_FILE" "$TEMP_BACKUP_PATH/"
|
|
[ -f ".env" ] && cp ".env" "$TEMP_BACKUP_PATH/"
|
|
|
|
# Generate the Restore Script inside the backup folder
|
|
create_restore_script "$TEMP_BACKUP_PATH"
|
|
|
|
# 5. Final Archive Creation
|
|
log "Step 5/5: Compressing full backup..."
|
|
FINAL_ARCHIVE_NAME="gitea_backup_$DATE.tar.gz"
|
|
|
|
# Tar the temp folder into one final file
|
|
tar -czf "$BACKUP_DIR/$FINAL_ARCHIVE_NAME" -C "$TEMP_BACKUP_PATH" .
|
|
|
|
# Remove temp folder
|
|
rm -rf "$TEMP_BACKUP_PATH"
|
|
|
|
log "${GREEN}Local Backup completed: $BACKUP_DIR/$FINAL_ARCHIVE_NAME${NC}"
|
|
|
|
# 6. NAS Transfer
|
|
if [[ "$SKIP_NAS" != "true" ]]; then
|
|
if [ -d "$NAS_DIR" ]; then
|
|
log "Copying to NAS ($NAS_DIR)..."
|
|
cp "$BACKUP_DIR/$FINAL_ARCHIVE_NAME" "$NAS_DIR/"
|
|
if [ $? -eq 0 ]; then
|
|
log "${GREEN}NAS Copy Successful.${NC}"
|
|
else
|
|
log "${RED}NAS Copy Failed. Check permissions on $NAS_DIR${NC}"
|
|
fi
|
|
else
|
|
log "${YELLOW}NAS Directory $NAS_DIR not found. Skipping NAS copy.${NC}"
|
|
fi
|
|
else
|
|
log "NAS copy skipped by user request."
|
|
fi
|
|
|
|
# 7. Cleanup Old Local Backups (Keep 7 Days)
|
|
find "$BACKUP_DIR" -name "gitea_backup_*.tar.gz" -mtime +7 -exec rm {} \;
|
|
log "Cleanup of old local backups complete."
|
|
}
|
|
|
|
# Function to generate the restore script
|
|
create_restore_script() {
|
|
local TARGET_DIR=$1
|
|
cat > "$TARGET_DIR/restore.sh" << 'EOF'
|
|
#!/bin/bash
|
|
# RESTORE SCRIPT
|
|
echo "WARNING: This will overwrite your current Gitea/DB/Runner data."
|
|
read -p "Are you sure? (y/N): " -n 1 -r
|
|
echo
|
|
if [[ ! $REPLY =~ ^[Yy]$ ]]; then exit 1; fi
|
|
|
|
docker compose down
|
|
|
|
echo "Restoring Database Volume..."
|
|
docker compose up -d db
|
|
echo "Waiting for DB to initialize..."
|
|
sleep 15
|
|
cat database.sql | docker compose exec -T db psql -U ${POSTGRES_USER:-gitea} -d ${POSTGRES_DB:-gitea}
|
|
|
|
echo "Restoring Gitea Files..."
|
|
docker run --rm --volumes-from gitea -v $(pwd):/backup alpine tar xzf /backup/gitea_data.tar.gz -C /data
|
|
|
|
echo "Restoring Runner Files..."
|
|
docker run --rm --volumes-from gitea-runner -v $(pwd):/backup alpine tar xzf /backup/runner_data.tar.gz -C /data
|
|
|
|
echo "Restarting stack..."
|
|
docker compose up -d
|
|
echo "Restore Complete."
|
|
EOF
|
|
chmod +x "$TARGET_DIR/restore.sh"
|
|
}
|
|
|
|
# ==========================================
|
|
# 3. EXECUTION FLOW
|
|
# ==========================================
|
|
|
|
check_dependencies
|
|
|
|
# Parse Arguments
|
|
if [ $# -eq 0 ]; then
|
|
perform_backup "false"
|
|
exit 0
|
|
fi
|
|
|
|
while [[ "$#" -gt 0 ]]; do
|
|
case $1 in
|
|
-h|--help) usage; exit 0 ;;
|
|
-l|--list) list_backups; exit 0 ;;
|
|
-n|--no-nas) perform_backup "true"; exit 0 ;;
|
|
*) echo "Unknown parameter: $1"; usage; exit 1 ;;
|
|
esac
|
|
shift
|
|
done |