405 lines
16 KiB
Bash
405 lines
16 KiB
Bash
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
# -------- Konfiguration -------------------------------------------------------
|
||
APP_USER="${APP_USER:-mailwolt}"
|
||
APP_GROUP="${APP_GROUP:-www-data}"
|
||
APP_DIR="${APP_DIR:-/var/www/mailwolt}"
|
||
BRANCH="${BRANCH:-main}"
|
||
MODE="${UPDATE_MODE:-tags}"
|
||
ALLOW_DIRTY="${ALLOW_DIRTY:-0}"
|
||
|
||
# -------- --check-only: nur Remote-Tags holen, Version schreiben, sicher beenden ---
|
||
# (wird von CheckUpdates via sudo aufgerufen — kein Update, nur Versionsprüfung)
|
||
if [[ "${1:-}" == "--check-only" ]]; then
|
||
LATEST="$(sudo -u "$APP_USER" bash -c \
|
||
"git -C '${APP_DIR}' ls-remote --tags --sort=-v:refname origin 'v*' 2>/dev/null \
|
||
| grep -v '\^{}' | head -1 | sed 's|.*refs/tags/||'" 2>/dev/null || true)"
|
||
if [[ -n "$LATEST" ]]; then
|
||
mkdir -p /var/lib/mailwolt
|
||
printf '%s\n' "$LATEST" > /var/lib/mailwolt/version_remote
|
||
fi
|
||
exit 0
|
||
fi
|
||
|
||
STATE_DIR="/var/lib/mailwolt/update"
|
||
LOCK_FILE="/var/run/mailwolt-update.lock"
|
||
LOG_FILE="/var/log/mailwolt-update.log"
|
||
|
||
# npm / CI Defaults
|
||
export CI=1
|
||
export NPM_CONFIG_FUND=false
|
||
export NPM_CONFIG_AUDIT=false
|
||
export npm_config_loglevel=warn
|
||
|
||
# -------- 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
|
||
# reload = graceful: laufende Requests fertig, dann Workers tauschen
|
||
systemctl reload "$u" 2>/dev/null || systemctl restart "$u"
|
||
return 0
|
||
fi
|
||
done
|
||
}
|
||
|
||
artisan_down(){
|
||
as_app "cd ${APP_DIR} && php artisan down --render=errors.503 --retry=10" 2>/dev/null || true
|
||
echo "[i] Wartungsmodus aktiviert"
|
||
}
|
||
|
||
artisan_up(){
|
||
as_app "cd ${APP_DIR} && php artisan up" 2>/dev/null || true
|
||
echo "[i] Wartungsmodus beendet"
|
||
}
|
||
|
||
git_safe(){
|
||
# Falls nötig: Repo als safe markieren (z.B. wenn Root den Wrapper aufruft)
|
||
as_app "git -C ${APP_DIR} config --global --add safe.directory ${APP_DIR} >/dev/null 2>&1 || true"
|
||
# .git/objects-Rechte reparieren: root-Läufe können Objekte mit falschen Rechten anlegen
|
||
find "${APP_DIR}/.git/objects" -not -user "$APP_USER" \
|
||
-exec chown "$APP_USER":"$APP_GROUP" {} \; 2>/dev/null || true
|
||
}
|
||
|
||
git_dirty_check(){
|
||
local dirty
|
||
dirty="$(as_app "git -C ${APP_DIR} status --porcelain" 2>/dev/null || true)"
|
||
if [[ -n "$dirty" ]]; then
|
||
echo "[!] Uncommitted Änderungen gefunden — tracked Dateien werden zurückgesetzt:"
|
||
echo "$dirty"
|
||
as_app "git -C ${APP_DIR} reset --hard HEAD" 2>/dev/null || true
|
||
as_app "git -C ${APP_DIR} clean -fd --exclude='.env' --exclude='storage/' --exclude='node_modules/'" 2>/dev/null || true
|
||
fi
|
||
}
|
||
|
||
get_version(){
|
||
as_app "cd ${APP_DIR} && (git describe --tags --always 2>/dev/null || git rev-parse --short=7 HEAD)"
|
||
}
|
||
|
||
write_version_files(){
|
||
local ver="$1" rev="$2"
|
||
install -d -m 0755 /etc/mailwolt /var/lib/mailwolt || true
|
||
printf "version=%s\nrev=%s\nupdated=%s\n" "$ver" "$rev" "$(date -Is)" > /etc/mailwolt/build.info || true
|
||
printf '%s\n' "${ver#v}" > /var/lib/mailwolt/version || true
|
||
printf '%s\n' "$ver" > /var/lib/mailwolt/version_raw || true
|
||
}
|
||
|
||
write_build_info(){ write_version_files "$@"; }
|
||
|
||
# --- Frontend build (quiet & robust) ------------------------------------------
|
||
frontend_build_quiet() {
|
||
local LOG="/var/log/mailwolt-frontend-build.log"
|
||
local NPM_ENV="CI=1 NPM_CONFIG_FUND=false NPM_CONFIG_AUDIT=false npm_config_loglevel=warn"
|
||
|
||
# Logfile vorbereiten
|
||
install -d -m 0755 "$(dirname "$LOG")"
|
||
: > "$LOG" || true
|
||
chmod 0644 "$LOG"
|
||
|
||
echo "[i] Frontend: vorbereiten …"
|
||
# Build-Ziele & Temp besitzbar machen
|
||
as_app "mkdir -p '${APP_DIR}/public/build' '${APP_DIR}/node_modules' '${APP_DIR}/.vite' '${APP_DIR}/.npm-cache'"
|
||
chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" "${APP_DIR}/.npm-cache" || true
|
||
chmod -R g+rwX "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" || true
|
||
|
||
# evtl. störrische Reste entfernen (vermeidet EACCES beim Unlink/Rimraf)
|
||
rm -rf "${APP_DIR}/node_modules/.vite" "${APP_DIR}/public/build/"* 2>/dev/null || true
|
||
|
||
# npm Config (kein Fund/Audit, eigener Cache unter App-User)
|
||
as_app "printf 'fund=false\naudit=false\ncache=${APP_DIR}/.npm-cache\n' > ~/.npmrc" >>"$LOG" 2>&1 || true
|
||
|
||
echo "[i] Frontend: Dependencies … (Details: $LOG)"
|
||
if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm ci --no-audit --no-fund --no-progress" >>"$LOG" 2>&1; then
|
||
if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm install --no-audit --no-fund --no-progress" >>"$LOG" 2>&1; then
|
||
echo "[!] npm install fehlgeschlagen. Letzte 60 Zeilen:"
|
||
tail -n 60 "$LOG" || true
|
||
return 1
|
||
fi
|
||
fi
|
||
|
||
echo "[i] Frontend: Build … (Details: $LOG)"
|
||
if ! as_app "cd '${APP_DIR}' && ${NPM_ENV} npm run build --silent --loglevel=warn" >>"$LOG" 2>&1; then
|
||
echo "[!] Build fehlgeschlagen. Letzte 80 Zeilen:"
|
||
tail -n 80 "$LOG" || true
|
||
return 1
|
||
fi
|
||
|
||
# Nach dem Build noch einmal Rechte begradigen
|
||
chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" || true
|
||
find "${APP_DIR}/public/build" -type d -exec chmod 2775 {} \; 2>/dev/null || true
|
||
find "${APP_DIR}/public/build" -type f -exec chmod 0664 {} \; 2>/dev/null || true
|
||
|
||
echo "[✓] Frontend gebaut."
|
||
}
|
||
|
||
# -------- Systemzustand sicherstellen (läuft bei JEDER Ausführung) -----------
|
||
# Neue Scripts/Sudoers-Regeln werden so auch ohne echtes Update eingespielt.
|
||
_sbin_install(){
|
||
local src="$1" dst="$2"
|
||
[[ -f "$src" ]] && install -m 755 "$src" "$dst" && return 0 || return 0
|
||
}
|
||
_sudoers_add(){
|
||
local file="$1" rule="$2"
|
||
[[ -f "$file" ]] || return 0
|
||
grep -qF "$rule" "$file" && return 0
|
||
chmod 640 "$file"
|
||
printf '%s\n' "$rule" >> "$file"
|
||
chmod 440 "$file"
|
||
}
|
||
ensure_system(){
|
||
# Scripts nach sbin
|
||
_sbin_install "${APP_DIR}/scripts/mailwolt-apply-domains" /usr/local/sbin/mailwolt-apply-domains
|
||
_sbin_install "${APP_DIR}/scripts/mailwolt-fetch-tags" /usr/local/sbin/mailwolt-fetch-tags
|
||
_sbin_install "${APP_DIR}/scripts/update.sh" /usr/local/sbin/mailwolt-update
|
||
# Sudoers-Regeln nachrüsten
|
||
for f in /etc/sudoers.d/mailwolt-certbot /etc/sudoers.d/mailwolt-dkim; do
|
||
_sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-fetch-tags'
|
||
_sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/local/sbin/mailwolt-update --check-only'
|
||
_sudoers_add "$f" 'www-data ALL=(root) NOPASSWD: /usr/bin/openssl x509 -enddate -noout -in /etc/letsencrypt/live/*/fullchain.pem'
|
||
done
|
||
# Laravel Scheduler cron (schedule:run muss jede Minute laufen)
|
||
local cron_file="/etc/cron.d/mailwolt"
|
||
local cron_line="* * * * * www-data php ${APP_DIR}/artisan schedule:run >> /dev/null 2>&1"
|
||
if ! grep -qF "artisan schedule:run" "${cron_file}" 2>/dev/null; then
|
||
printf '%s\n' "${cron_line}" > "${cron_file}"
|
||
chmod 644 "${cron_file}"
|
||
echo "[✓] Laravel Scheduler cron installiert."
|
||
fi
|
||
}
|
||
|
||
# -------- 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; }
|
||
|
||
# -------- Lock ----------------------------------------------------------------
|
||
mkdir -p "$STATE_DIR"
|
||
if [[ -f "$LOCK_FILE" ]]; then
|
||
LOCK_PID=$(cat "$LOCK_FILE" 2>/dev/null || true)
|
||
if [[ -n "$LOCK_PID" ]] && kill -0 "$LOCK_PID" 2>/dev/null; then
|
||
echo "[!] Update läuft bereits (PID $LOCK_PID) — Abbruch."
|
||
exit 1
|
||
fi
|
||
rm -f "$LOCK_FILE"
|
||
fi
|
||
echo $$ > "$LOCK_FILE"
|
||
|
||
# -------- State & Cleanup-Trap -----------------------------------------------
|
||
MAINTENANCE_ACTIVE=0
|
||
_cleanup() {
|
||
local rc=$?
|
||
[[ "$MAINTENANCE_ACTIVE" -eq 1 ]] && artisan_up
|
||
mkdir -p "$STATE_DIR"
|
||
echo "done" > "$STATE_DIR/state"
|
||
echo "$rc" > "$STATE_DIR/rc"
|
||
rm -f "$LOCK_FILE"
|
||
}
|
||
trap '_cleanup' EXIT INT TERM
|
||
|
||
# Update als laufend markieren
|
||
echo "running" > "$STATE_DIR/state"
|
||
rm -f "$STATE_DIR/rc"
|
||
: > "$LOG_FILE"
|
||
# Alle echo-Ausgaben in Log-Datei schreiben (und stdout — für CLI-Nutzung)
|
||
exec > >(tee -a "$LOG_FILE") 2>&1
|
||
|
||
git_safe
|
||
git_dirty_check
|
||
|
||
# -------- Git: neuen Stand holen ---------------------------------------------
|
||
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} describe --tags --abbrev=0 \$(git -C ${APP_DIR} rev-list --tags --max-count=1 2>/dev/null) 2>/dev/null || echo ''")"
|
||
# Neuesten Remote-Tag cachen für CheckUpdates (läuft als www-data ohne git-Credentials)
|
||
[[ -n "$LATEST_TAG" ]] && { mkdir -p /var/lib/mailwolt; printf '%s\n' "$LATEST_TAG" > /var/lib/mailwolt/version_remote; } || true
|
||
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"
|
||
ensure_system
|
||
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"
|
||
ensure_system
|
||
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
|
||
NEED_SBIN_UPDATE=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 "$CHANGED_FILES" | grep -qE '^scripts/' && NEED_SBIN_UPDATE=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)"
|
||
echo " Sbin-Scripts : $([[ $NEED_SBIN_UPDATE -eq 1 ]] && echo JA || echo nein)"
|
||
|
||
# System-Scripts aus scripts/ sofort nach dem Pull einspielen (kein Downtime nötig)
|
||
# ensure_system() am Ende des Scripts übernimmt das vollständig.
|
||
|
||
# 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
|
||
|
||
# -------- Composer (ohne Downtime) -------------------------------------------
|
||
if [[ $NEED_COMPOSER -eq 1 ]]; then
|
||
echo "[i] Composer …"
|
||
as_app "cd ${APP_DIR} && composer install --no-interaction --prefer-dist --optimize-autoloader --no-dev"
|
||
fi
|
||
|
||
# -------- Frontend (ohne Downtime — Assets sind statisch) --------------------
|
||
if [[ $NEED_FRONTEND -eq 1 ]]; then
|
||
echo "[i] Frontend-Änderungen erkannt – baue Assets …"
|
||
if ! frontend_build_quiet; then
|
||
echo "[!] Frontend-Build ist fehlgeschlagen (siehe /var/log/mailwolt-frontend-build.log)."
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
# -------- Wartungsmodus: Migrations + Cache + PHP-FPM reload -----------------
|
||
if [[ $NEED_MIGRATIONS -eq 1 || $NEED_PHP_RESTART -eq 1 || $NEED_COMPOSER -eq 1 ]]; then
|
||
artisan_down
|
||
MAINTENANCE_ACTIVE=1
|
||
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 optimize:clear || true"
|
||
as_app "cd ${APP_DIR} && php artisan config:cache || true"
|
||
as_app "cd ${APP_DIR} && php artisan route:cache || true"
|
||
as_app "cd ${APP_DIR} && php artisan queue:restart || true"
|
||
fi
|
||
|
||
# -------- Dienste neu laden (gezielt) ----------------------------------------
|
||
echo "[i] Dienste neu laden …"
|
||
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
|
||
|
||
# -------- Wartungsmodus beenden ----------------------------------------------
|
||
if [[ "$MAINTENANCE_ACTIVE" -eq 1 ]]; then
|
||
artisan_up
|
||
MAINTENANCE_ACTIVE=0
|
||
fi
|
||
|
||
# -------- Version + Build-Info ablegen ----------------------------------------
|
||
NEW_VER="$(get_version)"
|
||
write_version_files "$NEW_VER" "$NEW_REV"
|
||
|
||
echo "[✓] Update abgeschlossen: ${OLD_VER} → ${NEW_VER} (${OLD_REV:0:7} → ${NEW_REV:0:7})"
|
||
|
||
# -------- .env: REVERB-Werte auf Domain-Basis normalisieren -------------------
|
||
migrate_env_reverb() {
|
||
local env_file="${APP_DIR}/.env"
|
||
[ -f "${env_file}" ] || return 0
|
||
|
||
# APP_HOST auslesen (authoritative Domain)
|
||
local app_host
|
||
app_host=$(grep '^APP_HOST=' "${env_file}" | head -1 | cut -d= -f2- | tr -d '"' | tr -d "'")
|
||
[ -z "${app_host}" ] && return 0
|
||
|
||
# Nur migrieren wenn VITE_REVERB_HOST nicht auf app_host zeigt
|
||
local current_vite_host
|
||
current_vite_host=$(grep '^VITE_REVERB_HOST=' "${env_file}" | head -1 | cut -d= -f2-)
|
||
if [ "${current_vite_host}" = "\${REVERB_HOST}" ] || [ "${current_vite_host}" = "${app_host}" ]; then
|
||
return 0 # bereits korrekt
|
||
fi
|
||
|
||
echo "[i] .env: REVERB-Werte werden auf Domain '${app_host}' aktualisiert …"
|
||
|
||
local scheme="http" port="80"
|
||
[ -f "/etc/letsencrypt/live/${app_host}/fullchain.pem" ] && scheme="https" && port="443"
|
||
|
||
_uenv() {
|
||
local k="$1" v="$2"
|
||
if grep -q "^${k}=" "${env_file}"; then
|
||
sed -i "s|^${k}=.*|${k}=${v}|" "${env_file}"
|
||
else
|
||
echo "${k}=${v}" >> "${env_file}"
|
||
fi
|
||
}
|
||
|
||
_uenv "REVERB_HOST" "\${APP_HOST}"
|
||
_uenv "REVERB_PORT" "${port}"
|
||
_uenv "REVERB_SCHEME" "${scheme}"
|
||
_uenv "REVERB_PATH" "/ws"
|
||
_uenv "REVERB_SERVER_HOST" "127.0.0.1"
|
||
_uenv "REVERB_SERVER_PORT" "8080"
|
||
_uenv "REVERB_SERVER_SCHEME" "http"
|
||
_uenv "REVERB_SERVER_PATH" ""
|
||
_uenv "VITE_REVERB_HOST" "\${REVERB_HOST}"
|
||
_uenv "VITE_REVERB_PORT" "\${REVERB_PORT}"
|
||
_uenv "VITE_REVERB_SCHEME" "\${REVERB_SCHEME}"
|
||
_uenv "VITE_REVERB_PATH" "\${REVERB_PATH}"
|
||
|
||
# Assets neu bauen mit den korrekten Werten
|
||
frontend_build_quiet || true
|
||
as_app "cd ${APP_DIR} && php artisan optimize:clear || true"
|
||
echo "[✓] REVERB .env migriert + Assets neu gebaut."
|
||
}
|
||
migrate_env_reverb
|
||
|
||
ensure_system |