mailwolt-installer/scripts/update.sh

463 lines
18 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#!/usr/bin/env bash
set -euo pipefail
# -------- Konfiguration --------
APP_USER="${APP_USER:-mailwolt}"
APP_DIR="${APP_DIR:-/var/www/mailwolt}"
BRANCH="${BRANCH:-main}" # nur relevant bei UPDATE_MODE=branch
MODE="${UPDATE_MODE:-tags}" # tags | branch
ALLOW_DIRTY="${ALLOW_DIRTY:-0}" # 1 = Dirty-Working-Tree zulassen
# -------- Helper --------
as_app(){ sudo -u "$APP_USER" -H bash -lc "$*"; }
restart_if_exists(){ local u="$1"; systemctl list-unit-files | grep -q "^${u}\.service" && systemctl restart "$u" || true; }
reload_if_active(){ local u="$1"; systemctl is-active --quiet "$u" && systemctl reload "$u" || true; }
restart_php_fpm(){
for u in php8.3-fpm php8.2-fpm php8.1-fpm php-fpm; do
if systemctl list-unit-files | grep -q "^${u}\.service"; then
systemctl restart "$u"
return 0
fi
done
}
git_safe(){
# Falls nötig: Repo als safe markieren (manche Root-Umgebungen meckern sonst)
as_app "git -C ${APP_DIR} config --global --add safe.directory ${APP_DIR} >/dev/null 2>&1 || true"
}
git_dirty_check(){
if [[ "$ALLOW_DIRTY" != "1" ]]; then
local dirty
dirty="$(as_app "git -C ${APP_DIR} status --porcelain")"
if [[ -n "$dirty" ]]; then
echo "[!] Arbeitsbaum hat uncommitted Änderungen. Abbruch (ALLOW_DIRTY=1 zum Überschreiben)."
exit 2
fi
fi
}
get_version(){
as_app "cd ${APP_DIR} && (git describe --tags --always 2>/dev/null || git rev-parse --short=7 HEAD)"
}
write_build_info(){
local ver="$1" rev="$2"
printf "version=%s\nrev=%s\nupdated=%s\n" "$ver" "$rev" "$(date -Is)" > /etc/mailwolt/build.info || true
}
# -------- Guards --------
[[ "$(id -u)" -eq 0 ]] || { echo "[!] Bitte als root ausführen"; exit 1; }
[[ -d "$APP_DIR/.git" ]] || { echo "[!] $APP_DIR scheint kein Git-Repo zu sein"; exit 1; }
git_safe
git_dirty_check
echo "[i] Prüfe Repository …"
OLD_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")"
OLD_VER="$(get_version)"
NEW_REV="$OLD_REV"
if [[ "$MODE" = "tags" ]]; then
# → Neueste Tags holen
as_app "git -C ${APP_DIR} fetch --quiet origin && git -C ${APP_DIR} fetch --tags --quiet origin || true"
LATEST_TAG="$(as_app "git -C ${APP_DIR} tag --list | sort -V | tail -n1")"
if [[ -z "$LATEST_TAG" ]]; then
echo "[!] Keine Tags gefunden falle auf origin/${BRANCH} zurück"
as_app "git -C ${APP_DIR} checkout -q ${BRANCH} && git -C ${APP_DIR} pull --ff-only origin ${BRANCH}"
else
TARGET_REV="$(as_app "git -C ${APP_DIR} rev-list -n1 ${LATEST_TAG}")"
if [[ "$TARGET_REV" = "$OLD_REV" ]]; then
echo "[✓] Bereits auf neuestem Release (${LATEST_TAG}) nichts zu tun."
write_build_info "$(get_version)" "$OLD_REV"
exit 0
fi
echo "[i] Checkout auf Release ${LATEST_TAG} (${TARGET_REV:0:7}) …"
as_app "git -C ${APP_DIR} checkout -q ${LATEST_TAG}"
fi
NEW_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")"
else
# Rolling: branch folgen
as_app "git -C ${APP_DIR} fetch --quiet origin ${BRANCH}"
BEHIND="$(as_app "git -C ${APP_DIR} rev-list --count HEAD..origin/${BRANCH} || echo 0")"
if [[ "$BEHIND" -eq 0 ]]; then
echo "[✓] Branch origin/${BRANCH} ist bereits aktuell nichts zu tun."
write_build_info "$(get_version)" "$OLD_REV"
exit 0
fi
echo "[i] Es gibt ${BEHIND} neue Commit(s) ziehe Änderungen …"
as_app "git -C ${APP_DIR} checkout -q ${BRANCH} && git -C ${APP_DIR} pull --ff-only origin ${BRANCH}"
NEW_REV="$(as_app "git -C ${APP_DIR} rev-parse HEAD")"
fi
# -------- Änderungstypen ermitteln --------
CHANGED_FILES="$(as_app "git -C ${APP_DIR} diff --name-only ${OLD_REV}..${NEW_REV}")"
NEED_COMPOSER=0
NEED_MIGRATIONS=0
NEED_FRONTEND=0
NEED_PHP_RESTART=0
echo "$CHANGED_FILES" | grep -qE '(^|/)composer\.(json|lock)$' && NEED_COMPOSER=1
echo "$CHANGED_FILES" | grep -qE '^database/migrations/' && NEED_MIGRATIONS=1
echo "$CHANGED_FILES" | grep -qE '^(package(-lock)?\.json|vite\.config(\.ts|\.js)?|resources/|public/.*\.(js|css))' && NEED_FRONTEND=1
echo "$CHANGED_FILES" | grep -qE '^(app/|routes/|config/|resources/views/)' && NEED_PHP_RESTART=1
echo "[i] Zusammenfassung:"
echo " Composer : $([[ $NEED_COMPOSER -eq 1 ]] && echo JA || echo nein)"
echo " Migrations : $([[ $NEED_MIGRATIONS -eq 1 ]] && echo JA || echo nein)"
echo " Frontend : $([[ $NEED_FRONTEND -eq 1 ]] && echo JA || echo nein)"
echo " PHP restart : $([[ $NEED_PHP_RESTART -eq 1 ]] && echo JA || echo nein)"
# Wenn gar nichts relevantes geändert wurde → sauber beenden
if [[ $NEED_COMPOSER -eq 0 && $NEED_MIGRATIONS -eq 0 && $NEED_FRONTEND -eq 0 && $NEED_PHP_RESTART -eq 0 ]]; then
echo "[✓] Code-Stand aktualisiert, aber keine Build/Runtime-Änderungen keine Neustarts nötig."
NEW_VER="$(get_version)"
write_build_info "$NEW_VER" "$NEW_REV"
echo "[i] Version: ${OLD_VER}${NEW_VER}"
exit 0
fi
# -------- Gezielter Build/Deploy --------
if [[ $NEED_COMPOSER -eq 1 ]]; then
echo "[i] Composer …"
as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader"
fi
if [[ $NEED_MIGRATIONS -eq 1 ]]; then
echo "[i] DB-Migrationen …"
as_app "cd ${APP_DIR} && php artisan migrate --force"
fi
if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
echo "[i] Cache/Optimierungen …"
as_app "cd ${APP_DIR} && php artisan config:cache && php artisan route:cache || true"
as_app "cd ${APP_DIR} && php artisan queue:restart || true"
as_app "cd ${APP_DIR} && php artisan optimize:clear || true"
fi
if [[ $NEED_FRONTEND -eq 1 ]]; then
echo "[i] Frontend build …"
as_app "cd ${APP_DIR} && (npm ci --no-audit --no-fund || npm install)"
as_app "cd ${APP_DIR} && npm run build"
fi
# -------- Dienste nur wenn nötig --------
echo "[i] Dienste neu laden/neustarten (gezielt) …"
if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
restart_php_fpm
restart_if_exists "${APP_USER}-queue"
restart_if_exists "${APP_USER}-schedule"
restart_if_exists "${APP_USER}-ws"
fi
if [[ $NEED_FRONTEND -eq 1 || $NEED_PHP_RESTART -eq 1 ]]; then
reload_if_active nginx
fi
# -------- Build-Info ablegen --------
NEW_VER="$(get_version)"
write_build_info "$NEW_VER" "$NEW_REV"
echo "[✓] Update abgeschlossen: ${OLD_REV:0:7}${NEW_REV:0:7} (Version: ${NEW_VER})"
##!/usr/bin/env bash
#set -euo pipefail
#
## -------- Konfiguration --------
#APP_USER="mailwolt"
#APP_DIR="/var/www/mailwolt"
#BRANCH="${BRANCH:-main}" # nur relevant bei UPDATE_MODE=branch
#MODE="${UPDATE_MODE:-tags}" # tags | branch
#
## -------- Helper --------
#as_app(){ sudo -u "$APP_USER" -H bash -lc "$*"; }
#restart_if_exists(){ local u="$1"; systemctl list-unit-files | grep -q "^${u}\.service" && systemctl restart "$u" || true; }
#reload_if_active(){ local u="$1"; systemctl is-active --quiet "$u" && systemctl reload "$u" || true; }
#restart_php_fpm(){
# for u in php8.3-fpm php8.2-fpm php8.1-fpm php-fpm; do
# if systemctl list-unit-files | grep -q "^${u}\.service"; then
# systemctl restart "$u"
# return 0
# fi
# done
#}
#
## -------- Guards --------
#[[ "$(id -u)" -eq 0 ]] || { echo "[!] Bitte als root ausführen"; exit 1; }
#[[ -d "$APP_DIR/.git" ]] || { echo "[!] $APP_DIR scheint kein Git-Repo zu sein"; exit 1; }
#
#echo "[i] Prüfe Repository …"
#OLD_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
#NEW_REV="$OLD_REV"
#
#if [[ "$MODE" = "tags" ]]; then
# # Auf neuesten Release-Tag wechseln (semantisch sortiert)
# LATEST_TAG="$(as_app "cd ${APP_DIR} && git fetch --tags --quiet origin && git tag --list | sort -V | tail -n1")"
# if [[ -z "$LATEST_TAG" ]]; then
# echo "[!] Keine Tags gefunden falle auf origin/${BRANCH} zurück"
# as_app "cd ${APP_DIR} && git fetch --quiet origin ${BRANCH} && git checkout -q ${BRANCH} && git pull --ff-only origin ${BRANCH}"
# else
# TARGET_REV="$(as_app "cd ${APP_DIR} && git rev-list -n1 ${LATEST_TAG}")"
# if [[ "$TARGET_REV" = "$OLD_REV" ]]; then
# echo "[✓] Bereits auf neuestem Release (${LATEST_TAG}) nichts zu tun."
# exit 0
# fi
# echo "[i] Checkout auf Release ${LATEST_TAG} (${TARGET_REV:0:7}) …"
# as_app "cd ${APP_DIR} && git checkout -q ${LATEST_TAG}"
# fi
# NEW_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
#else
# # Rolling: branch folgen
# as_app "cd ${APP_DIR} && git fetch --quiet origin ${BRANCH}"
# BEHIND="$(as_app "cd ${APP_DIR} && git rev-list --count HEAD..origin/${BRANCH} || echo 0")"
# if [[ "$BEHIND" -eq 0 ]]; then
# echo "[✓] Branch origin/${BRANCH} ist bereits aktuell nichts zu tun."
# exit 0
# fi
# echo "[i] Es gibt ${BEHIND} neue Commit(s) ziehe Änderungen …"
# as_app "cd ${APP_DIR} && git checkout -q ${BRANCH} && git pull --ff-only origin ${BRANCH}"
# NEW_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
#fi
#
## -------- Änderungstypen ermitteln --------
#CHANGED_FILES="$(as_app "cd ${APP_DIR} && git diff --name-only ${OLD_REV}..${NEW_REV}")"
#
#NEED_COMPOSER=0
#NEED_MIGRATIONS=0
#NEED_FRONTEND=0
#NEED_PHP_RESTART=0
#
#echo "$CHANGED_FILES" | grep -qE '(^|/)composer\.(json|lock)$' && NEED_COMPOSER=1
#echo "$CHANGED_FILES" | grep -qE '^database/migrations/' && NEED_MIGRATIONS=1
#echo "$CHANGED_FILES" | grep -qE '^(package(-lock)?\.json|vite\.config|resources/|public/.*\.(js|css))' && NEED_FRONTEND=1
#echo "$CHANGED_FILES" | grep -qE '^(app/|routes/|config/|resources/views/)' && NEED_PHP_RESTART=1
#
#echo "[i] Zusammenfassung:"
#echo " Composer : $([[ $NEED_COMPOSER -eq 1 ]] && echo JA || echo nein)"
#echo " Migrations : $([[ $NEED_MIGRATIONS -eq 1 ]] && echo JA || echo nein)"
#echo " Frontend : $([[ $NEED_FRONTEND -eq 1 ]] && echo JA || echo nein)"
#echo " PHP restart : $([[ $NEED_PHP_RESTART -eq 1 ]] && echo JA || echo nein)"
#
## Wenn gar nichts relevantes geändert wurde → sauber beenden
#if [[ $NEED_COMPOSER -eq 0 && $NEED_MIGRATIONS -eq 0 && $NEED_FRONTEND -eq 0 && $NEED_PHP_RESTART -eq 0 ]]; then
# echo "[✓] Code-Stand aktualisiert, aber keine Build/Runtime-Änderungen keine Neustarts nötig."
# # Build-Info trotzdem aktualisieren
# INST_VER="$(as_app "cd ${APP_DIR} && (cat VERSION 2>/dev/null || echo dev)")"
# printf "version=%s\nrev=%s\nupdated=%s\n" "$INST_VER" "$NEW_REV" "$(date -Is)" > /etc/mailwolt/build.info || true
# exit 0
#fi
#
## -------- Gezielter Build/Deploy --------
#if [[ $NEED_COMPOSER -eq 1 ]]; then
# echo "[i] Composer …"
# as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader"
#fi
#
#if [[ $NEED_MIGRATIONS -eq 1 ]]; then
# echo "[i] DB-Migrationen …"
# as_app "cd ${APP_DIR} && php artisan migrate --force"
#fi
#
#if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
# echo "[i] Cache/Optimierungen …"
# as_app "cd ${APP_DIR} && php artisan config:cache && php artisan route:cache || true"
# as_app "cd ${APP_DIR} && php artisan queue:restart || true"
# as_app "cd ${APP_DIR} && php artisan optimize:clear || true"
#fi
#
#if [[ $NEED_FRONTEND -eq 1 ]]; then
# echo "[i] Frontend build …"
# as_app "cd ${APP_DIR} && (npm ci --no-audit --no-fund || npm install)"
# as_app "cd ${APP_DIR} && npm run build"
#fi
#
## -------- Dienste nur wenn nötig --------
#echo "[i] Dienste neu laden/neustarten (gezielt) …"
#if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
# restart_php_fpm
# restart_if_exists "${APP_USER}-queue"
# restart_if_exists "${APP_USER}-schedule"
# restart_if_exists "${APP_USER}-ws"
#fi
#if [[ $NEED_FRONTEND -eq 1 || $NEED_PHP_RESTART -eq 1 ]]; then
# reload_if_active nginx
#fi
#
## -------- Build-Info ablegen --------
#INST_VER="$(as_app "cd ${APP_DIR} && (cat VERSION 2>/dev/null || echo dev)")"
#printf "version=%s\nrev=%s\nupdated=%s\n" "$INST_VER" "$NEW_REV" "$(date -Is)" > /etc/mailwolt/build.info || true
#
#echo "[✓] Update abgeschlossen: ${OLD_REV:0:7} → ${NEW_REV:0:7} (Version: ${INST_VER})"
##!/usr/bin/env bash
#set -euo pipefail
#
#APP_USER="mailwolt"
#APP_DIR="/var/www/mailwolt"
#BRANCH="${BRANCH:-main}" # bei Bedarf anpassen
#
#as_app(){ sudo -u "$APP_USER" -H bash -lc "$*"; }
#
#restart_if_exists(){ local u="$1"; systemctl list-unit-files | grep -q "^${u}\.service" && systemctl restart "$u" || true; }
#reload_if_active(){ local u="$1"; systemctl is-active --quiet "$u" && systemctl reload "$u" || true; }
#
#restart_php_fpm(){
# for u in php8.3-fpm php8.2-fpm php8.1-fpm php-fpm; do
# systemctl list-unit-files | grep -q "^${u}\.service" && { systemctl restart "$u"; return 0; }
# done
# return 0
#}
#
#[[ "$(id -u)" -eq 0 ]] || { echo "[!] Bitte als root ausführen"; exit 1; }
#
##!/usr/bin/env bash
#set -euo pipefail
#APP_USER="mailwolt"
#APP_DIR="/var/www/mailwolt"
#MODE="${UPDATE_MODE:-tags}"
#
#as_app(){ sudo -u "$APP_USER" -H bash -lc "$*"; }
#
## --- Ab hier wie gehabt ---
#echo "[i] Prüfe Repository …"
#if [[ "$MODE" = "tags" ]]; then
# LATEST_TAG="$(as_app "cd ${APP_DIR} && git fetch --tags --quiet origin && git tag --list | sort -V | tail -n1")"
# if [[ -n "$LATEST_TAG" ]]; then
# OLD_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
# echo "[i] Checkout auf neuesten Release: ${LATEST_TAG}"
# as_app "cd ${APP_DIR} && git checkout -q ${LATEST_TAG}"
# NEW_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
# else
# echo "[!] Keine Tags gefunden, falle auf origin/main zurück"
# as_app "cd ${APP_DIR} && git pull --ff-only origin main"
# fi
#else
# as_app "cd ${APP_DIR} && git fetch --quiet origin main && git pull --ff-only origin main"
#fi
#
#echo "[i] Prüfe Repository …"
#OLD_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
#as_app "cd ${APP_DIR} && git fetch --tags --quiet origin ${BRANCH}"
#BEHIND="$(as_app "cd ${APP_DIR} && git rev-list --count HEAD..origin/${BRANCH} || echo 0")"
#
#if [[ "${BEHIND}" -eq 0 ]]; then
# echo "[✓] Bereits aktuell nichts zu tun."
# exit 0
#fi
#
#echo "[i] Es gibt ${BEHIND} neue Commit(s) ziehe Änderungen …"
#as_app "cd ${APP_DIR} && git pull --ff-only origin ${BRANCH}"
#NEW_REV="$(as_app "cd ${APP_DIR} && git rev-parse HEAD")"
#
## Welche Bereiche haben sich geändert?
#CHANGED_FILES="$(as_app "cd ${APP_DIR} && git diff --name-only ${OLD_REV}..${NEW_REV}")"
#
#NEED_COMPOSER=0
#NEED_MIGRATIONS=0
#NEED_FRONTEND=0
#NEED_PHP_RESTART=0
#
#if echo "$CHANGED_FILES" | grep -qE '^(composer\.json|composer\.lock)'; then
# NEED_COMPOSER=1
#fi
#if echo "$CHANGED_FILES" | grep -qE '^database/migrations/'; then
# NEED_MIGRATIONS=1
#fi
#if echo "$CHANGED_FILES" | grep -qE '^(package(-lock)?\.json|vite\.config\.|resources/|public/.*\.(js|css))'; then
# NEED_FRONTEND=1
#fi
## PHP-Code/Views/Config geändert?
#if echo "$CHANGED_FILES" | grep -qE '^(app/|routes/|config/|resources/views/)'; then
# NEED_PHP_RESTART=1
#fi
#
#echo "[i] Zusammenfassung:"
#echo " Composer : $([[ $NEED_COMPOSER -eq 1 ]] && echo JA || echo nein)"
#echo " Migrations : $([[ $NEED_MIGRATIONS -eq 1 ]] && echo JA || echo nein)"
#echo " Frontend : $([[ $NEED_FRONTEND -eq 1 ]] && echo JA || echo nein)"
#echo " PHP restart : $([[ $NEED_PHP_RESTART -eq 1 ]] && echo JA || echo nein)"
#
#echo "[i] Führe Build/Deploy (gezielt) aus …"
#if [[ $NEED_COMPOSER -eq 1 ]]; then
# as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader"
#fi
#
#if [[ $NEED_MIGRATIONS -eq 1 ]]; then
# as_app "cd ${APP_DIR} && php artisan migrate --force"
#fi
#
## Cache & Queue nur wenn relevant
#if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
# as_app "cd ${APP_DIR} && php artisan config:cache && php artisan route:cache || true"
# as_app "cd ${APP_DIR} && php artisan queue:restart || true"
# as_app "cd ${APP_DIR} && php artisan optimize:clear || true"
#fi
#
#if [[ $NEED_FRONTEND -eq 1 ]]; then
# as_app "cd ${APP_DIR} && (npm ci --no-audit --no-fund || npm install)"
# as_app "cd ${APP_DIR} && npm run build"
#fi
#
## Dienste nur neu laden/starten wenn nötig
#ANY_RUNTIME_CHANGE=0
#[[ $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 || $NEED_FRONTEND -eq 1 || $NEED_PHP_RESTART -eq 1 ]] && ANY_RUNTIME_CHANGE=1
#
#if [[ $ANY_RUNTIME_CHANGE -eq 1 ]]; then
# echo "[i] Dienste neu laden/neustarten (gezielt) …"
# if [[ $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 || $NEED_MIGRATIONS -eq 1 ]]; then
# restart_php_fpm
# restart_if_exists "${APP_USER}-queue"
# restart_if_exists "${APP_USER}-schedule"
# restart_if_exists "${APP_USER}-ws"
# fi
# # Webserver nur bei Assets/Views/Config
# if [[ $NEED_FRONTEND -eq 1 || $NEED_PHP_RESTART -eq 1 ]]; then
# reload_if_active nginx
# fi
#else
# echo "[i] Nichts zum Updaten."
#fi
#
#echo "[✓] Update auf ${NEW_REV:0:7} abgeschlossen."
##!/usr/bin/env bash
#set -euo pipefail
#
#APP_USER="mailwolt"
#APP_DIR="/var/www/mailwolt"
#
#as_app(){ sudo -u "$APP_USER" -H bash -lc "$*"; }
#
#restart_if_exists(){ local u="$1"; systemctl list-unit-files | grep -q "^${u}\.service" && systemctl restart "$u" || true; }
#reload_if_active(){ local u="$1"; systemctl is-active --quiet "$u" && systemctl reload "$u" || true; }
#
#restart_php_fpm(){
# for u in php8.3-fpm php8.2-fpm php8.1-fpm php-fpm; do
# systemctl list-unit-files | grep -q "^${u}\.service" && { systemctl restart "$u"; return 0; }
# done
# return 0
#}
#
#[[ "$(id -u)" -eq 0 ]] || { echo "[!] Bitte als root ausführen"; exit 1; }
#
#echo "[i] Code-Update & Build als ${APP_USER} …"
#as_app "cd ${APP_DIR} && git pull --ff-only"
#as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader"
#as_app "cd ${APP_DIR} && php artisan migrate --force"
#as_app "cd ${APP_DIR} && php artisan config:cache && php artisan route:cache || true && php artisan queue:restart || true && php artisan optimize:clear"
#as_app "cd ${APP_DIR} && (npm ci --no-audit --no-fund || npm install)"
#as_app "cd ${APP_DIR} && npm run build"
#
#echo "[i] Dienste neu laden/neustarten …"
#restart_php_fpm
#restart_if_exists "${APP_USER}-queue"
#restart_if_exists "${APP_USER}-schedule"
#restart_if_exists "${APP_USER}-ws"
#reload_if_active nginx
#reload_if_active opendkim
#reload_if_active postfix
#reload_if_active dovecot
#reload_if_active rspamd
#
#echo "[✓] Update abgeschlossen."