Validation: MJML email system — brand base + three layouts + docker build
Verdict: FAIL
Ticket
forgejo_admin/basketball-api#293 (board item #750 on board-westside-basketball) — MJML email template system with three branded layouts (notification, action, announcement), shared brand include, jersey-reminder MJML source, and Docker build-time compilation.
Merged PR: #296 (commit
cf37b1b, merged 2026-04-03T19:17:46Z)Environment
Tier 1: Local clone at
Tier 3: Production cluster, namespace
/tmp/basketball-api-validation on commit cf37b1b.Tier 3: Production cluster, namespace
basketball-api, pod basketball-api-6d9445b4c7-kcfzq (image tag 23902fe5... — old image, pre-merge).Checks
| # | Criterion | How Verified | Result | Evidence |
|---|---|---|---|---|
| 1 | Three MJML layout files exist with {{key}} placeholders | <code>ls templates/email/</code> | PASS | notification.mjml, action.mjml, announcement.mjml present. Placeholders: {{headline}}, {{body}}, {{cta_text}}, {{cta_url}}, {{footer_note}} |
| 2 | <code>npm run build:email</code> compiles all MJML to compiled/*.html | Local <code>npm run build:email</code> (after <code>mkdir -p compiled/</code>) | PASS | 4 files produced: action.html (12KB), announcement.html (15KB), jersey-reminder.html (29KB), notification.html (10KB) |
| 3 | Compiled HTML renders Westside-branded | <code>grep -c "#d42026\|#0a0a0a\|#141414"</code> on each compiled file | PASS | 2+ brand color matches per file |
| 4 | Action layout has red CTA button | <code>grep "button\|cta_text\|cta_url" action.html</code> | PASS | CTA button present with href={{cta_url}} and {{cta_text}} label |
| 5 | Dockerfile builds with compiled templates at /app/templates/email/compiled/ | Woodpecker pipeline #288, step <code>build-and-push</code> | FAIL | Error: "Multiple input files, but output option should be either an existing directory or an empty string: templates/email/compiled/ given". The <code>compiled/</code> directory does not exist in the Docker build stage. Missing <code>RUN mkdir -p templates/email/compiled</code> before the <code>npx mjml</code> command (Dockerfile line 11). |
| 6 | <code>load_email_template()</code> returns valid HTML | <code>pytest tests/test_jersey_reminder.py -v</code> (local, 23 tests) | PASS | 23/23 passed in 3.96s. All 7 new compiled template tests pass. |
| 7 | jersey-reminder.mjml compiles | Local <code>npm run build:email</code> | PASS | jersey-reminder.html (28,839 bytes) produced |
| 8 | CI pipeline green (Woodpecker #288) | <code>get_pipeline_status</code> | FAIL | Pipeline #288 status: failure. <code>test</code> step: 9 failures (all pre-existing opt_out tests in test_checkout, test_dual_auth, test_jersey — not caused by this PR). <code>build-and-push</code> step: failure (mkdir bug). <code>update-kustomize-tag</code>: skipped. |
| 9 | New image deployed to pod | <code>kubectl get pods -n basketball-api -o jsonpath image</code> | FAIL | Pod running image tag <code>23902fe5...</code> (old commit). Merge commit <code>cf37b1b</code> never built/pushed. |
| 10 | Pod healthy (regression check) | <code>kubectl exec ... python3 urllib /healthz</code> | PASS | HTTP 200, {"status":"ok"}. Existing deployment unaffected. |
| 11 | Table-based layout (no flexbox/grid) | <code>grep</code> for display:flex/grid in compiled HTML | PASS | 0 flexbox/grid matches. Table/td/tr elements present. |
| 12 | config.py default updated | <code>grep email_templates_dir config.py</code> | PASS | <code>email_templates_dir: str = "/app/templates/email/compiled"</code> |
| 13 | .gitignore entries | <code>grep compiled\|node_modules\|package-lock .gitignore</code> | PASS | All three patterns present |
Regression Check
Existing pod (
basketball-api-6d9445b4c7-kcfzq) is Running with 0 restarts. /healthz returns 200. No regression — the new image was never deployed, so production is unchanged.9 pre-existing test failures in CI (test_checkout, test_dual_auth, test_jersey) all relate to
opt_out option removed in #263. These are not caused by this PR.Discovered Issues
Blocker: Dockerfile line 11 is missing
mkdir -p templates/email/compiled before the MJML compile command. The MJML CLI requires the output directory to exist when processing multiple input files via glob. Fix: add RUN mkdir -p templates/email/compiled between lines 10 and 11.Pre-existing: 9 test failures related to removed
opt_out jersey option (#263). These need a separate cleanup ticket.