From a7412c65b51a861f8f499f7aa2e443978930a63e Mon Sep 17 00:00:00 2001 From: SimpleTest Date: Tue, 21 Jul 2026 18:18:43 +0300 Subject: [PATCH] Keep shared edge deployment isolated --- docs/operations.md | 3 +++ scripts/edge-up.sh | 8 +++++++ scripts/quality.sh | 4 +++- scripts/validate-edge-env.sh | 1 + .../support_and_content_removal_test.exs | 22 +++++++++++++++++-- 5 files changed, 35 insertions(+), 3 deletions(-) diff --git a/docs/operations.md b/docs/operations.md index e03d5a0..2712dac 100644 --- a/docs/operations.md +++ b/docs/operations.md @@ -132,6 +132,9 @@ Start/update test first and run the complete browser/API/Android verification. Only then check out that exact tested SHA in production. Start the shared edge from the production checkout with `./scripts/edge-up.sh .env`; start the production application separately with `./scripts/deploy-up.sh .env`. +`edge-up.sh` explicitly selects `EDGE_COMPOSE_PROJECT_NAME`, so the shared +proxy remains independent from both application Compose projects even though +the same production `.env` also contains `COMPOSE_PROJECT_NAME`. The authoritative A records for `whoneedhelp.com`, `www.whoneedhelp.com`, and `test.whoneedhelp.com` must point to the verified server address before Caddy diff --git a/scripts/edge-up.sh b/scripts/edge-up.sh index e7089f4..ed1eeea 100755 --- a/scripts/edge-up.sh +++ b/scripts/edge-up.sh @@ -15,7 +15,15 @@ fi "$ROOT/scripts/validate-edge-env.sh" "$env_file" +edge_compose_project_name=$(awk ' + index($0, "EDGE_COMPOSE_PROJECT_NAME=") == 1 { + print substr($0, length("EDGE_COMPOSE_PROJECT_NAME=") + 1) + exit + } +' "$env_file") + exec docker compose \ + --project-name "$edge_compose_project_name" \ --project-directory "$ROOT" \ --env-file "$env_file" \ --file "$ROOT/compose.edge.yaml" \ diff --git a/scripts/quality.sh b/scripts/quality.sh index 36787cd..0e554ff 100755 --- a/scripts/quality.sh +++ b/scripts/quality.sh @@ -323,9 +323,11 @@ if ./scripts/validate-production-env.sh \ fi echo "Rendering every Docker Compose profile" -docker compose --project-directory "$ROOT" --env-file "$production_env" \ +docker compose --project-name who_need_help_edge \ + --project-directory "$ROOT" --env-file "$production_env" \ --file compose.edge.yaml config --format json | jq --exit-status ' + .name == "who_need_help_edge" and (.services.edge.image | startswith("who-need-help:caddy-production-")) and .services.edge.user == "1000:1000" and .services.edge.read_only == true and diff --git a/scripts/validate-edge-env.sh b/scripts/validate-edge-env.sh index 49f502f..3028708 100755 --- a/scripts/validate-edge-env.sh +++ b/scripts/validate-edge-env.sh @@ -113,6 +113,7 @@ validate_upstream TEST_UPSTREAM "$test_upstream" } docker compose \ + --project-name "$compose_project_name" \ --project-directory "$ROOT" \ --env-file "$env_file" \ --file "$ROOT/compose.edge.yaml" \ diff --git a/test/who_need_help/support_and_content_removal_test.exs b/test/who_need_help/support_and_content_removal_test.exs index df067dd..a902c4e 100644 --- a/test/who_need_help/support_and_content_removal_test.exs +++ b/test/who_need_help/support_and_content_removal_test.exs @@ -22,14 +22,32 @@ defmodule WhoNeedHelp.SupportAndContentRemovalTest do assert request.contact_email == "person@example.com" assert is_nil(request.contact_verified_at) + test_pid = self() + assert_email_sent(fn email -> - email.subject =~ request.reference and email.text_body =~ Support.status_url(request) + matches_request = + email.subject =~ request.reference and + email.text_body =~ "/support/cases/#{request.id}?token=" + + if matches_request, do: send(test_pid, {:support_acknowledgement, email}) + matches_request end) + assert_receive {:support_acknowledgement, email} + + [status_url] = + Regex.run( + ~r{https?://[^\s]+/support/cases/#{Regex.escape(request.id)}\?token=[^\s]+}, + email.text_body + ) + + emailed_token = + status_url |> URI.parse() |> Map.fetch!(:query) |> URI.decode_query() |> Map.fetch!("token") + assert {:error, :not_found} = Support.get_by_access_token(request.id, "invalid") assert {:ok, verified} = - Support.get_by_access_token(request.id, Support.access_token(request)) + Support.get_by_access_token(request.id, emailed_token) assert verified.contact_verified_at