From 4766c57dda054b5e660b44c2c674bca8908833a7 Mon Sep 17 00:00:00 2001 From: Stephan Morand Date: Fri, 21 Aug 2026 11:12:25 +0200 Subject: [PATCH] =?UTF-8?q?D=C3=A9clarer=20PHP=208.4=20et=20all=C3=A9ger?= =?UTF-8?q?=20l'interpolation=20du=20compose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- README.md | 18 ++++++++++++++++-- compose.prod.yaml | 27 +++++++++++++++------------ composer.json | 2 +- composer.lock | 6 +++--- docker/entrypoint.prod.sh | 13 +++++++++++++ 5 files changed, 48 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 4c99bdc..08825fb 100644 --- a/README.md +++ b/README.md @@ -455,16 +455,30 @@ elle aucun projet ne peut être créé faute de langue source à choisir. ### Déployer via Coolify -1. **Nouvelle ressource → Docker Compose**, dépôt +> **Le build pack doit être « Docker Compose ».** C'est le réglage qui décide de +> tout le reste, et Coolify ne le devine pas correctement : à la création d'une +> ressource il détecte « application PHP » et choisit **Nixpacks**, qui ignore le +> Dockerfile comme le compose, installe PHP 8.3 et lance +> `composer install --ignore-platform-reqs` — dépendances de développement +> comprises. Le build échoue alors sur +> `Lazy loading proxies require PHP 8.4 or higher`, une erreur qui ne dit rien du +> vrai problème : ce n'est pas le bon constructeur qui tourne. + +1. **Nouvelle ressource → Docker Compose** (surtout pas *Nixpacks*), dépôt `ssh://git@git.tranquilys.com:22222/Stephan/TQ-Slator.git`, branche `main`. + Sur une ressource existante : *Configuration → General → Build Pack*. 2. Fichier compose : `compose.prod.yaml`. Ne pas laisser `compose.yaml`, que - Coolify propose par défaut. + Coolify propose par défaut — Docker y fusionnerait `compose.override.yaml`. 3. Renseigner les variables du tableau ci-dessus dans l'onglet *Environment*. 4. Attribuer le domaine au service **`php`**. `SERVICE_FQDN_PHP_80` est déjà déclaré dans le fichier : Coolify y place le domaine et configure son proxy. 5. Déployer, puis lancer la commande d'amorçage ci-dessus depuis le terminal de la ressource. +Le domaine attribué par défaut est en `http://…sslip.io`. Il fonctionne, mais +les liens d'invitation partiront en clair : posez un vrai domaine en HTTPS avant +d'inviter qui que ce soit. + Le TLS est terminé par le proxy de Coolify ; Caddy a donc `auto_https off`, et `SYMFONY_TRUSTED_PROXIES` est renseigné pour que Symfony lise `X-Forwarded-Proto` — sans quoi les liens d'invitation partiraient en `http://` diff --git a/compose.prod.yaml b/compose.prod.yaml index 88e1c55..9d28ee9 100644 --- a/compose.prod.yaml +++ b/compose.prod.yaml @@ -45,17 +45,20 @@ services: # concerne que la Delivery API appelée depuis un autre domaine. CORS_ALLOW_ORIGIN: '${CORS_ALLOW_ORIGIN:-^$$}' - # `?` : absente, la variable arrête le déploiement au lieu de laisser - # démarrer un service à moitié configuré. - APP_SECRET: '${APP_SECRET:?openssl rand -hex 32}' - DATABASE_URL: 'mysql://tqslator:${MARIADB_PASSWORD:?mot de passe MariaDB}@database:3306/tqslator?serverVersion=11.4.0-MariaDB&charset=utf8mb4' + # Interpolation simple, sans la forme `${VAR:?message}` : celle-ci arrête + # bien `docker compose` sur une variable manquante, mais le parseur de + # Coolify ne la comprend pas partout. Le refus est donc porté par + # docker/entrypoint.prod.sh, où il est testé — et où le message explique + # quoi faire plutôt que de lister un nom de variable. + APP_SECRET: '${APP_SECRET}' + DATABASE_URL: 'mysql://tqslator:${MARIADB_PASSWORD}@database:3306/tqslator?serverVersion=11.4.0-MariaDB&charset=utf8mb4' REDIS_URL: 'redis://cache:6379' # Sert à composer les liens d'invitation envoyés par e-mail. Une valeur # fausse ne casse rien de visible ici : elle casse la réception, ailleurs, # plus tard. L'entrypoint refuse donc localhost. - BACK_OFFICE_URL: '${BACK_OFFICE_URL:?URL publique du back-office}' - MAILER_DSN: '${MAILER_DSN:?DSN SMTP réel, Mailpit ne part pas en production}' + BACK_OFFICE_URL: '${BACK_OFFICE_URL}' + MAILER_DSN: '${MAILER_DSN}' MAILER_FROM: '${MAILER_FROM:-no-reply@tranquilys.com}' DEFAULT_ORGANIZATION_SLUG: '${DEFAULT_ORGANIZATION_SLUG:-tranquilys}' @@ -83,11 +86,11 @@ services: environment: APP_ENV: prod APP_DEBUG: '0' - APP_SECRET: '${APP_SECRET:?openssl rand -hex 32}' - DATABASE_URL: 'mysql://tqslator:${MARIADB_PASSWORD:?mot de passe MariaDB}@database:3306/tqslator?serverVersion=11.4.0-MariaDB&charset=utf8mb4' + APP_SECRET: '${APP_SECRET}' + DATABASE_URL: 'mysql://tqslator:${MARIADB_PASSWORD}@database:3306/tqslator?serverVersion=11.4.0-MariaDB&charset=utf8mb4' REDIS_URL: 'redis://cache:6379' - BACK_OFFICE_URL: '${BACK_OFFICE_URL:?URL publique du back-office}' - MAILER_DSN: '${MAILER_DSN:?DSN SMTP réel}' + BACK_OFFICE_URL: '${BACK_OFFICE_URL}' + MAILER_DSN: '${MAILER_DSN}' MAILER_FROM: '${MAILER_FROM:-no-reply@tranquilys.com}' DEFAULT_ORGANIZATION_SLUG: '${DEFAULT_ORGANIZATION_SLUG:-tranquilys}' # Ne migre pas : il ATTEND que le schéma soit à jour. @@ -119,8 +122,8 @@ services: environment: MARIADB_DATABASE: tqslator MARIADB_USER: tqslator - MARIADB_PASSWORD: '${MARIADB_PASSWORD:?mot de passe MariaDB}' - MARIADB_ROOT_PASSWORD: '${MARIADB_ROOT_PASSWORD:?mot de passe root MariaDB}' + MARIADB_PASSWORD: '${MARIADB_PASSWORD}' + MARIADB_ROOT_PASSWORD: '${MARIADB_ROOT_PASSWORD}' command: # Doit correspondre à default_table_options de doctrine.yaml, sinon les # tables créées hors migration divergent en silence. Voir §3.6 de diff --git a/composer.json b/composer.json index 87e1247..6446d14 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,7 @@ "minimum-stability": "stable", "prefer-stable": true, "require": { - "php": ">=8.3", + "php": ">=8.4", "ext-ctype": "*", "ext-iconv": "*", "ext-intl": "*", diff --git a/composer.lock b/composer.lock index eb525e1..828fe3c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cdd141b5f1000a8c6bde9febd6fba449", + "content-hash": "5b8da7895e6fa84a780b9b1972339e4e", "packages": [ { "name": "api-platform/doctrine-common", @@ -11925,7 +11925,7 @@ "prefer-stable": true, "prefer-lowest": false, "platform": { - "php": ">=8.3", + "php": ">=8.4", "ext-ctype": "*", "ext-iconv": "*", "ext-intl": "*", @@ -11937,5 +11937,5 @@ "platform-overrides": { "php": "8.4.0" }, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" } diff --git a/docker/entrypoint.prod.sh b/docker/entrypoint.prod.sh index c24817f..74d41aa 100755 --- a/docker/entrypoint.prod.sh +++ b/docker/entrypoint.prod.sh @@ -28,6 +28,19 @@ if [ -z "${DATABASE_URL:-}" ]; then 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.