first commit
This commit is contained in:
Executable
+954
@@ -0,0 +1,954 @@
|
||||
Le contenu est généré par les utilisateurs et non vérifié.
|
||||
#!/bin/bash
|
||||
# =============================================================================
|
||||
# php-upgrade-nextcloud.sh
|
||||
# Montée de version PHP sur Debian 12+ — contexte Nextcloud
|
||||
#
|
||||
# Pourquoi ce script est nécessaire :
|
||||
# Sur Debian, installer une nouvelle version PHP ne migre RIEN automatiquement.
|
||||
# Trois composants doivent être mis à jour manuellement et de façon coordonnée :
|
||||
# 1. Le socket PHP-FPM (chemin dans pool.d/www.conf et dans le vhost)
|
||||
# 2. La configuration du serveur web (Apache a2enconf / Nginx fastcgi_pass)
|
||||
# 3. L'alternative CLI (/usr/bin/php via update-alternatives)
|
||||
# C'est l'absence de cette coordination qui rend les montées "compliquées".
|
||||
#
|
||||
# Ce script gère les deux également :
|
||||
# - Le dépôt Sury (packages.sury.org) qui est le seul moyen d'obtenir PHP 8.3+
|
||||
# sur Debian 12, le dépôt officiel Debian 12 ne fournissant que PHP 8.2.
|
||||
# - La coexistence de plusieurs versions PHP, ce qui permet un rollback rapide.
|
||||
#
|
||||
# Sources de référence :
|
||||
# [1] Sury repo Debian : https://packages.sury.org/php/
|
||||
# [2] Nextcloud PHP reqs : https://docs.nextcloud.com/server/latest/admin_manual/
|
||||
# installation/system_requirements.html
|
||||
# [3] PHP versions sup. : https://www.php.net/supported-versions.php
|
||||
# [4] Apache mod_proxy : https://httpd.apache.org/docs/2.4/mod/mod_proxy_fcgi.html
|
||||
# =============================================================================
|
||||
|
||||
# --- Options shell de sécurité ---
|
||||
# -e : arrête le script à la première erreur non gérée
|
||||
# -u : traite les variables non définies comme des erreurs
|
||||
# -o pipefail : un pipe échoue si l'une de ses commandes échoue (pas seulement la dernière)
|
||||
set -euo pipefail
|
||||
|
||||
# Séparateur de champs interne : on évite que les espaces et tabulations
|
||||
# cassent le découpage de mots dans les boucles for/while sur des chemins de fichiers
|
||||
IFS=$'\n\t'
|
||||
|
||||
# --- Codes couleur ANSI pour la lisibilité des logs ---
|
||||
R='\033[0;31m' # Rouge → erreurs fatales
|
||||
Y='\033[1;33m' # Jaune → avertissements non bloquants
|
||||
G='\033[0;32m' # Vert → succès / confirmations
|
||||
B='\033[0;34m' # Bleu → informations neutres
|
||||
W='\033[1m' # Gras → titres / séparateurs
|
||||
N='\033[0m' # Reset → retour à la couleur normale
|
||||
|
||||
info() { echo -e "${B}[INFO]${N} $*"; }
|
||||
ok() { echo -e "${G}[ OK ]${N} $*"; }
|
||||
warn() { echo -e "${Y}[WARN]${N} $*"; }
|
||||
# die() écrit sur stderr (>&2) pour que les messages d'erreur soient capturables
|
||||
# séparément si le script est piloté par un autre outil
|
||||
die() { echo -e "${R}[ERR ]${N} $*" >&2; exit 1; }
|
||||
sep() { echo -e "${W}──────────────────────────────────────────────${N}"; }
|
||||
|
||||
# --- Variables globales ---
|
||||
# Initialisées ici pour éviter les erreurs "unbound variable" dues à set -u.
|
||||
# Elles seront remplies au fil des fonctions de détection.
|
||||
PHP_CURRENT="" # Version PHP actuellement en service (ex: "8.2")
|
||||
PHP_TARGET="" # Version PHP vers laquelle on migre (ex: "8.3")
|
||||
WEB_SERVER="" # "apache2" ou "nginx"
|
||||
PHP_MODE="" # "fpm" (recommandé) ou "mod" (mod_php, déprécié pour Nextcloud)
|
||||
SURY_PRESENT=false # true si le dépôt packages.sury.org est déjà configuré
|
||||
DEBIAN_CODENAME="" # ex: "bookworm" — nécessaire pour composer la ligne de dépôt Sury
|
||||
BACKUP_DIR="" # Chemin du répertoire de backup créé par do_backup()
|
||||
NC_PATH="" # Chemin d'installation de Nextcloud (répertoire contenant occ)
|
||||
NC_USER="www-data" # Utilisateur système qui exécute PHP/Nextcloud (défaut Debian)
|
||||
declare -a AVAILABLE_VERSIONS=() # Tableau des versions PHP-FPM disponibles via apt
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 1. PRÉREQUIS SYSTÈME
|
||||
# =============================================================================
|
||||
|
||||
check_root() {
|
||||
# $EUID est l'UID effectif du processus courant.
|
||||
# Les opérations apt, systemctl, a2enconf, etc. nécessitent tous les droits root.
|
||||
[[ $EUID -eq 0 ]] || die "Ce script doit être exécuté en root (sudo $0)."
|
||||
}
|
||||
|
||||
check_debian() {
|
||||
# /etc/os-release est le fichier standard (LSB) pour identifier la distribution.
|
||||
# On le source pour obtenir les variables ID, VERSION_ID, VERSION_CODENAME.
|
||||
[[ -f /etc/os-release ]] || die "/etc/os-release absent."
|
||||
# shellcheck disable=SC1091 — le fichier est dynamique, shellcheck ne peut pas l'analyser
|
||||
source /etc/os-release
|
||||
|
||||
[[ "${ID:-}" == "debian" ]] \
|
||||
|| die "Ce script est Debian-uniquement. OS détecté : ${ID:-inconnu}"
|
||||
|
||||
local vid="${VERSION_ID:-0}"
|
||||
# Debian 12 (Bookworm) est le minimum : c'est la première version où
|
||||
# /usr/share/keyrings/ est la pratique standard pour les clés GPG de dépôts tiers,
|
||||
# et où systemd est suffisamment stable pour nos appels systemctl.
|
||||
(( vid >= 12 )) \
|
||||
|| die "Debian ${vid} non supporté. Minimum requis : Debian 12 (Bookworm)."
|
||||
|
||||
DEBIAN_CODENAME="${VERSION_CODENAME:-}"
|
||||
ok "Debian ${VERSION_ID} (${DEBIAN_CODENAME})"
|
||||
}
|
||||
|
||||
check_deps() {
|
||||
# Vérifie que tous les outils nécessaires sont présents avant de commencer.
|
||||
# On échoue tôt plutôt qu'en plein milieu d'une opération destructive.
|
||||
local missing=()
|
||||
for cmd in curl gpg apt-get dpkg grep sed awk; do
|
||||
command -v "${cmd}" &>/dev/null || missing+=("${cmd}")
|
||||
done
|
||||
(( ${#missing[@]} == 0 )) \
|
||||
|| die "Dépendances manquantes : ${missing[*]}"
|
||||
ok "Dépendances système : OK"
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 2. DÉTECTION DE L'ENVIRONNEMENT
|
||||
# =============================================================================
|
||||
|
||||
detect_current_php() {
|
||||
sep; info "Détection PHP actuel..."
|
||||
|
||||
# On commence par la version CLI car elle est la plus simple à détecter.
|
||||
if command -v php &>/dev/null; then
|
||||
# On demande à PHP lui-même sa version : plus fiable que de parser
|
||||
# le nom du binaire ou la sortie de "php -v" (qui contient d'autres infos).
|
||||
PHP_CURRENT=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')
|
||||
ok "PHP CLI : ${PHP_CURRENT}"
|
||||
fi
|
||||
|
||||
# La version FPM est prioritaire sur la version CLI.
|
||||
# Raison : c'est le processus PHP-FPM qui répond aux requêtes HTTP de Nextcloud,
|
||||
# pas le PHP CLI. Les deux peuvent différer si l'administrateur a changé
|
||||
# l'un sans l'autre. On identifie la version FPM par son socket Unix actif.
|
||||
#
|
||||
# Convention de nommage des sockets PHP-FPM sur Debian :
|
||||
# /run/php/phpX.Y-fpm.sock
|
||||
# Ce chemin est défini dans /etc/php/X.Y/fpm/pool.d/www.conf (directive "listen").
|
||||
for sock in /run/php/php*-fpm.sock; do
|
||||
# -S teste si le fichier est un socket Unix (et non un fichier régulier)
|
||||
[[ -S "${sock}" ]] || continue
|
||||
|
||||
# grep -oP avec lookahead : extrait uniquement le numéro de version
|
||||
# depuis un nom de type "php8.2-fpm.sock"
|
||||
local ver
|
||||
ver=$(basename "${sock}" | grep -oP '\d+\.\d+' || true)
|
||||
[[ -n "${ver}" ]] || continue
|
||||
|
||||
if [[ "${ver}" != "${PHP_CURRENT}" ]]; then
|
||||
warn "FPM actif (${ver}) ≠ PHP CLI (${PHP_CURRENT:-?}). Version FPM retenue."
|
||||
fi
|
||||
PHP_CURRENT="${ver}"
|
||||
ok "PHP-FPM socket actif : ${sock}"
|
||||
break # On s'arrête au premier socket trouvé (cas standard : une seule version active)
|
||||
done
|
||||
|
||||
[[ -n "${PHP_CURRENT}" ]] \
|
||||
|| die "Impossible de détecter une version PHP active (CLI ou FPM)."
|
||||
}
|
||||
|
||||
detect_webserver() {
|
||||
sep; info "Détection serveur web..."
|
||||
WEB_SERVER=""
|
||||
PHP_MODE="fpm" # FPM est la valeur par défaut — mod_php sera détecté explicitement
|
||||
|
||||
if systemctl is-active --quiet apache2 2>/dev/null; then
|
||||
WEB_SERVER="apache2"
|
||||
|
||||
# "apache2ctl -M" liste les modules Apache chargés.
|
||||
# mod_php se présente sous la forme "php8.2_module" (le point est remplacé
|
||||
# par rien dans le nom du module — "php82_module" serait faux, c'est bien
|
||||
# "php8.2_module" avec le point conservé).
|
||||
# Ref : nommage des modules dans /etc/apache2/mods-available/
|
||||
if apache2ctl -M 2>/dev/null | grep -q "php${PHP_CURRENT}_module"; then
|
||||
PHP_MODE="mod"
|
||||
warn "mod_php${PHP_CURRENT} détecté."
|
||||
warn "mod_php est déprécié pour Nextcloud (pas compatible avec certains opcaches)."
|
||||
warn "Ce script basculera vers FPM si mod_php de la version cible est absent."
|
||||
fi
|
||||
ok "Apache2 (mode PHP : ${PHP_MODE})"
|
||||
fi
|
||||
|
||||
if systemctl is-active --quiet nginx 2>/dev/null; then
|
||||
if [[ -n "${WEB_SERVER}" ]]; then
|
||||
# Les deux peuvent coexister (ex: Apache pour d'autres vhosts, Nginx devant Nextcloud)
|
||||
# On prend Nginx car il ne supporte pas mod_php — c'est forcément FPM.
|
||||
warn "Apache2 ET Nginx sont actifs. Nginx sera utilisé pour la bascule FPM."
|
||||
fi
|
||||
WEB_SERVER="nginx"
|
||||
PHP_MODE="fpm" # Nginx ne supporte pas mod_php, toujours FPM via fastcgi_pass
|
||||
ok "Nginx (mode : fpm)"
|
||||
fi
|
||||
|
||||
[[ -n "${WEB_SERVER}" ]] \
|
||||
|| die "Aucun serveur web actif détecté (apache2, nginx)."
|
||||
}
|
||||
|
||||
detect_php_repos() {
|
||||
sep; info "Détection dépôts PHP tiers..."
|
||||
|
||||
# On cherche la chaîne "packages.sury.org" dans tous les fichiers de configuration apt.
|
||||
# Deux formats coexistent sur Debian 12 :
|
||||
# - Format one-line (.list) : "deb https://... codename main"
|
||||
# - Format DEB822 (.sources) : clé-valeur multi-lignes
|
||||
# grep -rl ("recursive + liste les fichiers seulement") gère les deux.
|
||||
local found
|
||||
found=$(grep -rl "packages.sury.org" \
|
||||
/etc/apt/sources.list \
|
||||
/etc/apt/sources.list.d/ \
|
||||
2>/dev/null | head -n1 || true)
|
||||
|
||||
if [[ -n "${found}" ]]; then
|
||||
SURY_PRESENT=true
|
||||
ok "Dépôt Sury trouvé : ${found}"
|
||||
else
|
||||
warn "Dépôt packages.sury.org non détecté."
|
||||
info "Sans ce dépôt, seule la version PHP stock de Debian sera disponible."
|
||||
# Debian 12 (Bookworm) livré avec PHP 8.2 uniquement dans ses dépôts officiels.
|
||||
# PHP 8.3 et 8.4 ne sont accessibles que via Sury pour Debian 12.
|
||||
info "Debian 12 stock = PHP 8.2 uniquement."
|
||||
fi
|
||||
}
|
||||
|
||||
detect_nextcloud() {
|
||||
sep; info "Recherche de Nextcloud..."
|
||||
|
||||
# Chemins d'installation les plus courants selon les pratiques de déploiement Debian.
|
||||
# La présence du fichier "occ" (outil CLI de Nextcloud) identifie sans ambiguïté
|
||||
# le répertoire racine de l'installation.
|
||||
local candidates=(
|
||||
/var/www/nextcloud # Installation manuelle standard
|
||||
/var/www/html/nextcloud # Installation via guide officiel Nextcloud
|
||||
/srv/nextcloud # Alternative FHS pour les données de service
|
||||
/opt/nextcloud # Installation par paquet tiers ou snap
|
||||
)
|
||||
for path in "${candidates[@]}"; do
|
||||
if [[ -f "${path}/occ" ]]; then
|
||||
NC_PATH="${path}"
|
||||
# On détecte automatiquement l'utilisateur propriétaire de occ.
|
||||
# Cela évite les problèmes de droits si l'installation n'utilise pas www-data
|
||||
# (ex: certains setups utilisent "nextcloud", "http", ou un user dédié).
|
||||
# stat -c '%U' retourne le nom d'utilisateur (pas l'UID).
|
||||
NC_USER=$(stat -c '%U' "${NC_PATH}/occ")
|
||||
ok "Nextcloud : ${NC_PATH} (user: ${NC_USER})"
|
||||
return
|
||||
fi
|
||||
done
|
||||
|
||||
warn "Nextcloud non trouvé dans les chemins standard."
|
||||
read -rp " Chemin Nextcloud (vide pour ignorer occ) : " NC_PATH
|
||||
if [[ -n "${NC_PATH}" && -f "${NC_PATH}/occ" ]]; then
|
||||
NC_USER=$(stat -c '%U' "${NC_PATH}/occ")
|
||||
ok "Nextcloud : ${NC_PATH} (user: ${NC_USER})"
|
||||
else
|
||||
NC_PATH=""
|
||||
# Si occ n'est pas utilisable, la migration reste possible mais sans
|
||||
# maintenance mode automatique. L'utilisateur devra le gérer manuellement.
|
||||
warn "occ non utilisé. Activez le mode maintenance manuellement si nécessaire."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 3. DÉPÔT SURY
|
||||
# =============================================================================
|
||||
|
||||
add_sury_repo() {
|
||||
# Le dépôt packages.sury.org/php est maintenu par Ondřej Surý, mainteneur
|
||||
# officiel des paquets PHP dans Debian. C'est la source recommandée pour
|
||||
# obtenir des versions PHP plus récentes que celles des dépôts Debian stables.
|
||||
# Procédure d'après : https://packages.sury.org/php/README.txt
|
||||
info "Ajout du dépôt packages.sury.org/php pour Debian ${DEBIAN_CODENAME}..."
|
||||
|
||||
# Depuis Debian 12, la bonne pratique est de stocker les clés GPG des dépôts
|
||||
# tiers dans /usr/share/keyrings/ (format binaire .gpg) et de les référencer
|
||||
# via l'option "signed-by=" dans la ligne de dépôt.
|
||||
# L'ancienne méthode (apt-key add → /etc/apt/trusted.gpg) est dépréciée car
|
||||
# elle rend la clé valide pour TOUS les dépôts, ce qui est un risque de sécurité.
|
||||
local keyring="/usr/share/keyrings/php-sury.gpg"
|
||||
if [[ ! -f "${keyring}" ]]; then
|
||||
# Le fichier apt.gpg de Sury est déjà au format binaire GPG (non armored).
|
||||
# On le télécharge directement sans passer par gpg --dearmor.
|
||||
# Options curl :
|
||||
# -s : silencieux (pas de barre de progression)
|
||||
# -S : montre les erreurs malgré -s
|
||||
# -f : échoue avec code 22 en cas d'erreur HTTP (4xx, 5xx)
|
||||
# -L : suit les redirections
|
||||
curl -sSfL "https://packages.sury.org/php/apt.gpg" -o "${keyring}" \
|
||||
|| die "Échec téléchargement clé GPG Sury. Vérifiez la connectivité."
|
||||
ok "Clé GPG Sury : ${keyring}"
|
||||
else
|
||||
ok "Clé GPG déjà présente : ${keyring}"
|
||||
fi
|
||||
|
||||
# Format one-line avec signed-by : lie explicitement cette clé GPG à ce seul dépôt.
|
||||
local src_file="/etc/apt/sources.list.d/php-sury.list"
|
||||
echo "deb [signed-by=${keyring}] https://packages.sury.org/php/ ${DEBIAN_CODENAME} main" \
|
||||
> "${src_file}"
|
||||
ok "Dépôt Sury : ${src_file}"
|
||||
|
||||
# -qq : mode très silencieux, n'affiche que les erreurs
|
||||
apt-get update -qq
|
||||
SURY_PRESENT=true
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 4. VERSIONS DISPONIBLES ET CHOIX UTILISATEUR
|
||||
# =============================================================================
|
||||
|
||||
detect_available_versions() {
|
||||
sep; info "Versions PHP-FPM disponibles via apt..."
|
||||
|
||||
# Mise à jour du cache apt avant la recherche, pour avoir les données fraîches.
|
||||
apt-get update -qq 2>/dev/null
|
||||
|
||||
# On cherche les paquets phpX.Y-fpm disponibles comme proxy pour les versions PHP.
|
||||
# Raison du choix de -fpm : c'est le paquet qui conditionne l'installation complète
|
||||
# (FPM est requis pour Nextcloud avec Apache/Nginx). S'il est disponible,
|
||||
# les extensions (php8.3-gd, etc.) le sont aussi.
|
||||
#
|
||||
# apt-cache search --names-only : cherche uniquement dans les noms de paquets
|
||||
# (pas dans les descriptions), avec regex.
|
||||
#
|
||||
# grep -oP avec lookahead/lookbehind PCRE : extrait "8.3" depuis "php8.3-fpm"
|
||||
# sans capturer le préfixe "php" ni le suffixe "-fpm".
|
||||
#
|
||||
# sort -V : tri "version" (naturel) : 8.2 < 8.3 < 8.10 (et non ordre lexicographique)
|
||||
mapfile -t AVAILABLE_VERSIONS < <(
|
||||
apt-cache search --names-only '^php[0-9]+\.[0-9]+-fpm$' 2>/dev/null \
|
||||
| grep -oP 'php\K\d+\.\d+(?=-fpm)' \
|
||||
| sort -V
|
||||
)
|
||||
|
||||
(( ${#AVAILABLE_VERSIONS[@]} > 0 )) \
|
||||
|| die "Aucune version PHP-FPM disponible. Vérifiez vos dépôts."
|
||||
|
||||
ok "Versions trouvées : ${AVAILABLE_VERSIONS[*]}"
|
||||
}
|
||||
|
||||
select_target_version() {
|
||||
sep
|
||||
echo -e "${W}Versions PHP-FPM disponibles :${N}"
|
||||
for i in "${!AVAILABLE_VERSIONS[@]}"; do
|
||||
local v="${AVAILABLE_VERSIONS[$i]}"
|
||||
local tag=""
|
||||
[[ "${v}" == "${PHP_CURRENT}" ]] && tag=" ${Y}← actuelle${N}"
|
||||
printf " %d) PHP %s%b\n" "$((i+1))" "${v}" "${tag}"
|
||||
done
|
||||
echo ""
|
||||
|
||||
local choice
|
||||
while true; do
|
||||
read -rp "Version cible [1-${#AVAILABLE_VERSIONS[@]}] : " choice
|
||||
# Double condition :
|
||||
# 1. La chaîne est bien un entier (regex)
|
||||
# 2. L'entier est dans la plage valide (arithmétique bash)
|
||||
# Note : (( )) avec set -e est sûr ici car on est dans un "if"
|
||||
# (les conditions if/while ne déclenchent pas set -e sur faux)
|
||||
if [[ "${choice}" =~ ^[0-9]+$ ]] \
|
||||
&& (( choice >= 1 )) \
|
||||
&& (( choice <= ${#AVAILABLE_VERSIONS[@]} )); then
|
||||
break
|
||||
fi
|
||||
warn "Choix invalide."
|
||||
done
|
||||
|
||||
PHP_TARGET="${AVAILABLE_VERSIONS[$((choice-1))]}"
|
||||
# Migrer vers la même version n'aurait aucun sens et risquerait d'écraser
|
||||
# des configurations sans bénéfice.
|
||||
[[ "${PHP_TARGET}" != "${PHP_CURRENT}" ]] \
|
||||
|| die "Version cible identique à la version actuelle (${PHP_CURRENT}). Abandon."
|
||||
ok "Version cible sélectionnée : PHP ${PHP_TARGET}"
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 5. SAUVEGARDE
|
||||
# =============================================================================
|
||||
|
||||
do_backup() {
|
||||
sep
|
||||
|
||||
# Timestamp dans le nom du répertoire pour éviter les collisions si le script
|
||||
# est relancé plusieurs fois, et pour savoir facilement quand la backup a été faite.
|
||||
BACKUP_DIR="/root/php-upgrade-backup-$(date +%Y%m%d-%H%M%S)"
|
||||
mkdir -p "${BACKUP_DIR}"
|
||||
info "Sauvegarde → ${BACKUP_DIR}"
|
||||
|
||||
# --- Configuration PHP (php.ini, conf.d/, fpm/pool.d/) ---
|
||||
# cp -a : préserve les permissions, timestamps et liens symboliques.
|
||||
# Cela inclut tous les fichiers dans /etc/php/X.Y/ :
|
||||
# - fpm/php.ini (paramètres PHP pour les requêtes web)
|
||||
# - cli/php.ini (paramètres PHP pour occ et les crons)
|
||||
# - fpm/pool.d/www.conf (configuration du pool FPM : socket, workers, etc.)
|
||||
# - conf.d/ (extensions activées via des fichiers .ini)
|
||||
if [[ -d "/etc/php/${PHP_CURRENT}" ]]; then
|
||||
cp -a "/etc/php/${PHP_CURRENT}" "${BACKUP_DIR}/php-${PHP_CURRENT}-config"
|
||||
ok "Config PHP ${PHP_CURRENT} sauvegardée."
|
||||
fi
|
||||
|
||||
# --- Configuration Apache ---
|
||||
if [[ "${WEB_SERVER}" == "apache2" ]]; then
|
||||
# sites-available/ : vhosts (contiennent les SetHandler ou ProxyPass vers FPM)
|
||||
# conf-available/ : confs générales dont phpX.Y-fpm.conf
|
||||
cp -a /etc/apache2/sites-available/ \
|
||||
"${BACKUP_DIR}/apache2-sites-available" 2>/dev/null || true
|
||||
cp -a /etc/apache2/conf-available/ \
|
||||
"${BACKUP_DIR}/apache2-conf-available" 2>/dev/null || true
|
||||
ok "Config Apache2 sauvegardée."
|
||||
fi
|
||||
|
||||
# --- Configuration Nginx ---
|
||||
if [[ "${WEB_SERVER}" == "nginx" ]]; then
|
||||
# sites-available/ : vhosts (contiennent fastcgi_pass vers le socket FPM)
|
||||
# conf.d/ : configurations globales
|
||||
cp -a /etc/nginx/sites-available/ \
|
||||
"${BACKUP_DIR}/nginx-sites-available" 2>/dev/null || true
|
||||
cp -a /etc/nginx/conf.d/ \
|
||||
"${BACKUP_DIR}/nginx-conf.d" 2>/dev/null || true
|
||||
ok "Config Nginx sauvegardée."
|
||||
fi
|
||||
|
||||
# --- Liste des paquets PHP installés ---
|
||||
# Cette liste servira de référence pour vérifier quelles extensions ont été migrées
|
||||
# et pour un éventuel rollback manuel (apt-get install $(cat php-X.Y-packages.txt)).
|
||||
# dpkg -l "php8.2-*" : liste tous les paquets dont le nom commence par "php8.2-"
|
||||
# awk '/^ii/' : filtre uniquement les paquets "installed" (état "ii" = installé+propre)
|
||||
dpkg -l "php${PHP_CURRENT}-*" 2>/dev/null \
|
||||
| awk '/^ii/ {print $2}' \
|
||||
> "${BACKUP_DIR}/php-${PHP_CURRENT}-packages.txt" || true
|
||||
ok "Liste paquets PHP ${PHP_CURRENT} : ${BACKUP_DIR}/php-${PHP_CURRENT}-packages.txt"
|
||||
|
||||
ok "Backup complet : ${BACKUP_DIR}"
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 6. INSTALLATION PHP CIBLE
|
||||
# =============================================================================
|
||||
|
||||
install_new_php() {
|
||||
sep; info "Installation PHP ${PHP_TARGET}..."
|
||||
|
||||
# Stratégie : répliquer exactement les extensions installées sur la version actuelle.
|
||||
# C'est la cause la plus fréquente de "blanc" après une migration : on installe
|
||||
# le nouveau PHP sans les extensions, et Nextcloud échoue silencieusement.
|
||||
#
|
||||
# On lit la liste des paquets actuels via dpkg (plus fiable qu'apt list
|
||||
# dont le format de sortie est instable entre versions).
|
||||
local current_pkgs=()
|
||||
mapfile -t current_pkgs < <(
|
||||
dpkg -l "php${PHP_CURRENT}-*" 2>/dev/null \
|
||||
| awk '/^ii/ {print $2}' || true
|
||||
)
|
||||
|
||||
local to_install=()
|
||||
for pkg in "${current_pkgs[@]}"; do
|
||||
# Substitution de version dans le nom du paquet :
|
||||
# "php8.2-gd" devient "php8.3-gd", etc.
|
||||
# La syntaxe bash ${var/pattern/replacement} remplace la PREMIÈRE occurrence.
|
||||
local new_pkg="${pkg/php${PHP_CURRENT}-/php${PHP_TARGET}-}"
|
||||
|
||||
# On vérifie que le paquet existe avant de l'ajouter à la liste.
|
||||
# Certaines extensions peuvent avoir été renommées ou supprimées d'une
|
||||
# version PHP à l'autre (ex: php-json a disparu en PHP 8.0, intégré au core).
|
||||
# &>/dev/null : on supprime stdout ET stderr de apt-cache show
|
||||
if apt-cache show "${new_pkg}" &>/dev/null 2>&1; then
|
||||
to_install+=("${new_pkg}")
|
||||
else
|
||||
warn "Non disponible pour PHP ${PHP_TARGET} : ${new_pkg} (ignoré)"
|
||||
fi
|
||||
done
|
||||
|
||||
# On s'assure que fpm et cli sont toujours présents, même si pour une raison
|
||||
# quelconque ils n'étaient pas dans la liste dpkg.
|
||||
to_install+=("php${PHP_TARGET}-fpm" "php${PHP_TARGET}-cli")
|
||||
|
||||
# Déduplique la liste via un pipeline sort -u pour éviter de passer deux fois
|
||||
# le même paquet à apt (inoffensif mais peu propre).
|
||||
mapfile -t to_install < <(printf '%s\n' "${to_install[@]}" | sort -u)
|
||||
|
||||
info "Paquets à installer :"
|
||||
printf ' %s\n' "${to_install[@]}"
|
||||
echo ""
|
||||
|
||||
# DEBIAN_FRONTEND=noninteractive : supprime les questions interactives de debconf
|
||||
# (ex: "Voulez-vous redémarrer les services ?") qui bloqueraient le script.
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y "${to_install[@]}" \
|
||||
|| die "Échec installation PHP ${PHP_TARGET}. Consultez le log apt."
|
||||
ok "PHP ${PHP_TARGET} installé."
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 7. MIGRATION CONFIGURATION FPM ET PHP.INI
|
||||
# =============================================================================
|
||||
|
||||
migrate_fpm_config() {
|
||||
sep; info "Migration configuration PHP-FPM..."
|
||||
|
||||
# --- Pool FPM (www.conf) ---
|
||||
# Le fichier www.conf définit le comportement du pool FPM :
|
||||
# nombre de workers (pm.max_children), mode de gestion (pm = dynamic/static/ondemand),
|
||||
# et surtout le chemin du socket Unix (listen = /run/php/phpX.Y-fpm.sock).
|
||||
# On copie la config de l'ancienne version et on met à jour le chemin du socket.
|
||||
local src_pool="/etc/php/${PHP_CURRENT}/fpm/pool.d/www.conf"
|
||||
local dst_pool="/etc/php/${PHP_TARGET}/fpm/pool.d/www.conf"
|
||||
|
||||
if [[ -f "${src_pool}" && -f "${dst_pool}" ]]; then
|
||||
cp "${src_pool}" "${dst_pool}"
|
||||
|
||||
# sed -i : modification en place du fichier destination.
|
||||
# On remplace l'ancien chemin de socket par le nouveau.
|
||||
# Utilisation du séparateur | au lieu de / pour éviter les conflits
|
||||
# avec les slashes dans les chemins de fichiers.
|
||||
sed -i \
|
||||
"s|/run/php/php${PHP_CURRENT}-fpm\.sock|/run/php/php${PHP_TARGET}-fpm.sock|g" \
|
||||
"${dst_pool}"
|
||||
ok "Pool FPM migré → ${dst_pool}"
|
||||
else
|
||||
warn "pool www.conf non migré (source ou destination absente)."
|
||||
fi
|
||||
|
||||
# --- php.ini FPM ---
|
||||
# On migre les paramètres critiques pour Nextcloud uniquement, pas tout le php.ini.
|
||||
# Raison : copier tout le php.ini d'une version à l'autre peut introduire des
|
||||
# valeurs invalides ou dépréciées pour la nouvelle version.
|
||||
# On cible les directives documentées par Nextcloud [2] et les directives de base.
|
||||
local src_ini="/etc/php/${PHP_CURRENT}/fpm/php.ini"
|
||||
local dst_ini="/etc/php/${PHP_TARGET}/fpm/php.ini"
|
||||
|
||||
if [[ -f "${src_ini}" && -f "${dst_ini}" ]]; then
|
||||
local keys=(
|
||||
memory_limit # Nextcloud recommande >= 512M
|
||||
upload_max_filesize # Doit être >= post_max_size pour les uploads de fichiers
|
||||
post_max_size # Taille max du corps de la requête HTTP POST
|
||||
max_execution_time # Délai avant timeout d'un script PHP (occ, tâches fond)
|
||||
max_input_time # Délai d'attente pour recevoir les données POST
|
||||
output_buffering # Nextcloud requiert "Off" pour les streams
|
||||
"date.timezone" # Fuseau horaire — nécessaire pour la cohérence des logs/dates
|
||||
)
|
||||
info "Migration php.ini FPM (paramètres Nextcloud-critiques) :"
|
||||
for key in "${keys[@]}"; do
|
||||
# On extrait la ligne de configuration depuis l'ancien php.ini.
|
||||
# tail -n1 : on prend la DERNIÈRE occurrence en cas de doublon (la plus récente).
|
||||
# || true : si grep ne trouve rien, il retourne 1 — on l'absorbe pour ne pas
|
||||
# déclencher set -e (ce n'est pas une erreur si un paramètre n'est pas défini).
|
||||
local line
|
||||
line=$(grep -E "^[[:space:]]*${key}[[:space:]]*=" "${src_ini}" | tail -n1 || true)
|
||||
[[ -n "${line}" ]] || continue
|
||||
|
||||
# Deux cas :
|
||||
# - La directive existe dans le php.ini destination → on la remplace
|
||||
# - Elle n'existe pas (nouvelle install minimaliste) → on l'ajoute à la fin
|
||||
if grep -qE "^[[:space:]]*${key}[[:space:]]*=" "${dst_ini}" 2>/dev/null; then
|
||||
sed -i "s|^[[:space:]]*${key}[[:space:]]*=.*|${line}|" "${dst_ini}"
|
||||
else
|
||||
echo "${line}" >> "${dst_ini}"
|
||||
fi
|
||||
printf ' %-30s OK\n' "${key}"
|
||||
done
|
||||
|
||||
# --- php.ini CLI ---
|
||||
# Le PHP CLI est utilisé par "occ" (crons, maintenance, upgrade Nextcloud).
|
||||
# On migre seulement les deux directives les plus impactantes pour occ.
|
||||
local src_cli="/etc/php/${PHP_CURRENT}/cli/php.ini"
|
||||
local dst_cli="/etc/php/${PHP_TARGET}/cli/php.ini"
|
||||
if [[ -f "${src_cli}" && -f "${dst_cli}" ]]; then
|
||||
for key in memory_limit "date.timezone"; do
|
||||
local line
|
||||
line=$(grep -E "^[[:space:]]*${key}[[:space:]]*=" "${src_cli}" | tail -n1 || true)
|
||||
[[ -n "${line}" ]] || continue
|
||||
if grep -qE "^[[:space:]]*${key}[[:space:]]*=" "${dst_cli}" 2>/dev/null; then
|
||||
sed -i "s|^[[:space:]]*${key}[[:space:]]*=.*|${line}|" "${dst_cli}"
|
||||
else
|
||||
echo "${line}" >> "${dst_cli}"
|
||||
fi
|
||||
done
|
||||
ok "php.ini CLI migré (memory_limit, date.timezone)."
|
||||
fi
|
||||
else
|
||||
warn "php.ini non migré (source ou destination absente)."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 8. BASCULE DU SERVEUR WEB
|
||||
# =============================================================================
|
||||
|
||||
switch_apache() {
|
||||
sep; info "Bascule Apache → PHP ${PHP_TARGET}..."
|
||||
|
||||
# --- Cas mod_php ---
|
||||
if [[ "${PHP_MODE}" == "mod" ]]; then
|
||||
# a2dismod désactive le module Apache (supprime le lien symbolique dans mods-enabled/).
|
||||
# || true : si le module n'est pas activé, a2dismod retourne une erreur qu'on ignore.
|
||||
a2dismod "php${PHP_CURRENT}" 2>/dev/null || true
|
||||
|
||||
if a2enmod "php${PHP_TARGET}" 2>/dev/null; then
|
||||
ok "mod_php${PHP_TARGET} activé."
|
||||
else
|
||||
# mod_php n'est disponible que si le paquet libapache2-mod-phpX.Y est installé.
|
||||
# Le dépôt Sury le fournit, mais il peut ne pas être installé. Dans ce cas,
|
||||
# on bascule vers FPM qui est de toute façon la configuration recommandée.
|
||||
warn "mod_php${PHP_TARGET} absent. Bascule forcée vers PHP-FPM."
|
||||
PHP_MODE="fpm"
|
||||
fi
|
||||
fi
|
||||
|
||||
# --- Cas PHP-FPM (ou bascule forcée depuis mod_php) ---
|
||||
if [[ "${PHP_MODE}" == "fpm" ]]; then
|
||||
# proxy_fcgi et setenvif sont les modules Apache nécessaires pour proxy vers FPM.
|
||||
# proxy_fcgi : gère le protocole FastCGI vers le socket FPM
|
||||
# setenvif : nécessaire pour la directive SetEnvIfNoCase (header Authorization)
|
||||
# Ref Apache : https://httpd.apache.org/docs/2.4/mod/mod_proxy_fcgi.html
|
||||
a2enmod proxy_fcgi setenvif 2>/dev/null || true
|
||||
|
||||
# Désactive la conf FPM de l'ancienne version (retire le lien dans conf-enabled/).
|
||||
a2disconf "php${PHP_CURRENT}-fpm" 2>/dev/null || true
|
||||
|
||||
local new_conf="/etc/apache2/conf-available/php${PHP_TARGET}-fpm.conf"
|
||||
|
||||
# Le paquet php${PHP_TARGET}-fpm installe normalement ce fichier automatiquement.
|
||||
# S'il est absent (cas rare mais possible avec certains setups Sury), on génère
|
||||
# une configuration minimale fonctionnelle basée sur la doc Apache [4].
|
||||
if [[ ! -f "${new_conf}" ]]; then
|
||||
warn "${new_conf} absent après installation. Génération d'une conf minimale."
|
||||
cat > "${new_conf}" <<APACHECONF
|
||||
# Généré par php-upgrade-nextcloud.sh
|
||||
# Référence : https://httpd.apache.org/docs/2.4/mod/mod_proxy_fcgi.html
|
||||
<IfModule mod_proxy_fcgi.c>
|
||||
<IfModule mod_setenvif.c>
|
||||
# Passe le header HTTP Authorization à PHP-FPM (nécessaire pour CalDAV/WebDAV)
|
||||
SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=\$1
|
||||
</IfModule>
|
||||
<FilesMatch ".+\.ph(?:ar|p|tml)$">
|
||||
# Redirige les requêtes PHP vers le socket Unix FPM via le protocole FastCGI.
|
||||
# Le format "proxy:unix:/chemin/socket|fcgi://localhost" est spécifique
|
||||
# à mod_proxy_fcgi et différent d'un proxy HTTP classique.
|
||||
SetHandler "proxy:unix:/run/php/php${PHP_TARGET}-fpm.sock|fcgi://localhost"
|
||||
</FilesMatch>
|
||||
<FilesMatch "^\.ph(?:ar|p|ps|tml)$">
|
||||
# Refuse l'accès direct aux fichiers cachés PHP (sécurité)
|
||||
Require all denied
|
||||
</FilesMatch>
|
||||
</IfModule>
|
||||
APACHECONF
|
||||
ok "Conf Apache PHP-FPM minimale générée : ${new_conf}"
|
||||
fi
|
||||
|
||||
a2enconf "php${PHP_TARGET}-fpm"
|
||||
ok "Conf Apache php${PHP_TARGET}-fpm activée."
|
||||
fi
|
||||
|
||||
# Toujours vérifier la syntaxe avant de redémarrer Apache.
|
||||
# apache2ctl configtest retourne 0 et affiche "Syntax OK" si tout est bon.
|
||||
apache2ctl configtest 2>&1 | grep -q "Syntax OK" \
|
||||
|| die "Erreur syntaxe Apache. Lancez : apache2ctl configtest"
|
||||
ok "Syntaxe Apache : OK"
|
||||
}
|
||||
|
||||
switch_nginx() {
|
||||
sep; info "Bascule Nginx → PHP ${PHP_TARGET}-FPM..."
|
||||
|
||||
# Nginx référence le socket FPM directement dans les blocs "location ~ \.php$"
|
||||
# des vhosts, via la directive "fastcgi_pass unix:/run/php/phpX.Y-fpm.sock;".
|
||||
# Il n'y a pas de mécanisme a2enconf équivalent : on doit modifier les fichiers
|
||||
# de conf directement.
|
||||
local old_sock="php${PHP_CURRENT}-fpm.sock"
|
||||
local new_sock="php${PHP_TARGET}-fpm.sock"
|
||||
local changed=0
|
||||
|
||||
# find avec -print0 / read -d '' : gestion correcte des noms de fichiers
|
||||
# contenant des espaces ou des caractères spéciaux (bonne pratique générale).
|
||||
# -maxdepth 3 : on évite de descendre trop profond dans l'arborescence.
|
||||
while IFS= read -r -d '' f; do
|
||||
grep -q "${old_sock}" "${f}" 2>/dev/null || continue
|
||||
sed -i "s|${old_sock}|${new_sock}|g" "${f}"
|
||||
ok "Mis à jour : ${f}"
|
||||
# Note : on utilise changed=$(( changed + 1 )) plutôt que (( changed++ ))
|
||||
# car avec set -e, (( )) retourne 1 si le résultat est 0 (faux arithmétique),
|
||||
# ce qui déclencherait une sortie prématurée du script.
|
||||
changed=$(( changed + 1 ))
|
||||
done < <(find /etc/nginx/ -maxdepth 3 -type f \
|
||||
\( -name "*.conf" -o -name "*.conf.disabled" \) \
|
||||
-print0 2>/dev/null)
|
||||
|
||||
(( changed > 0 )) \
|
||||
|| warn "Aucun fichier Nginx modifié. Vérifiez manuellement les vhosts."
|
||||
|
||||
# nginx -t : teste la configuration sans redémarrer le service.
|
||||
# La sortie contient "syntax is ok" (minuscules) sur stdout/stderr.
|
||||
nginx -t 2>&1 | grep -q "syntax is ok" \
|
||||
|| die "Erreur syntaxe Nginx. Lancez : nginx -t"
|
||||
ok "Syntaxe Nginx : OK"
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 9. UPDATE-ALTERNATIVES — PHP CLI
|
||||
# =============================================================================
|
||||
|
||||
switch_cli_alternatives() {
|
||||
sep; info "Mise à jour update-alternatives (php CLI)..."
|
||||
|
||||
# update-alternatives gère les liens symboliques pour les commandes qui existent
|
||||
# en plusieurs versions (java, python, php, etc.).
|
||||
# Sans cette étape, "php" en CLI continuerait à pointer vers l'ancienne version,
|
||||
# ce qui casse "occ" et tous les crons Nextcloud qui appellent "php" directement.
|
||||
local new_bin="/usr/bin/php${PHP_TARGET}"
|
||||
|
||||
if [[ ! -x "${new_bin}" ]]; then
|
||||
warn "${new_bin} introuvable. Alternative CLI non mise à jour."
|
||||
return
|
||||
fi
|
||||
|
||||
# Vérifie si l'alternative est déjà enregistrée dans le système.
|
||||
# Le paquet php${PHP_TARGET}-cli l'enregistre normalement automatiquement,
|
||||
# mais dans certains cas (install manuelle, Sury mal configuré) ce n'est pas fait.
|
||||
if ! update-alternatives --list php 2>/dev/null | grep -q "${new_bin}"; then
|
||||
# Calcul de priorité : "8.3" → tr -d '.' → "83" → * 10 → 830
|
||||
# Les priorités plus élevées sont préférées en mode "auto".
|
||||
# Les versions plus récentes obtiennent une priorité plus haute.
|
||||
local prio
|
||||
prio=$(echo "${PHP_TARGET}" | tr -d '.' )
|
||||
prio=$(( prio * 10 ))
|
||||
update-alternatives --install /usr/bin/php php "${new_bin}" "${prio}" 2>/dev/null || true
|
||||
fi
|
||||
|
||||
# --set force le choix même si une autre version a une priorité plus haute
|
||||
update-alternatives --set php "${new_bin}" 2>/dev/null \
|
||||
&& ok "php CLI → ${new_bin}" \
|
||||
|| warn "update-alternatives a échoué. Vérifiez avec : php -v"
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 10. REDÉMARRAGE DES SERVICES
|
||||
# =============================================================================
|
||||
|
||||
restart_services() {
|
||||
sep; info "Redémarrage des services..."
|
||||
|
||||
# On démarre d'abord PHP-FPM de la nouvelle version AVANT de redémarrer
|
||||
# le serveur web. Raison : si Apache/Nginx redémarre et que le socket FPM
|
||||
# n'existe pas encore, les premières requêtes tombent en 502 Bad Gateway.
|
||||
systemctl restart "php${PHP_TARGET}-fpm" \
|
||||
|| die "Échec démarrage php${PHP_TARGET}-fpm. Consultez : journalctl -u php${PHP_TARGET}-fpm"
|
||||
ok "php${PHP_TARGET}-fpm : actif"
|
||||
|
||||
systemctl restart "${WEB_SERVER}" \
|
||||
|| die "Échec redémarrage ${WEB_SERVER}. Consultez : journalctl -u ${WEB_SERVER}"
|
||||
ok "${WEB_SERVER} : redémarré"
|
||||
|
||||
# On arrête et désactive l'ancien FPM pour libérer les ressources (workers,
|
||||
# mémoire du pool) et éviter qu'il redémarre au prochain boot inutilement.
|
||||
# On conserve les fichiers de config et les paquets installés pour un rollback rapide.
|
||||
if systemctl is-active --quiet "php${PHP_CURRENT}-fpm" 2>/dev/null; then
|
||||
systemctl stop "php${PHP_CURRENT}-fpm" || true
|
||||
systemctl disable "php${PHP_CURRENT}-fpm" || true
|
||||
ok "php${PHP_CURRENT}-fpm : arrêté et désactivé (paquets conservés pour rollback)"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 11. HELPERS NEXTCLOUD (occ)
|
||||
# =============================================================================
|
||||
|
||||
run_occ() {
|
||||
# Guard clause : si NC_PATH est vide ou si occ n'existe pas, on sort silencieusement.
|
||||
# Cela permet d'appeler run_occ dans le flux principal sans vérification répétée.
|
||||
[[ -n "${NC_PATH}" && -f "${NC_PATH}/occ" ]] || return 0
|
||||
|
||||
# occ doit être exécuté en tant qu'utilisateur web (www-data ou équivalent).
|
||||
# L'exécuter en root crée des fichiers de cache avec des permissions root,
|
||||
# ce qui casse les accès web ultérieurs.
|
||||
sudo -u "${NC_USER}" php "${NC_PATH}/occ" "$@"
|
||||
}
|
||||
|
||||
nc_maintenance() {
|
||||
local state="${1:-on}"
|
||||
[[ -n "${NC_PATH}" ]] || return 0
|
||||
|
||||
# Le mode maintenance de Nextcloud bloque toutes les requêtes utilisateur
|
||||
# pendant la migration, évitant la corruption de sessions ou d'uploads en cours.
|
||||
# La désactivation en fin de script est garantie par l'ordre d'appel dans main().
|
||||
info "Nextcloud maintenance:mode --${state}"
|
||||
run_occ maintenance:mode "--${state}" || warn "occ maintenance:mode --${state} a échoué."
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 12. VÉRIFICATION POST-MIGRATION
|
||||
# =============================================================================
|
||||
|
||||
verify() {
|
||||
sep; info "Vérification post-migration..."
|
||||
|
||||
# Vérifie que le PHP CLI utilisé est bien la nouvelle version.
|
||||
# Si update-alternatives a échoué, cette vérification le détecte.
|
||||
local cli_ver
|
||||
cli_ver=$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;' 2>/dev/null || echo "?")
|
||||
|
||||
if [[ "${cli_ver}" == "${PHP_TARGET}" ]]; then
|
||||
ok "PHP CLI actif : ${cli_ver} (cible atteinte)"
|
||||
else
|
||||
warn "PHP CLI actif : ${cli_ver} (attendu : ${PHP_TARGET}). Vérifiez update-alternatives."
|
||||
fi
|
||||
|
||||
# Vérifie que le service FPM de la nouvelle version est bien en cours d'exécution.
|
||||
if systemctl is-active --quiet "php${PHP_TARGET}-fpm" 2>/dev/null; then
|
||||
ok "php${PHP_TARGET}-fpm : actif"
|
||||
else
|
||||
warn "php${PHP_TARGET}-fpm n'est pas actif !"
|
||||
fi
|
||||
|
||||
# Pour Apache, vérifie qu'un module PHP est bien chargé (mod_php ou proxy_fcgi).
|
||||
if [[ "${WEB_SERVER}" == "apache2" ]]; then
|
||||
apache2ctl -M 2>/dev/null | grep -q "php\|proxy_fcgi" \
|
||||
&& ok "Module PHP/proxy Apache : chargé" \
|
||||
|| warn "Aucun module PHP/proxy visible dans Apache. Vérifiez a2enconf."
|
||||
fi
|
||||
|
||||
# Nextcloud occ status retourne un JSON avec installed, version, maintenance.
|
||||
# C'est la vérification de bout en bout la plus complète disponible sans navigateur.
|
||||
if [[ -n "${NC_PATH}" ]]; then
|
||||
info "Nextcloud occ status :"
|
||||
run_occ status || warn "occ status a retourné une erreur."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 13. NETTOYAGE OPTIONNEL
|
||||
# =============================================================================
|
||||
|
||||
maybe_cleanup() {
|
||||
sep
|
||||
|
||||
# On laisse le choix à l'utilisateur de conserver l'ancienne version.
|
||||
# Avantages de la conserver : rollback instantané si un problème est détecté
|
||||
# après la migration (un plugin incompatible, une extension manquante, etc.).
|
||||
# Inconvénient : occupe de l'espace disque (quelques centaines de Mo max).
|
||||
echo -e "${Y}Note :${N} les deux versions PHP peuvent coexister sans problème."
|
||||
read -rp "Supprimer PHP ${PHP_CURRENT} et ses paquets ? [o/N] : " ans
|
||||
if [[ "${ans,,}" == "o" ]]; then
|
||||
# On récupère la liste exacte des paquets à purger via dpkg
|
||||
# plutôt que de passer "php8.2-*" directement à apt-get purge.
|
||||
# Raison : le glob shell n'est pas garanti d'être étendu correctement
|
||||
# par apt selon la version et la configuration.
|
||||
local old_pkgs=()
|
||||
mapfile -t old_pkgs < <(
|
||||
dpkg -l "php${PHP_CURRENT}-*" 2>/dev/null \
|
||||
| awk '/^ii/ {print $2}' || true
|
||||
)
|
||||
if (( ${#old_pkgs[@]} > 0 )); then
|
||||
# purge : supprime les paquets ET leurs fichiers de configuration
|
||||
# (contrairement à "remove" qui laisse les conffiles)
|
||||
apt-get purge -y "${old_pkgs[@]}" || warn "Purge partielle."
|
||||
# autoremove : nettoie les dépendances orphelines (libapache2-mod-phpX.Y, etc.)
|
||||
apt-get autoremove -y || true
|
||||
ok "PHP ${PHP_CURRENT} supprimé."
|
||||
else
|
||||
info "Aucun paquet PHP ${PHP_CURRENT} trouvé."
|
||||
fi
|
||||
else
|
||||
info "PHP ${PHP_CURRENT} conservé."
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# MAIN — Orchestration
|
||||
# =============================================================================
|
||||
|
||||
main() {
|
||||
echo -e "\n${W}================================================${N}"
|
||||
echo -e "${W} PHP Upgrade — Debian 12+ / Nextcloud ${N}"
|
||||
echo -e "${W}================================================${N}\n"
|
||||
|
||||
# --- Phase de détection (lecture seule, aucune modification) ---
|
||||
check_root
|
||||
check_debian
|
||||
check_deps
|
||||
detect_current_php
|
||||
detect_webserver
|
||||
detect_php_repos
|
||||
detect_nextcloud
|
||||
|
||||
# Proposition d'ajout Sury si absent.
|
||||
# Sans Sury, "detect_available_versions" ne trouvera probablement que PHP 8.2
|
||||
# (version stock Debian 12), ce qui rend la migration inutile si on cible 8.3+.
|
||||
if ! "${SURY_PRESENT}"; then
|
||||
echo ""
|
||||
warn "Sans le dépôt Sury, seule la version PHP stock Debian est disponible."
|
||||
info "Debian 12 stock = PHP 8.2 uniquement. Sury permet 8.3, 8.4, etc."
|
||||
read -rp "Ajouter packages.sury.org/php ? [O/n] : " ans_sury
|
||||
[[ "${ans_sury,,}" == "n" ]] || add_sury_repo
|
||||
fi
|
||||
|
||||
# Maintenant qu'on sait si Sury est présent, on peut lister les versions dispo.
|
||||
detect_available_versions
|
||||
select_target_version
|
||||
|
||||
# --- Récapitulatif avant modification ---
|
||||
# On montre tout à l'utilisateur et on demande confirmation
|
||||
# avant de toucher quoi que ce soit au système.
|
||||
sep
|
||||
echo -e "${W}Récapitulatif de la migration :${N}"
|
||||
printf " %-20s %s\n" "PHP actuel" ": ${PHP_CURRENT}"
|
||||
printf " %-20s %s\n" "PHP cible" ": ${PHP_TARGET}"
|
||||
printf " %-20s %s\n" "Serveur web" ": ${WEB_SERVER} (mode: ${PHP_MODE})"
|
||||
printf " %-20s %s\n" "Nextcloud occ" ": ${NC_PATH:-non détecté}"
|
||||
printf " %-20s %s\n" "Backup" ": /root/php-upgrade-backup-<timestamp>"
|
||||
echo ""
|
||||
read -rp "Confirmer la migration ? [o/N] : " confirm
|
||||
[[ "${confirm,,}" == "o" ]] || { info "Annulé par l'utilisateur."; exit 0; }
|
||||
|
||||
# --- Phase de modification (ordre important) ---
|
||||
do_backup # Toujours en premier : sécurité avant tout
|
||||
nc_maintenance on # Coupe le trafic Nextcloud pendant la migration
|
||||
install_new_php # Installe les paquets (ne touche pas encore au service web)
|
||||
migrate_fpm_config # Copie pool.d/www.conf et les clés php.ini critiques
|
||||
|
||||
# Bascule la configuration du serveur web selon le type détecté
|
||||
case "${WEB_SERVER}" in
|
||||
apache2) switch_apache ;;
|
||||
nginx) switch_nginx ;;
|
||||
esac
|
||||
|
||||
switch_cli_alternatives # Met à jour le lien /usr/bin/php
|
||||
restart_services # Démarre nouveau FPM, redémarre web, arrête ancien FPM
|
||||
verify # Checks PHP CLI + FPM actif + module web + occ status
|
||||
nc_maintenance off # Rouvre Nextcloud au trafic
|
||||
maybe_cleanup # Propose de supprimer l'ancienne version
|
||||
|
||||
# --- Résumé final ---
|
||||
sep
|
||||
ok "Migration terminée : PHP ${PHP_CURRENT} → PHP ${PHP_TARGET}"
|
||||
info "Backup disponible : ${BACKUP_DIR}"
|
||||
echo ""
|
||||
echo -e "${Y}Rollback rapide si un problème apparaît :${N}"
|
||||
echo " systemctl stop php${PHP_TARGET}-fpm"
|
||||
echo " systemctl start php${PHP_CURRENT}-fpm"
|
||||
if [[ "${WEB_SERVER}" == "apache2" ]]; then
|
||||
echo " a2disconf php${PHP_TARGET}-fpm && a2enconf php${PHP_CURRENT}-fpm"
|
||||
fi
|
||||
echo " systemctl restart ${WEB_SERVER}"
|
||||
echo " update-alternatives --set php /usr/bin/php${PHP_CURRENT}"
|
||||
echo ""
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
Reference in New Issue
Block a user