Validation: MJML email system — brand base + three layouts + docker build

validation-293-2026-04-03 Doc

validation fail

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 /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.