Last active
September 28, 2019 06:46
-
-
Save comete-upn/f0c8e64d44dc05eba0a4 to your computer and use it in GitHub Desktop.
Simple utilitaire bash pour mise à jour de Moodle.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
## | |
# Interactive Moodle update script. This script works only if you use git as | |
# source for Moodle core files and modules. | |
# | |
# The script doesn't write to a log file. For best results execute it with tee : | |
# | |
# moodleup 2>&1 | tee moodleup.log | |
# | |
# This way you can see the results on the console and written in a log file. | |
# | |
# @package local_commit | |
# @copyright Université Paris Ouest Nanterre <[email protected]> | |
# @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
## | |
# get terminal columns used in Echo for pretty output | |
COLS="${COLUMNS:-$(tput cols)}" | |
PHP=$(which php) | |
## | |
# Pretty output | |
# | |
# Print text with applied style. List of styles : | |
# - header | |
# - ask | |
# - run | |
# - info | |
# - warn | |
# | |
# @param style string the style to apply | |
# @param text string the text to output | |
# @example | |
Echo() { | |
case "$1" in | |
"header") | |
# line of '-' | |
printf '%*s\n' "$COLS" '' | tr ' ' - | |
# centered text | |
printf "%*s\n" $(( (${#2} + $COLS) / 2)) "$2" | |
# line of '-' | |
printf '%*s\n' "$COLS" '' | tr ' ' - | |
;; | |
"ask") | |
printf "[?] %s" "$2" | fmt -s -w $COLS | |
;; | |
"run") | |
printf "[>] %s\n" "$2" | fmt -s -w $COLS | |
;; | |
"info") | |
printf "[*] %s\n" "$2" | fmt -s -w $COLS | |
;; | |
"warn") | |
printf "[!] %s\n" "$2" | fmt -s -w $COLS | |
;; | |
*) | |
echo -e "$2" | fmt -s -w $COLS | |
;; | |
esac | |
} | |
## | |
# General purpose function to prompt user for oui/non. | |
# | |
# Takes the question as argument. Returns 0 if O/o read from input | |
# and 1 otherwise. | |
## | |
Confirm() { | |
# print the question and read only 1 character | |
read -p "$(Echo ask "$1 [o/n]:") " -n 1 | |
if [[ $REPLY =~ ^[Oo]$ ]]; then | |
echo " [Oui]" | |
return 0 | |
else | |
echo " [Non]" | |
return 1 | |
fi | |
} | |
## | |
# Get value from php variable and print it. | |
# | |
# Don't include the leading $ in tha variable name you search for. | |
# @param string the variable name to search for (don't put leading $) | |
# @param string the file to search | |
# @return string the value of the variable if found | |
# @example value=$(GetPhpValue CFG->dbname config.php) | |
## | |
GetPhpValue() { | |
echo $(sed -nre 's/.*\$'"$1"' *= ['\''"]?([^'\'']*)['\''"]? ?;.*/\1/p' "$2") | |
} | |
## | |
# Checks if git branch has new commits and they "can be pulled". | |
# | |
# This function return 0 if new commits are available and the local | |
# branch is not ahead or diverged. | |
# | |
# @param string folder | |
# @return integer returns 0 if updates are available and on common merge base | |
# @credit http://stackoverflow.com/a/3278427 | |
## | |
Checkup() { | |
local cpwd hasupstream glocal gremote gbase | |
# save current path | |
cpwd=$(pwd -P) | |
# enter folder to check for updates | |
cd $1 | |
git fetch | |
# git status --untracked-files=no | |
# print header lines for information | |
git status -uno | head -n 2 | |
glocal=$(git rev-parse @{0}) | |
gremote=$(git rev-parse @{u}) | |
# check if there is upstream branch configured | |
if [ $? -ne 0 ]; then | |
hasupstream=1 | |
else | |
hasupstream=0 | |
# get last common commit (merge base) | |
gbase=$(git merge-base @ @{u}) | |
fi | |
# return to current path | |
cd $cpwd | |
if [ $hasupstream -eq 1 ]; then | |
Echo info "Aucune branche amont(upstream) configurée. Le dépôt ne peut pas être mis à jour." | |
return 1 | |
elif [ $glocal = $gremote ]; then | |
Echo info "Le dépôt est à jour." | |
return 1 | |
elif [ $glocal = $gbase ]; then | |
Echo info "Le dépôt peut être mis à jour." | |
return 0 | |
elif [ $gremote = $gbase ]; then | |
Echo info "La branche locale est en avance de la branche d'origine. Le dépôt ne peut pas être mis à jour." | |
return 1 | |
else | |
Echo info "La branche locale divérge de la branche d'origine. Le dépôt ne peut pas être mis à jour." | |
return 1 | |
fi | |
} | |
## | |
# Update local git repository. This is done in the following steps : | |
# 1. Check for modified files | |
# 2. Create patches | |
# 3. Checkout original files | |
# 4. Pull changes. | |
# | |
# @param string folder | |
## | |
Update() { | |
local path cpwd filestopatch file | |
for path in $1; do | |
# save current path | |
cpwd=$(pwd -P) | |
# enter folder to update | |
cd $path | |
filestopatch=$(git diff --name-only) | |
if [ -n "$filestopatch" ]; then | |
# add repository for patching later | |
modpatch+="$path " | |
for file in $filestopatch; do | |
Echo info "Création du fichier correctif ${file}.moodleup.patch." | |
git diff "$file" > "${file}.moodleup.patch" | |
git checkout -- "$file" | |
done | |
fi | |
# update | |
git pull | |
# return to current path | |
cd $cpwd | |
done | |
} | |
## | |
# Apply patches created by moodleup in the repository given as argument. | |
# | |
# @param string folder | |
## | |
Patch() { | |
local path cpwd file | |
for path in $1; do | |
# save current path | |
cpwd=$(pwd -P) | |
# enter folder to update | |
cd $path | |
for file in $(find -type f -name *.moodleup.patch); do | |
# check if patch applies without errors | |
git apply --check "$file" | |
if [ $? -eq 0 ]; then | |
# apply the patch | |
git apply "$file" | |
Echo info "Fichier correctif $file appliqué avec succès." | |
# delete the patch file | |
rm "$file" | |
else | |
Echo warn "Erreur lors de l'application du fichier correctif $file." | |
mv "$file" "${file/moodleup.patch/moodleup.failed.patch}" | |
fi | |
done | |
# return to current path | |
cd $cpwd | |
done | |
} | |
## | |
# Calculate elapsed time and display it. | |
# | |
# @param integer start time in seconds | |
# @param integer end time in seconds, if empty now will be taken | |
## | |
TimeElapsed() { | |
local end elapsed | |
if [ -n "$2" ]; then | |
end="$2" | |
else | |
end=$(date +%s) | |
fi | |
elapsed=$(($end-$1)) | |
echo "$(($elapsed/60))min $(($elapsed%60))sec" | |
} | |
## | |
# Script starts here | |
## | |
Echo header "Script interactif de mise à jour de Moodle" | |
cat <<EOF | |
Ce script se déroule en trois étapes: | |
1. Vérification | |
2. Configuration des tâches à exécuter | |
3. Exécution de la mise à jour | |
Lors de la mise à jour des fichiers si vous avez apporté des modifications | |
locales des fichiers de correctifs seront crées et appliqués après la mise à | |
jour. | |
Votre confirmation sera demandé avant l'exécution. | |
Appuyez sur une touche pour commencer. | |
EOF | |
read | |
# | |
# Config | |
# | |
read -e -p "$(Echo ask "Chemin relatif ou absolu vers le dossier racine Moodle:") " MOODLE_ROOT | |
cd "$MOODLE_ROOT" > /dev/null 2>&1 | |
if [ $? -eq 1 ]; then | |
Echo warn "Le chemin $MOODLE_ROOT n'existe pas." | |
exit 1 | |
else | |
MOODLE_ROOT=$(pwd -P) | |
if [ -f "$MOODLE_ROOT/config.php" ]; then | |
Echo info "Je trouve config.php, à priori c'est un dossier racine Moodle." | |
else | |
Echo warn "Je ne trouve pas config.php, ce n'est pas un dossier racine Moodle." | |
exit 1 | |
fi | |
fi | |
if [ ! -d "$MOODLE_ROOT/.git" ]; then | |
Echo warn "Ce script fonctionne que si Moodle est installé à partir de git." | |
exit 1 | |
fi | |
Confirm "Le chemin vers votre Moodle est bien $MOODLE_ROOT?" | |
# If yes continue, exit otherwise | |
[ $? -eq 0 ] || exit 0 | |
# From here on we are in Moodle root folder | |
cd $MOODLE_ROOT | |
dbname=$(GetPhpValue "CFG->dbname" config.php) | |
dbhost=$(GetPhpValue "CFG->dbhost" config.php) | |
dbuser=$(GetPhpValue "CFG->dbuser" config.php) | |
dbpass=$(GetPhpValue "CFG->dbpass" config.php) | |
version=$(GetPhpValue "version" version.php) | |
release=$(GetPhpValue "release" version.php) | |
lastcron=$(mysql -h "$dbhost" -u "$dbuser" -p"$dbpass" -BNe "SELECT max(lastcron) FROM modules" $dbname) | |
lastcron=$(date -d @$lastcron +"%Y-%m-%d %H:%M") | |
Echo header "Informations Moodle" | |
echo "Version : $version" | |
echo "Release : $release" | |
echo "Dossier : $MOODLE_ROOT" | |
echo "DB host : $dbhost" | |
echo "DB name : $dbname" | |
echo "DB user : $dbuser" | |
echo "Cron : $lastcron" | |
Echo header "Vérification des mises à jour noyau." | |
Checkup $MOODLE_ROOT | |
if [ $? -eq 0 ]; then | |
corepull=0 | |
# Check for modified files | |
filestopatch=$(git diff --name-only) | |
if [ -n "$filestopatch" ]; then | |
corepatch=0 | |
Echo info "Des fichiers ont été modifiés, voici la liste : " | |
echo $filestopatch | |
else | |
corepatch=1 | |
fi | |
else | |
corepull=1 | |
# if no updates for core ask to continue with module updates | |
Confirm "Continuer le script pour mettre à jour les modules complémentaires ?" | |
[ $? -eq 0 ] || exit 0 | |
fi | |
# store modules that need updating | |
modpull='' | |
# store modules that need patching ( see Update function) | |
modpatch='' | |
Echo header "Mise à jour des modules complémentaires installés à partir de git." | |
# find all .git folders | |
for dir in $(find * -type d -name *.git); do | |
# remove the trailing .git | |
dir=${dir%.git} | |
Echo info "Vérification des mises à jour pour $dir." | |
Checkup $dir | |
if [ $? -eq 0 ]; then | |
# Ask user only if updates are availble | |
Confirm "Mettre à jour $dir?" | |
if [ $? -eq 0 ]; then | |
modpull+="$dir " | |
fi | |
fi | |
done | |
Echo header "Sauvegarder votre Moodle" | |
Confirm "Sauvegarder le dossier Moodle?" | |
backupfiles=$? | |
if [ $backupfiles -eq 0 ]; then | |
read -e -p "$(Echo ask "Dossier de sauvegarde:") " -i "${MOODLE_ROOT}_upgrade" backupdir | |
if [ "$MOODLE_ROOT" = "$backupdir" ]; then | |
Echo warn "Le dossier de sauvegarde ne peux pas être le même que votre dossier actuel de Moodle." | |
exit 1 | |
fi | |
if [ -d "$backupdir" ]; then | |
Confirm "Le dossier de sauvegarde existe. Supprimer le dossier et sauvegarder dessus ?" | |
backupdirdelete=$? | |
if [ $backupdirdelete -eq 1 ]; then | |
Echo warn "Supprimmer le dossier de sauvegarde manuellement ou choisissez un autre dossier de sauvegarde." | |
exit 1 | |
fi | |
else | |
backupdirdelete=1 | |
fi | |
fi | |
Confirm "Sauvegarder la base de données ?" | |
backupdb=$? | |
if [ $backupdb -eq 0 ]; then | |
read -e -p "$(Echo ask "Hôte de la base de données de sauvegarde:") " -i "$dbhost" backupdbhost | |
read -e -p "$(Echo ask "Nom de la base de données de sauvegarde:") " -i "${dbname}_upgrade" backupdbname | |
if [ "$dbname" = "$backupdbname" ]; then | |
Echo warn "La base de données de sauvegarde ne peux pas être la même que votre base actuelle de Moodle." | |
exit 1 | |
fi | |
read -e -p "$(Echo ask "Utilisateur (avec privilèges d'administrateur):") " -i "$dbuser" backupdbuser | |
read -e -s -p "$(Echo ask "Mot de passe:") " backupdbpass | |
echo | |
# check if database exists | |
r=$(mysql -h $backupdbhost -u $backupdbuser -p$backupdbpass -BNe "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$backupdbname'") | |
# if there is an error connecting it'll be printed | |
# so on error we can just exit here | |
[ $? -eq 0 ] || exit 1 | |
if [ -n "$r" ]; then | |
Confirm "Base de données $backupdbname existe. Recréer la base et et sauvegarder dessus?" | |
backupdbdelete=$? | |
if [ $backupdbdelete -eq 1 ]; then | |
Echo warn "Supprimmer la base de sauvegarde manuellement ou choisissez une autre base de sauvegarde." | |
exit 1 | |
fi | |
else | |
backupdbdelete=1 | |
fi | |
Confirm "Sauvegarder le contenu de la table de log (logstore_standard_log)?" | |
backupdblog=$? | |
fi | |
Echo header "Tâches supplémentaires" | |
Confirm "Activer le mode maintenance?" | |
maintenance=$? | |
if [ $maintenance -eq 0 ]; then | |
Echo info "Verifier le message qui sera affiche en mode maintenance : /admin/settings.php?section=maintenancemode ." | |
fi | |
Confirm "Purger les caches après la mise à jour ?" | |
purgecache=$? | |
Confirm "Executer le cron après la mise à jour ? " | |
runcron=$? | |
## | |
# Summary of actions to take | |
## | |
Echo header "Récapitulatif des tâches à exécuter" | |
if [ $maintenance -eq 0 ]; then | |
echo "Activer le mode maintenance." | |
fi | |
if [ $backupfiles -eq 0 ]; then | |
if [ $backupdirdelete -eq 0 ]; then | |
echo "Suppression du dossier de sauvegarde $backupdir." | |
fi | |
echo "Sauvegarde du dossier Moodle dans le dossier $backupdir." | |
fi | |
if [ $backupdb -eq 0 ]; then | |
if [ $backupdbdelete -eq 0 ]; then | |
echo "Vider la base de données $backupdbname." | |
fi | |
echo "Sauvegarde de la base de données $dbname dans la base $backupdbname." | |
if [ $backupdblog -eq 1 ]; then | |
echo "La table log (logstore_standard_log) ne sera pas sauvegardé." | |
fi | |
fi | |
if [ $corepull -eq 0 ]; then | |
echo "Mise à jour des fichiers noyau." | |
fi | |
if [ -n "$modpull" ]; then | |
echo "Mise à jour des fichiers des modules complémentaires:" | |
echo "$modpull" | tr " " "\n" | |
fi | |
if [ $purgecache -eq 0 ]; then | |
echo "Vider les caches." | |
fi | |
if [ $runcron -eq 0 ]; then | |
echo "Exécuter le cron." | |
fi | |
if [ $maintenance -eq 0 ]; then | |
echo "Désactiver le mode maintenance." | |
fi | |
## | |
# Run | |
## | |
Confirm "Exécuter les tâches de mise à jour?" | |
[ $? -eq 0 ] || exit 0 | |
updatestart=$(date +%s) | |
Echo header "Lancement de la mise à jour" | |
if [ $maintenance -eq 0 ]; then | |
Echo info "Activation du mode maintenance." | |
sudo -u www-data $PHP $MOODLE_ROOT/admin/cli/maintenance.php --enable | |
fi | |
if [ $backupfiles -eq 0 ]; then | |
taskstart=$(date +%s) | |
if [ $backupdirdelete -eq 0 ]; then | |
Echo info "Suppression du dossier de sauvegarde $backupdir." | |
rm -rf $backupdir | |
else | |
mkdir $backupdir | |
fi | |
Echo info "Sauvegarde du dossier Moodle dans le dossier $backupdir." | |
cp -a $MOODLE_ROOT $backupdir | |
Echo info "Temps de sauvegarde des fichiers $(TimeElapsed $taskstart)." | |
fi | |
if [ $backupdb -eq 0 ]; then | |
Echo info "Sauvegarde de la base de données." | |
taskstart=$(date +%s) | |
if [ $backupdbdelete -eq 0 ]; then | |
Echo info "Suppression la base de données de sauvegarde." | |
mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" -BNe "DROP DATABASE $backupdbname" | |
[ $? -eq 0 ] || exit 1 | |
fi | |
charset=$(mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" -BNe "SELECT DEFAULT_CHARACTER_SET_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$dbname'") | |
collate=$(mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" -BNe "SELECT DEFAULT_COLLATION_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '$dbname'") | |
Echo info "Création de la base de données de sauvegarde." | |
mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" -BNe "CREATE DATABASE $backupdbname CHARACTER SET $charset COLLATE $collate" | |
[ $? -eq 0 ] || exit 1 | |
Echo info "Copie de la structure." | |
mysqldump -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" --no-data "$dbname" | mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" "$backupdbname" | |
[ $? -eq 0 ] || exit 1 | |
if [ $backupdblog -eq 0 ]; then | |
Echo info "Copie des données." | |
mysqldump -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" "$dbname" | mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" "$backupdbname" | |
else | |
Echo info "Copie des données à l'exception de la table de log (logstore_standard_log)." | |
mysqldump -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" "$dbname" --ignore-table="$dbname".logstore_standard_log | mysql -h "$backupdbhost" -u "$backupdbuser" -p"$backupdbpass" "$backupdbname" | |
fi | |
[ $? -eq 0 ] || exit 1 | |
Echo info "Temps de sauvegarde de la base de données $(TimeElapsed $taskstart)." | |
fi | |
if [ -n "$modpull" ] || [ -n "$corepull" ]; then | |
Echo header "Mise à jour des fichiers" | |
taskstart=$(date +%s) | |
if [ -n "$corepull" ]; then | |
Echo info "Mise à jour des fichiers noyau" | |
Update "$MOODLE_ROOT" | |
fi | |
if [ -n "$modpull" ]; then | |
Echo info "Mise à jour des fichiers des modules complémentaires" | |
Update "$modpull" | |
fi | |
Echo header "Mise à jour de la base de données." | |
# run Moodle database upgrade before or after patching ? | |
sudo -u www-data $PHP $MOODLE_ROOT/admin/cli/upgrade.php --non-interactive | |
Echo header "Application des correctifs" | |
if [ -n "$modpatch" ]; then | |
Patch "$modpatch" | |
fi | |
if [ -n "$corepatch" ]; then | |
Patch "$MOODLE_ROOT" | |
fi | |
failed=$(find -type f -name "*.moodleup.failed.patch") | |
if [ -n "$failed" ]; then | |
Echo info "L'application de certains fichiers correctif a échoué. Voici la liste : " | |
echo $failed | |
fi | |
Echo info "Temps de mise à jour des fichiers $(TimeElapsed $taskstart)" | |
fi | |
if [ $purgecache -eq 0 ]; then | |
Echo info "Vider les caches." | |
sudo -u www-data $PHP $MOODLE_ROOT/admin/cli/purge_caches.php | |
fi | |
if [ $maintenance -eq 0 ]; then | |
Echo info "Désactivation du mode maintenance." | |
sudo -u www-data $PHP $MOODLE_ROOT/admin/cli/maintenance.php --disable | |
fi | |
if [ $runcron -eq 0 ]; then | |
Echo info "Exécution du cron." | |
sudo -u www-data $PHP $MOODLE_ROOT/admin/cli/cron.php | |
fi | |
Echo info "Temps total de l'exécution de la mise à jour $(TimeElapsed $updatestart)." | |
Echo info "Mise à jour finie !" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment