diff --git a/Caddyfile b/Caddyfile index 29b6001..115c88f 100644 --- a/Caddyfile +++ b/Caddyfile @@ -48,10 +48,13 @@ handle_errors { # Don't mask API errors with the SPA shell — return a proper # JSON error so the frontend can display a meaningful message. + # NOTE: `respond` only accepts `body`/`close` subdirectives; the + # Content-Type must be set via a separate `header` directive. An + # invalid subdirective is a Caddyfile parse error and crash-loops + # the container at startup (issue #20). @api path /api/* - respond @api `{"code":{err.status_code},"message":"Backend unavailable"}` {err.status_code} { - Content-Type application/json - } + header @api Content-Type application/json + respond @api `{"code":{err.status_code},"message":"Backend unavailable"}` {err.status_code} rewrite * /index.html file_server diff --git a/infra/development/site/deploy-playbook.yml b/infra/development/site/deploy-playbook.yml index 442b8b4..537e1db 100644 --- a/infra/development/site/deploy-playbook.yml +++ b/infra/development/site/deploy-playbook.yml @@ -100,3 +100,53 @@ {{ pb_logs.stdout | default('') }} {{ pb_logs.stderr | default('') }} when: pb_health is failed + + # The frontend container carries the SPA + Caddy. An invalid Caddyfile + # crash-loops it at startup while the PocketBase gate stays green — that + # outage (issue #20) was invisible to CI because the test job swaps in + # Caddyfile.test. Gate on the real container actually serving. + - name: Wait for the frontend (Caddy) to become healthy + ansible.builtin.command: docker exec learning-platform wget -q --spider http://127.0.0.1:80/ + register: fe_health + until: fe_health.rc == 0 + retries: 18 + delay: 5 + changed_when: false + ignore_errors: yes + + - name: Collect frontend logs for diagnosis + ansible.builtin.command: docker logs --tail 80 learning-platform + register: fe_logs + when: fe_health is failed + changed_when: false + failed_when: false + + - name: Abort deploy — frontend is not healthy + ansible.builtin.fail: + msg: | + The frontend (Caddy) container did not become healthy after the deploy. + Recent container logs: + {{ fe_logs.stdout | default('') }} + {{ fe_logs.stderr | default('') }} + when: fe_health is failed + + # Observability behind the auth perimeter: surface the end-to-end state + # in the CI log on every deploy. + - name: Post-deploy smoke report + ansible.builtin.shell: | + cd /opt/learning-platform + echo '--- docker compose ps ---' + docker compose ps + echo '--- frontend -> pocketbase proxy health ---' + docker exec learning-platform wget -q -O - http://127.0.0.1:80/api/health || echo 'PROXY HEALTH FAILED' + echo '--- team_members auth methods (OIDC provider present?) ---' + docker exec pocketbase-learning wget -q -O - http://127.0.0.1:8090/api/collections/team_members/auth-methods || echo 'AUTH-METHODS FAILED' + echo '--- pocketbase logs (tail 30) ---' + docker compose logs --tail=30 pocketbase-learning + register: smoke_report + changed_when: false + failed_when: false + + - name: Show smoke report + ansible.builtin.debug: + msg: "{{ smoke_report.stdout_lines }}" diff --git a/infra/production/site/deploy-playbook.yml b/infra/production/site/deploy-playbook.yml index 442b8b4..537e1db 100644 --- a/infra/production/site/deploy-playbook.yml +++ b/infra/production/site/deploy-playbook.yml @@ -100,3 +100,53 @@ {{ pb_logs.stdout | default('') }} {{ pb_logs.stderr | default('') }} when: pb_health is failed + + # The frontend container carries the SPA + Caddy. An invalid Caddyfile + # crash-loops it at startup while the PocketBase gate stays green — that + # outage (issue #20) was invisible to CI because the test job swaps in + # Caddyfile.test. Gate on the real container actually serving. + - name: Wait for the frontend (Caddy) to become healthy + ansible.builtin.command: docker exec learning-platform wget -q --spider http://127.0.0.1:80/ + register: fe_health + until: fe_health.rc == 0 + retries: 18 + delay: 5 + changed_when: false + ignore_errors: yes + + - name: Collect frontend logs for diagnosis + ansible.builtin.command: docker logs --tail 80 learning-platform + register: fe_logs + when: fe_health is failed + changed_when: false + failed_when: false + + - name: Abort deploy — frontend is not healthy + ansible.builtin.fail: + msg: | + The frontend (Caddy) container did not become healthy after the deploy. + Recent container logs: + {{ fe_logs.stdout | default('') }} + {{ fe_logs.stderr | default('') }} + when: fe_health is failed + + # Observability behind the auth perimeter: surface the end-to-end state + # in the CI log on every deploy. + - name: Post-deploy smoke report + ansible.builtin.shell: | + cd /opt/learning-platform + echo '--- docker compose ps ---' + docker compose ps + echo '--- frontend -> pocketbase proxy health ---' + docker exec learning-platform wget -q -O - http://127.0.0.1:80/api/health || echo 'PROXY HEALTH FAILED' + echo '--- team_members auth methods (OIDC provider present?) ---' + docker exec pocketbase-learning wget -q -O - http://127.0.0.1:8090/api/collections/team_members/auth-methods || echo 'AUTH-METHODS FAILED' + echo '--- pocketbase logs (tail 30) ---' + docker compose logs --tail=30 pocketbase-learning + register: smoke_report + changed_when: false + failed_when: false + + - name: Show smoke report + ansible.builtin.debug: + msg: "{{ smoke_report.stdout_lines }}"