#!/usr/bin/env bash # ./deploy.sh public # ./deploy.sh backend # ./deploy.sh all # ./deploy.sh list-backups public # ./deploy.sh rollback public # ./deploy.sh rollback public PUBLIC-20260216-143015-server01 set -Eeuo pipefail ######################################## # Farben ######################################## RED="\e[31m" GREEN="\e[32m" YELLOW="\e[33m" BLUE="\e[34m" BOLD="\e[1m" RESET="\e[0m" ######################################## # Logging ######################################## log_info() { echo -e "${BLUE}[INFO]${RESET} $1"; } log_success() { echo -e "${GREEN}[OK]${RESET} $1"; } log_warn() { echo -e "${YELLOW}[WARN]${RESET} $1"; } log_error() { echo -e "${RED}[ERROR]${RESET} $1"; } trap 'log_error "Fehler in Zeile $LINENO"' ERR ######################################## # Konfiguration ######################################## BASE_DIR="/data/WEBSERVER" BACKUP_DIR="$BASE_DIR/BACKUP" PUBLIC_REPO="http://gitea.blackpi.ipv64.net/bruno/Web-Public.git" BACKEND_REPO="http://gitea.blackpi.ipv64.net/bruno/Backend.git" TIMESTAMP=$(date +"%Y%m%d-%H%M%S") HOSTNAME=$(hostname) ######################################## # Root-Check ######################################## if [[ "$EUID" -ne 0 ]]; then log_error "Script muss als root laufen!" exit 1 fi ######################################## # Lockfile ######################################## exec 9>/var/lock/webdeploy.lock flock -n 9 || { log_error "Deploy läuft bereits!"; exit 1; } ######################################## # Helper ######################################## ensure_directory() { [[ -d "$1" ]] || mkdir -p "$1" } restart_container() { local container="$1" if docker ps -a --format '{{.Names}}' | grep -q "^${container}$"; then docker restart "$container" >/dev/null log_success "Container $container neu gestartet." else log_warn "Container $container existiert nicht." fi } backup_current() { local name="$1" local target="$BASE_DIR/$name" local rsync_server="10.233.1.89" local rsync_user="root" local rsync_dir="/root/BACKUP/NFS/WEBSERVER/" if [[ -d "$target" ]]; then ensure_directory "$BACKUP_DIR" local backup_name="${name}-${TIMESTAMP}-${HOSTNAME}" mv "$target" "$BACKUP_DIR/$backup_name" log_info "$name wurde lokal gesichert: $backup_name" # rsync ausführen if rsync -avz "$BACKUP_DIR/$backup_name" "${rsync_user}@${rsync_server}:${rsync_dir}"; then log_info "$backup_name wurde erfolgreich auf den Backup-Server: ${rsync_user}@${rsync_server}:${rsync_dir} kopiert." else log_error "Fehler beim Kopieren von $backup_name auf den Backup-Server!" fi fi } deploy() { local name="$1" local repo="$2" local container="$3" local target="$BASE_DIR/$name" log_info "Deploying $name ..." backup_current "$name" git clone "$repo" "$target" if [[ "$name" == "BACKEND" ]]; then git -C "$target" checkout live fi restart_container "$container" log_success "$name erfolgreich deployed." } ######################################## # Rollback ######################################## list_backups() { local name="$1" log_info "Verfügbare Backups für $name:" ls -ldt "$BACKUP_DIR/${name}-"* 2>/dev/null || log_warn "Keine Backups gefunden." } rollback() { local name="$1" local container="$2" local specific_backup="${3:-}" ensure_directory "$BACKUP_DIR" local current="$BASE_DIR/$name" local backup_to_restore if [[ -n "$specific_backup" ]]; then backup_to_restore="$BACKUP_DIR/$specific_backup" else backup_to_restore=$(ls -dt "$BACKUP_DIR/${name}-"* 2>/dev/null | head -n1) fi if [[ -z "${backup_to_restore:-}" || ! -d "$backup_to_restore" ]]; then log_error "Kein gültiges Backup gefunden!" exit 1 fi log_warn "Rollback von $name → $(basename "$backup_to_restore")" # 🔴 Container stoppen if docker ps --format '{{.Names}}' | grep -q "^${container}$"; then log_info "Stoppe Container $container ..." docker stop "$container" >/dev/null fi # 🔴 Verzeichnis sicher löschen if [[ -d "$current" ]]; then log_info "Entferne aktuelles Verzeichnis ..." rm -rf "$current" fi # 🔴 Backup zurückspielen mv "$backup_to_restore" "$current" # 🟢 Container neu starten docker start "$container" >/dev/null log_success "Rollback abgeschlossen." } ######################################## # Fetch ######################################## fetch_changes() { local name="$1" local target="$BASE_DIR/$name" if [[ -d "$target/.git" ]]; then git -C "$target" fetch origin git -C "$target" diff HEAD..origin/main --name-only else log_warn "$name ist nicht installiert." fi } ######################################## # CLI ######################################## ACTION="${1:-}" TARGET="${2:-}" OPTIONAL="${3:-}" case "$ACTION" in all) deploy "PUBLIC" "$PUBLIC_REPO" "webserver_public" deploy "BACKEND" "$BACKEND_REPO" "webserver_backend" ;; public) deploy "PUBLIC" "$PUBLIC_REPO" "webserver_public" ;; backend) deploy "BACKEND" "$BACKEND_REPO" "webserver_backend" ;; rollback) case "$TARGET" in public) rollback "PUBLIC" "webserver_public" "$OPTIONAL" ;; backend) rollback "BACKEND" "webserver_backend" "$OPTIONAL" ;; *) echo "Usage: $0 rollback {public|backend} [backup-name]" exit 1 ;; esac ;; list-backups) case "$TARGET" in public) list_backups "PUBLIC" ;; backend) list_backups "BACKEND" ;; *) echo "Usage: $0 list-backups {public|backend}" exit 1 ;; esac ;; fetch) fetch_changes "PUBLIC" fetch_changes "BACKEND" ;; *) echo "Usage:" echo " $0 {all|public|backend}" echo " $0 rollback {public|backend} [backup-name]" echo " $0 list-backups {public|backend}" echo " $0 fetch" exit 1 ;; esac