337 lines
8.8 KiB
Bash
337 lines
8.8 KiB
Bash
#!/usr/bin/env bash
|
||
|
||
# ./deploy.sh public
|
||
# ./deploy.sh backend
|
||
# ./deploy.sh server
|
||
# ./deploy.sh all
|
||
# ./deploy.sh fetch
|
||
# ./deploy.sh list-backups public
|
||
# ./deploy.sh rollback public
|
||
# ./deploy.sh rollback public PUBLIC-20260216-143015-server01
|
||
# ./deploy.sh rollback server
|
||
# ./deploy.sh rollback server SERVER-20260216-143015-server01.commit
|
||
|
||
|
||
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/DEPLOYS"
|
||
BACKUP_DIR="$BASE_DIR/MSHOSSA"
|
||
|
||
SERVER_REPO="https://gitea.mrhossa.de/bruno/Server-Mshossa.git"
|
||
|
||
# Zielverzeichnisse
|
||
SERVER_TARGET="/data" # liegt direkt in /data, NICHT in BASE_DIR!
|
||
SERVER_BRANCH="main" # ggf. anpassen (z. B. "live")
|
||
SERVER_CONTAINER="dnsmasq"
|
||
|
||
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 [[ -z "$container" ]]; then
|
||
log_info "Kein Container konfiguriert – überspringe Neustart."
|
||
return
|
||
fi
|
||
|
||
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
|
||
}
|
||
|
||
# Zeigt pro Datei die Anzahl geänderter Zeilen (git diff --stat) und die Commits dazwischen
|
||
show_changes() {
|
||
local target="$1"
|
||
local from="$2"
|
||
local to="$3"
|
||
|
||
local stat
|
||
stat=$(git -C "$target" diff --stat=100 "$from" "$to")
|
||
|
||
if [[ -z "$stat" ]]; then
|
||
log_info "Keine Änderungen."
|
||
return
|
||
fi
|
||
|
||
log_info "Geänderte Dateien:"
|
||
echo "$stat"
|
||
|
||
local commits
|
||
commits=$(git -C "$target" log --oneline "$from..$to")
|
||
if [[ -n "$commits" ]]; then
|
||
log_info "Neue Commits:"
|
||
echo "$commits"
|
||
fi
|
||
}
|
||
|
||
# Warnt, wenn getrackte Dateien lokal geändert wurden (gehen bei reset --hard verloren)
|
||
warn_local_changes() {
|
||
local target="$1"
|
||
|
||
# nur getrackte Dateien: untracked (SEAFILE/, MARIADB/, .pem) bleiben bei reset --hard unberührt
|
||
if [[ -n "$(git -C "$target" status --porcelain --untracked-files=no)" ]]; then
|
||
log_warn "Lokale Änderungen an getrackten Dateien gehen beim Deploy verloren:"
|
||
git -C "$target" diff --stat HEAD
|
||
fi
|
||
}
|
||
|
||
deploy() {
|
||
local name="$1"
|
||
local repo="$2"
|
||
local container="$3"
|
||
local target="$4"
|
||
|
||
log_info "Deploying $name ..."
|
||
|
||
backup_current "$name" "$target"
|
||
|
||
ensure_directory "$(dirname "$target")"
|
||
|
||
git clone "$repo" "$target"
|
||
|
||
if [[ "$name" == "BACKEND" ]]; then
|
||
git -C "$target" checkout live
|
||
fi
|
||
|
||
restart_container "$container"
|
||
log_success "$name erfolgreich deployed."
|
||
}
|
||
|
||
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 target="$3"
|
||
local specific_backup="${4:-}"
|
||
|
||
ensure_directory "$BACKUP_DIR"
|
||
|
||
local current="$target"
|
||
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")"
|
||
|
||
if [[ -n "$container" ]] && docker ps --format '{{.Names}}' | grep -q "^${container}$"; then
|
||
log_info "Stoppe Container $container ..."
|
||
docker stop "$container" >/dev/null
|
||
fi
|
||
|
||
if [[ -d "$current" ]]; then
|
||
log_info "Entferne aktuelles Verzeichnis ..."
|
||
rm -rf "$current"
|
||
fi
|
||
|
||
mv "$backup_to_restore" "$current"
|
||
|
||
if [[ -n "$container" ]]; then
|
||
docker start "$container" >/dev/null
|
||
fi
|
||
log_success "Rollback abgeschlossen."
|
||
}
|
||
|
||
########################################
|
||
# SERVER (Git-basiertes Deploy, liegt direkt in /data)
|
||
########################################
|
||
deploy_server() {
|
||
local target="$SERVER_TARGET"
|
||
|
||
if [[ -d "$target/.git" ]]; then
|
||
# Aktuellen Commit als "Backup" protokollieren
|
||
local current_commit
|
||
current_commit=$(git -C "$target" rev-parse HEAD)
|
||
ensure_directory "$BACKUP_DIR"
|
||
echo "$current_commit" > "$BACKUP_DIR/SERVER-${TIMESTAMP}-${HOSTNAME}.commit"
|
||
log_info "Aktueller Server-Commit gesichert: $current_commit"
|
||
|
||
log_info "Aktualisiere Server-Repo ..."
|
||
git -C "$target" fetch origin
|
||
|
||
show_changes "$target" HEAD "origin/$SERVER_BRANCH"
|
||
warn_local_changes "$target"
|
||
|
||
git -C "$target" reset --hard "origin/$SERVER_BRANCH"
|
||
else
|
||
# Erstinstallation: /data ist nicht leer (WEBSERVER liegt dort), daher über Temp-Ordner
|
||
log_info "Server-Repo noch nicht vorhanden, initialisiere ..."
|
||
local tmp_clone
|
||
tmp_clone=$(mktemp -d)
|
||
git clone "$SERVER_REPO" "$tmp_clone"
|
||
|
||
shopt -s dotglob
|
||
for item in "$tmp_clone"/*; do
|
||
mv "$item" "$target/"
|
||
done
|
||
shopt -u dotglob
|
||
rmdir "$tmp_clone"
|
||
fi
|
||
|
||
restart_container "$SERVER_CONTAINER"
|
||
log_success "SERVER erfolgreich deployed."
|
||
}
|
||
|
||
rollback_server() {
|
||
local specific_backup="${1:-}"
|
||
local target="$SERVER_TARGET"
|
||
local commit_file
|
||
|
||
ensure_directory "$BACKUP_DIR"
|
||
|
||
if [[ -n "$specific_backup" ]]; then
|
||
commit_file="$BACKUP_DIR/$specific_backup"
|
||
else
|
||
commit_file=$(ls -t "$BACKUP_DIR/SERVER-"*.commit 2>/dev/null | head -n1)
|
||
fi
|
||
|
||
if [[ -z "${commit_file:-}" || ! -f "$commit_file" ]]; then
|
||
log_error "Kein gültiges Server-Backup (Commit) gefunden!"
|
||
exit 1
|
||
fi
|
||
|
||
local commit_hash
|
||
commit_hash=$(cat "$commit_file")
|
||
|
||
log_warn "Rollback von SERVER → Commit $commit_hash"
|
||
git -C "$target" fetch origin
|
||
|
||
show_changes "$target" HEAD "$commit_hash"
|
||
warn_local_changes "$target"
|
||
|
||
git -C "$target" reset --hard "$commit_hash"
|
||
|
||
restart_container "$SERVER_CONTAINER"
|
||
log_success "Server-Rollback abgeschlossen."
|
||
}
|
||
|
||
list_backups_server() {
|
||
log_info "Verfügbare Server-Backups (Commits):"
|
||
ls -lt "$BACKUP_DIR/SERVER-"*.commit 2>/dev/null || log_warn "Keine Backups gefunden."
|
||
}
|
||
|
||
########################################
|
||
# Fetch (Vorschau, ohne Deploy)
|
||
########################################
|
||
fetch_changes() {
|
||
local name="$1"
|
||
local target="$2"
|
||
|
||
if [[ -d "$target/.git" ]]; then
|
||
git -C "$target" fetch origin
|
||
show_changes "$target" HEAD "origin/$SERVER_BRANCH"
|
||
warn_local_changes "$target"
|
||
else
|
||
log_warn "$name ist nicht installiert."
|
||
fi
|
||
}
|
||
|
||
########################################
|
||
# CLI
|
||
########################################
|
||
ACTION="${1:-}"
|
||
TARGET_ARG="${2:-}"
|
||
OPTIONAL="${3:-}"
|
||
|
||
case "$ACTION" in
|
||
all)
|
||
deploy_server
|
||
;;
|
||
server)
|
||
deploy_server
|
||
;;
|
||
rollback)
|
||
case "$TARGET_ARG" in
|
||
server)
|
||
rollback_server "$OPTIONAL"
|
||
;;
|
||
*)
|
||
echo "Usage: $0 rollback {public|backend|server} [backup-name]"
|
||
exit 1
|
||
;;
|
||
esac
|
||
;;
|
||
list-backups)
|
||
case "$TARGET_ARG" in
|
||
server)
|
||
list_backups_server
|
||
;;
|
||
*)
|
||
echo "Usage: $0 list-backups {public|backend|server}"
|
||
exit 1
|
||
;;
|
||
esac
|
||
;;
|
||
fetch)
|
||
fetch_changes "SERVER" "$SERVER_TARGET"
|
||
;;
|
||
*)
|
||
echo "Usage:"
|
||
echo " $0 {all|server}"
|
||
echo " $0 rollback {server} [backup-name]"
|
||
echo " $0 list-backups {server}"
|
||
echo " $0 fetch"
|
||
exit 1
|
||
;;
|
||
esac |