#!/usr/bin/env bash # ----------------------------------------------------------------------------- # migrations/apply.sh # # Applique les migrations SQL au conteneur donut-mariadb sur le serveur Donut. # - Lit /srv/donut/secrets/mariadb.env pour le mot de passe root MariaDB # - Lit /srv/donut/secrets/infolab.env pour DB_USER / DB_PASS / DB_NAME (app user) # - Crée la BDD si absente, le user app si absent (idempotent), refresh le mot de passe # - Trace les migrations appliquées dans la table `schema_migrations` : # chaque .sql n'est appliqué qu'une seule fois (relance => skip) # - Affiche un résumé (tables présentes + count categories) # # Usage : # ./migrations/apply.sh # # Pré-requis sur l'hôte : # - docker disponible # - conteneur `donut-mariadb` en marche # - /srv/donut/secrets/{mariadb,infolab}.env lisibles par l'utilisateur courant # ----------------------------------------------------------------------------- set -euo pipefail DIR="$(cd "$(dirname "$0")" && pwd)" MARIADB_ENV="${MARIADB_ENV:-/srv/donut/secrets/mariadb.env}" INFOLAB_ENV="${INFOLAB_ENV:-/srv/donut/secrets/infolab.env}" CONTAINER="${CONTAINER:-donut-mariadb}" require_file() { if [[ ! -r "$1" ]]; then echo "ERR: fichier inaccessible : $1" >&2 exit 1 fi } env_get() { # Récupère la dernière occurrence de VAR=... dans un fichier .env (1er = à droite). local file="$1" var="$2" grep -E "^${var}=" "$file" | tail -n1 | cut -d= -f2- || true } require_file "$MARIADB_ENV" require_file "$INFOLAB_ENV" # Le secret root MariaDB peut être nommé MARIADB_ROOT_PASSWORD ou MYSQL_ROOT_PASSWORD. ROOT_PWD="$(env_get "$MARIADB_ENV" MARIADB_ROOT_PASSWORD)" if [[ -z "$ROOT_PWD" ]]; then ROOT_PWD="$(env_get "$MARIADB_ENV" MYSQL_ROOT_PASSWORD)" fi if [[ -z "$ROOT_PWD" ]]; then echo "ERR: ni MARIADB_ROOT_PASSWORD ni MYSQL_ROOT_PASSWORD trouvé dans $MARIADB_ENV" >&2 exit 1 fi APP_USER="$(env_get "$INFOLAB_ENV" DB_USER)"; APP_USER="${APP_USER:-infolab}" APP_PASS="$(env_get "$INFOLAB_ENV" DB_PASS)" APP_DB="$(env_get "$INFOLAB_ENV" DB_NAME)"; APP_DB="${APP_DB:-infolab}" if [[ -z "$APP_PASS" || "$APP_PASS" == "CHANGE_ME_STRONG_PASSWORD" ]]; then echo "ERR: DB_PASS vide ou non personnalisé dans $INFOLAB_ENV" >&2 exit 1 fi # Wrapper docker exec -> mariadb. Pas d'option -t (on streame du SQL via stdin). mysql_root() { docker exec -i "$CONTAINER" mariadb -uroot -p"$ROOT_PWD" "$@" } # Test d'accès root avant d'aller plus loin. if ! mysql_root -e 'SELECT 1' >/dev/null 2>&1; then echo "ERR: impossible de se connecter à $CONTAINER en root. Conteneur up ? mot de passe correct ?" >&2 exit 1 fi echo ">> [1/4] Création de la base \`$APP_DB\` et du user \`$APP_USER\`" mysql_root <> [2/4] Bootstrap de la table schema_migrations (suivi des .sql appliqués)" mysql_root "$APP_DB" <<'SQL' CREATE TABLE IF NOT EXISTS schema_migrations ( filename VARCHAR(255) NOT NULL, applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (filename) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; SQL echo ">> [3/4] Application des migrations dans l'ordre alphabétique" shopt -s nullglob applied=0 skipped=0 for f in $(ls "$DIR"/*.sql 2>/dev/null | sort); do name="$(basename "$f")" # Skip si déjà appliquée (idempotent — on ne ré-applique pas). already="$(mysql_root "$APP_DB" -Nse "SELECT 1 FROM schema_migrations WHERE filename = '$name' LIMIT 1" 2>/dev/null || true)" if [[ -n "$already" ]]; then echo " -- skip $name (déjà appliquée)" skipped=$((skipped+1)) continue fi echo " -- apply $name" mysql_root "$APP_DB" < "$f" mysql_root "$APP_DB" -e "INSERT INTO schema_migrations (filename) VALUES ('$name')" applied=$((applied+1)) done echo ">> [4/4] Résumé" mysql_root "$APP_DB" <<'SQL' SELECT TABLE_NAME AS `table` FROM information_schema.tables WHERE TABLE_SCHEMA = DATABASE() ORDER BY TABLE_NAME; SELECT COUNT(*) AS categories_count FROM categories; SQL echo echo "OK — migrations appliquées : $applied / sautées : $skipped"