fix(qa): give the container the environment variables it now requires (#107)

QA refuses to start: "UPLOADS_DIR is required and is not set."

#64 made UPLOADS_DIR always required, on the reasoning that its fallback of /app/uploads is correct inside the container and wrong everywhere else, so nothing should inherit it silently. That reasoning stands — but docker-compose.qa.yml had never set it either. QA was relying on exactly the fallback that change set out to stop people relying on, so the first deploy after it merged is the first one to fail.

That is an incomplete check, and one that looked confident. #64 verified both CI workflows set every always-required variable and said so. CI is not what deploys. The compose file, which is, was never opened.

ADMIN_GATE_SECRET was missing for a different reason, and the container reported it unset even after it was added to the Portainer stack. That is not a mistake, it is how compose works: stack variables are substituted into this file as ${VAR}, not handed to the container. A service receives exactly what its environment block lists. #87 already wrote that down; this is the first time it has bitten.

The two get different treatment for a reason. UPLOADS_DIR is hardcoded because it is not a secret and because it has to match the right-hand side of the volume mapping — splitting one value across two places in the same file is how they drift apart. ADMIN_GATE_SECRET is interpolated from the stack so the secret itself never enters the repository, and the header comment now lists it among the required stack variables.

Verified by feeding the environment docker compose config actually renders into validateEnv, the same function that was rejecting it: zero errors and zero warnings, the absence of warnings confirming the admin gate is now configured rather than merely quiet.

Production runs from a stack outside this repository with the same history and will refuse to boot on its next rebuild unless UPLOADS_DIR is set there first. This commit does not fix that.

Closes #107
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-21 17:24:55 -05:00
parent afb3182ea7
commit 0710cccac1
+16
View File
@@ -37,6 +37,9 @@
# QA_SMTP_USER — Brevo SMTP login. Named QA_ for the same reason as the # QA_SMTP_USER — Brevo SMTP login. Named QA_ for the same reason as the
# QA_SMTP_PASSWORD database password: pasting production's variables in here # QA_SMTP_PASSWORD database password: pasting production's variables in here
# QA_SMTP_FROM must not silently work. # QA_SMTP_FROM must not silently work.
# ADMIN_GATE_SECRET — the shared secret Nginx Proxy Manager injects as the
# X-Admin-Gate header on the gated location. Both sides must
# hold the same value or the admin API returns 403. See #63.
services: services:
redefined-designs-qa: redefined-designs-qa:
@@ -98,6 +101,19 @@ services:
- SITE_CURRENCY=USD - SITE_CURRENCY=USD
- RESERVATION_MINUTES=15 - RESERVATION_MINUTES=15
- PUBLIC_URL=${PUBLIC_URL} - PUBLIC_URL=${PUBLIC_URL}
# Required since #64, and previously inherited from the code's fallback —
# which is exactly what that change set out to stop. Hardcoded rather than
# taken from a stack variable because it is not a secret and because it
# has to match the right-hand side of the volume mapping below; splitting
# it across two files is how they drift apart.
- UPLOADS_DIR=/app/uploads
# Interpolated from the stack environment so the secret itself never
# enters the repository. Note that setting it in Portainer alone is not
# enough: stack variables are substituted into this file, not handed to
# the container, so a variable with no line here never reaches the app.
- ADMIN_GATE_SECRET=${ADMIN_GATE_SECRET}
volumes: volumes:
# Separate uploads directory. Sharing production's would let a QA run # Separate uploads directory. Sharing production's would let a QA run
# write into, and a QA teardown delete, real product images. # write into, and a QA teardown delete, real product images.