fix(deploy): pass SES config through to the app stack

The stack env is assembled from Gitea repo secrets by the deploy
workflows' `env_data` block — there is no .env file on the host for the
app stack. SES was in neither, so `MailService` came up unconfigured on
every deployment and, with NODE_ENV=production killing the stdout dev
fallback, every notification and renewal aviso failed.

Wire SES_REGION / SES_FROM / SES_FROM_NAME / SES_ACCESS_KEY /
SES_SECRET_KEY / SES_CONFIGURATION_SET / NOTIFICATION_ADMIN_EMAILS
through both galactus and cubex. No `_GALACTUS` suffix: one SES identity
serves every deployment.

Kept out of the required-secrets preflight — mail is not needed to boot,
and failing a deploy over it would be wrong. Preflight warns instead,
since the failure is otherwise invisible until someone clicks "Ejecutar".

Also corrects the comments added in the previous commit, which claimed
these belonged in a host env file rather than in CI secrets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-02 10:59:45 -07:00
co-authored by Claude Opus 5
parent 33833c3af9
commit f4b92fa7a5
5 changed files with 74 additions and 14 deletions
+41 -1
View File
@@ -40,6 +40,18 @@
# SESSION_SECRET_GALACTUS 64-hex (openssl rand -hex 32)
# MINIO_ROOT_USER / MINIO_ROOT_PASSWORD
# MYSQL_PASSWORD / MYSQL_ROOT_PASSWORD
# Optional — outbound mail. Not needed to deploy; needed for
# /notificaciones to send anything at all (the image sets
# NODE_ENV=production, which disables MailService's stdout fallback, so
# a blank config fails every send loudly):
# SES_REGION e.g. us-west-2
# SES_FROM a VERIFIED SES sending identity
# SES_FROM_NAME display name, optional
# SES_ACCESS_KEY / SES_SECRET_KEY
# SES_CONFIGURATION_SET optional, for bounce/complaint events
# NOTIFICATION_ADMIN_EMAILS comma-separated summary recipients
# These are NOT galactus-specific (no _GALACTUS suffix) — one SES identity
# serves every deployment.
# - The runner (which lives on cubex) must be able to reach BOTH
# galactus:9443 (Portainer) and galactus:3306 (MySQL, for migrate deploy).
# If it cannot reach 3306, run the migration by hand from a host that can
@@ -116,6 +128,13 @@ jobs:
MINIO_ROOT_PASSWORD: ${{ secrets.MINIO_ROOT_PASSWORD }}
MYSQL_PASSWORD: ${{ secrets.MYSQL_PASSWORD }}
MYSQL_ROOT_PASSWORD: ${{ secrets.MYSQL_ROOT_PASSWORD }}
# Not required — the app boots fine without mail. Warned about below,
# because the failure mode is remote: everything looks healthy until
# someone clicks "Ejecutar" and every send fails.
SES_REGION: ${{ secrets.SES_REGION }}
SES_FROM: ${{ secrets.SES_FROM }}
SES_ACCESS_KEY: ${{ secrets.SES_ACCESS_KEY }}
SES_SECRET_KEY: ${{ secrets.SES_SECRET_KEY }}
SCOPE: ${{ github.event.inputs.scope }}
run: |
REQUIRED="PORTAINER_URL_GALACTUS PORTAINER_API_KEY_GALACTUS
@@ -140,6 +159,20 @@ jobs:
fi
echo "all required secrets present for scope=$SCOPE"
# Mail is optional to deploy but not optional to work. Say so loudly
# rather than letting /notificaciones fail one send at a time.
mail_missing=""
for name in SES_REGION SES_FROM SES_ACCESS_KEY SES_SECRET_KEY; do
eval "value=\${$name}"
[ -z "$value" ] && mail_missing="$mail_missing $name"
done
if [ -n "$mail_missing" ]; then
echo "::warning::outbound mail is NOT configured, missing:$mail_missing"
echo "::warning::the deploy will succeed, but every notification and"
echo "::warning::renewal aviso will fail with 'El envío de correo no"
echo "::warning::está configurado.' See docs/MASS_EMAIL_NOTIFICATIONS.md"
fi
# --- full only: database ---------------------------------------------
- name: Deploy database stack
if: ${{ github.event.inputs.scope == 'full' }}
@@ -260,7 +293,14 @@ jobs:
"OPS_DB_ADMIN_USER": "root",
"OPS_DB_ADMIN_PASSWORD": "${{ secrets.MYSQL_ROOT_PASSWORD }}",
"MINIO_ROOT_USER": "${{ secrets.MINIO_ROOT_USER }}",
"MINIO_ROOT_PASSWORD": "${{ secrets.MINIO_ROOT_PASSWORD }}"
"MINIO_ROOT_PASSWORD": "${{ secrets.MINIO_ROOT_PASSWORD }}",
"SES_REGION": "${{ secrets.SES_REGION }}",
"SES_FROM": "${{ secrets.SES_FROM }}",
"SES_FROM_NAME": "${{ secrets.SES_FROM_NAME }}",
"SES_ACCESS_KEY": "${{ secrets.SES_ACCESS_KEY }}",
"SES_SECRET_KEY": "${{ secrets.SES_SECRET_KEY }}",
"SES_CONFIGURATION_SET": "${{ secrets.SES_CONFIGURATION_SET }}",
"NOTIFICATION_ADMIN_EMAILS": "${{ secrets.NOTIFICATION_ADMIN_EMAILS }}"
}
# --- prove it ----------------------------------------------------------