TQ-Slator/docker/entrypoint.prod.sh
Stephan Morand 4766c57dda Déclarer PHP 8.4 et alléger l'interpolation du compose
Le premier déploiement Coolify a échoué sur « Lazy loading proxies require
PHP 8.4 or higher ». La cause n'était pas dans le dépôt — Coolify avait
construit avec Nixpacks au lieu du compose — mais l'échec a mis au jour une
inexactitude réelle.

composer.json annonçait « php >=8.3 » alors que l'application exige 8.4 :
config.platform le pinçait déjà à 8.4.0, et les proxies de chargement paresseux
de Symfony ne fonctionnent pas en dessous. Toute chaîne de packaging qui lit la
contrainte pouvait donc choisir 8.3 en toute bonne foi, et échouer bien plus
loin sur un message sans rapport apparent. Contrainte remise à >=8.4.

compose.prod.yaml n'utilise plus `${VAR:?message}` : la forme arrête
correctement `docker compose`, mais le parseur de Coolify ne la comprend pas
partout. Le refus reste porté par docker/entrypoint.prod.sh, où il est testé et
où le message dit quoi faire plutôt que de citer un nom de variable.

Ajout au passage du garde-fou manquant : MARIADB_PASSWORD oubliée produisait
« mysql://tqslator:@database:… », un DSN valide au mot de passe vide, dont
l'échec remonte en erreur de droits d'accès sans jamais nommer la variable.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 11:12:25 +02:00

105 lines
4.3 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/sh
set -e
# Démarrage d'un conteneur de production.
#
# Distinct de l'entrypoint de dev, et non une variante paramétrée : celui-ci ne
# doit JAMAIS lancer `composer install` ni régénérer le cache applicatif. Tout
# cela est figé dans l'image au build, et le refaire au démarrage rendrait deux
# conteneurs issus de la même image potentiellement différents — exactement ce
# qu'un déploiement reproductible cherche à éviter.
#
# Il ne reste donc que ce qui dépend de l'environnement d'exécution : la base.
# ── Garde-fous de configuration ───────────────────────────────────────────────
#
# Échouer ici, bruyamment, plutôt que démarrer un service à moitié configuré.
# Une application qui répond avec un APP_SECRET vide est une application dont
# les cookies de session sont forgeables ; mieux vaut qu'elle ne réponde pas.
if [ -z "${APP_SECRET:-}" ]; then
echo '[entrypoint] APP_SECRET est vide. Générez-le une fois pour toutes :' >&2
echo '[entrypoint] openssl rand -hex 32' >&2
exit 1
fi
if [ -z "${DATABASE_URL:-}" ]; then
echo '[entrypoint] DATABASE_URL est absent.' >&2
exit 1
fi
# Le DSN est composé dans compose.prod.yaml à partir de MARIADB_PASSWORD.
# Oubliée, la variable produit « mysql://tqslator:@database:… » — un DSN
# syntaxiquement valide, avec un mot de passe vide. MariaDB refuse alors la
# connexion, et le message qui remonte parle de droits d'accès, jamais de la
# variable manquante.
case "$DATABASE_URL" in
*://*:@*)
echo '[entrypoint] Le mot de passe de la base est vide dans DATABASE_URL.' >&2
echo '[entrypoint] Renseignez MARIADB_PASSWORD.' >&2
exit 1
;;
esac
# BACK_OFFICE_URL sert à composer les liens d'invitation. Laissé sur sa valeur
# de développement, il enverrait des e-mails pointant vers localhost — une
# panne qui ne se voit que du côté du destinataire, plusieurs jours plus tard.
case "${BACK_OFFICE_URL:-}" in
''|*localhost*|*127.0.0.1*)
echo "[entrypoint] BACK_OFFICE_URL vaut « ${BACK_OFFICE_URL:-vide} »." >&2
echo '[entrypoint] Les liens dinvitation envoyés par e-mail seraient inutilisables.' >&2
echo '[entrypoint] Renseignez lURL publique du back-office.' >&2
exit 1
;;
esac
mkdir -p var/cache var/log var/storage
# ── Attente de la base ────────────────────────────────────────────────────────
#
# Le healthcheck du service MariaDB couvre la disponibilité du serveur ; il ne
# dit rien de la capacité de CETTE application à s'y connecter avec CES
# identifiants. La distinction compte au premier déploiement, quand un mot de
# passe erroné produirait sinon une boucle de redémarrage sans message clair.
attempts=0
until php bin/console dbal:run-sql 'SELECT 1' >/dev/null 2>&1; do
attempts=$((attempts + 1))
if [ "$attempts" -ge 30 ]; then
echo '[entrypoint] Base injoignable après 60 s. Dernière erreur :' >&2
php bin/console dbal:run-sql 'SELECT 1' >&2 || true
exit 1
fi
echo "[entrypoint] attente de la base ($attempts/30)…"
sleep 2
done
# ── Migrations ────────────────────────────────────────────────────────────────
#
# Un seul service migre. Sans ce garde-fou, `php` et `worker` démarrent en
# parallèle et se disputent la table de verrouillage des migrations.
if [ "${RUN_MIGRATIONS:-0}" = "1" ]; then
echo '[entrypoint] migrations…'
php bin/console doctrine:migrations:migrate --no-interaction --allow-no-migration
else
attempts=0
until php bin/console doctrine:migrations:up-to-date >/dev/null 2>&1; do
attempts=$((attempts + 1))
if [ "$attempts" -ge 60 ]; then
echo '[entrypoint] Schéma toujours pas à jour après 120 s.' >&2
echo '[entrypoint] Le service qui porte RUN_MIGRATIONS=1 a-t-il démarré ?' >&2
exit 1
fi
echo "[entrypoint] attente des migrations ($attempts/60)…"
sleep 2
done
fi
echo '[entrypoint] prêt.'
exec "$@"