#!/usr/bin/env bash set -euo pipefail # -------- Konfiguration ------------------------------------------------------- APP_USER="${APP_USER:-mailwolt}" APP_GROUP="${APP_GROUP:-www-data}" # Fallback; setz das in deiner Umgebung falls anders 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 # npm / CI Defaults für weniger Lärm 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 systemctl restart "$u" return 0 fi done } 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_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" install -d -m 0755 /etc/mailwolt || true printf "version=%s\nrev=%s\nupdated=%s\n" "$ver" "$rev" "$(date -Is)" > /etc/mailwolt/build.info || true } # --- 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." } # -------- 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 # -------- 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} 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 # -------- Frontend: nur wenn nötig ------------------------------------------- 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 # -------- 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="${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 #export CI=1 #export NPM_CONFIG_FUND=false #export NPM_CONFIG_AUDIT=false # ## -------- 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 # ## -------- Frontend build (hardened) -------- #if [[ $NEED_FRONTEND -eq 1 ]]; then # echo "[i] Frontend build (vite) …" # # # Preflight: Schreibrechte sicherstellen # as_app "mkdir -p ${APP_DIR}/public/build ${APP_DIR}/node_modules ${APP_DIR}/.vite" # chown -R "$APP_USER":"$APP_GROUP" "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" || true # chmod -R g+rwX "${APP_DIR}/public/build" "${APP_DIR}/node_modules" "${APP_DIR}/.vite" || true # # # Nicht-interaktive / leise npm-Runs # NPM_ENV="CI=1 NPM_CONFIG_FUND=false NPM_CONFIG_AUDIT=false npm_config_loglevel=warn" # # echo "[i] npm ci …" # as_app "cd ${APP_DIR} && ${NPM_ENV} npm ci --no-audit --no-fund --loglevel=warn --no-progress || \ # ${NPM_ENV} npm install --no-audit --no-fund --loglevel=warn --no-progress" # # echo "[i] npm run build …" # as_app "cd ${APP_DIR} && ${NPM_ENV} npm run build --silent --loglevel=warn" #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."