Complete staff translations and release verification

This commit is contained in:
SimpleTest 2026-08-03 13:35:45 +03:00
parent cf283db350
commit 4c52c8bf3d
12 changed files with 7565 additions and 4440 deletions

View File

@ -1978,3 +1978,14 @@ complete.
- Production capacity, minimum CPU/RAM, database HA topology, and autoscaling - Production capacity, minimum CPU/RAM, database HA topology, and autoscaling
thresholds remain unknown until representative target-environment thresholds remain unknown until representative target-environment
measurements exist. measurements exist.
# 2026-08-03 release-candidate quality gate
The isolated full repository quality run completed successfully on 2026-08-03.
It covered shell, Compose, Helm, source-secret and infrastructure checks; Elixir
format, compilation with warnings as errors, Gettext catalog freshness, xref,
Credo, Sobelow, Dialyzer, Hex audit and the full ExUnit suite; pinned runtime
image smoke tests and vulnerability scans; and production release, rollback and
migration-policy drills. The transient Compose project, database volume and
audit images were removed by the run's cleanup trap. This verifies the local
candidate only; production browser E2E must be repeated after that candidate is
promoted.

View File

@ -974,8 +974,10 @@ defmodule WhoNeedHelpWeb.ModerationLive do
</div> </div>
<span class="badge badge-outline">{status_label(proposal.mode)}</span> <span class="badge badge-outline">{status_label(proposal.mode)}</span>
<span class={["badge", status_class(proposal.status)]}>{status_label(proposal.status)}</span> <span class={["badge", status_class(proposal.status)]}>{status_label(proposal.status)}</span>
<span class="text-sm text-base-content/60">{gettext("%{count} votes", <span class="text-sm text-base-content/60">{ngettext(
count: proposal.vote_count "%{count} vote",
"%{count} votes",
proposal.vote_count
)}</span> )}</span>
<.icon name="hero-chevron-down" class="size-5 transition group-open:rotate-180" /> <.icon name="hero-chevron-down" class="size-5 transition group-open:rotate-180" />
</summary> </summary>

View File

@ -112,9 +112,15 @@ defmodule WhoNeedHelpWeb.SupportOperationLive do
end end
defp assignment_options(users) do defp assignment_options(users) do
[{gettext("Unassigned"), ""} | Enum.map(users, &{&1.display_name || &1.email, &1.id})] [{gettext("Unassigned"), ""} | Enum.map(users, &{staff_identity_label(&1), &1.id})]
end end
defp staff_identity_label(%{display_name: name, email: email})
when is_binary(name) and name != "" and name != email,
do: gettext("%{name} — %{email}", name: name, email: email)
defp staff_identity_label(%{email: email}), do: email
defp queue_path(:support), do: ~p"/support/operations?queue=support" defp queue_path(:support), do: ~p"/support/operations?queue=support"
defp queue_path(:legal), do: ~p"/support/operations?queue=legal" defp queue_path(:legal), do: ~p"/support/operations?queue=legal"

View File

@ -231,10 +231,16 @@ defmodule WhoNeedHelpWeb.SupportOperationsLive do
{gettext("Any assignee"), ""}, {gettext("Any assignee"), ""},
{gettext("Unassigned"), "unassigned"}, {gettext("Unassigned"), "unassigned"},
{gettext("Assigned to me"), "mine"} {gettext("Assigned to me"), "mine"}
| Enum.map(users, fn user -> {user.display_name || user.email, user.id} end) | Enum.map(users, fn user -> {staff_identity_label(user), user.id} end)
] ]
end end
defp staff_identity_label(%{display_name: name, email: email})
when is_binary(name) and name != "" and name != email,
do: gettext("%{name} — %{email}", name: name, email: email)
defp staff_identity_label(%{email: email}), do: email
defp support_kind(:account_access), do: gettext("Account access") defp support_kind(:account_access), do: gettext("Account access")
defp support_kind(:technical_issue), do: gettext("Technical issue") defp support_kind(:technical_issue), do: gettext("Technical issue")
defp support_kind(:safety_concern), do: gettext("Safety concern") defp support_kind(:safety_concern), do: gettext("Safety concern")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -167,16 +167,19 @@ git cat-file -e "$commit^{commit}" 2>/dev/null || {
exit 1 exit 1
} }
while IFS= read -r changed_path; do # The deployed application is always archived from its exact remote commit.
case "$changed_path" in # Local development may legitimately be ahead of production; those files never
lib/mix/tasks/wnh.staging_full_e2e.ex | e2e/tests/activity-moderation.spec.ts | e2e/tests/helpers.ts | test/who_need_help/trust_safety_test.exs) # enter either runner image. Only these explicit verifier overlays are copied
;; # below, and their hashes are retained with the evidence bundle.
*) for verifier_path in \
echo "Production E2E refused: unexpected local change relative to $commit: $changed_path" >&2 lib/mix/tasks/wnh.staging_full_e2e.ex \
e2e/tests/activity-moderation.spec.ts \
e2e/tests/helpers.ts; do
if [[ ! -f "$ROOT/$verifier_path" ]]; then
echo "Production E2E verifier is unavailable: $verifier_path" >&2
exit 1 exit 1
;; fi
esac done
done < <(git diff --name-only "$commit")
confirmation="whoneedhelp.com:${commit}:${database}:${RUN_ID}:full-browser-e2e" confirmation="whoneedhelp.com:${commit}:${database}:${RUN_ID}:full-browser-e2e"

View File

@ -1463,6 +1463,7 @@ echo "Running Elixir format, compiler, xref, Credo, Sobelow, Dialyzer, and Hex a
docker run --rm "$quality_image" sh -euc ' docker run --rm "$quality_image" sh -euc '
mix format --check-formatted mix format --check-formatted
mix compile --force --warnings-as-errors mix compile --force --warnings-as-errors
mix gettext.extract --check-up-to-date
mix xref graph --label compile-connected --fail-above 0 mix xref graph --label compile-connected --fail-above 0
mix credo --strict --min-priority high mix credo --strict --min-priority high
mix sobelow --exit --strict --private --skip mix sobelow --exit --strict --private --skip

View File

@ -407,6 +407,18 @@ defmodule WhoNeedHelpWeb.SupportControllerTest do
test "renders separate support and removal queues for a moderator", %{conn: conn} do test "renders separate support and removal queues for a moderator", %{conn: conn} do
moderator = staff_user_fixture([:support, :legal]) moderator = staff_user_fixture([:support, :legal])
first_duplicate =
staff_user_fixture([:support, :legal],
display_name: "Same operator",
email: unique_user_email()
)
second_duplicate =
staff_user_fixture([:support, :legal],
display_name: "Same operator",
email: unique_user_email()
)
{:ok, support_request} = {:ok, support_request} =
WhoNeedHelp.Support.create_request(nil, %{ WhoNeedHelp.Support.create_request(nil, %{
"kind" => "technical_issue", "kind" => "technical_issue",
@ -447,6 +459,8 @@ defmodule WhoNeedHelpWeb.SupportControllerTest do
assert response =~ "Support requests" assert response =~ "Support requests"
assert response =~ "Legal and removal" assert response =~ "Legal and removal"
assert response =~ "Same operator — #{first_duplicate.email}"
assert response =~ "Same operator — #{second_duplicate.email}"
assert has_element?(queue_view, "#support-row-#{support_request.id}") assert has_element?(queue_view, "#support-row-#{support_request.id}")
refute response =~ "Operator action" refute response =~ "Operator action"
@ -471,6 +485,8 @@ defmodule WhoNeedHelpWeb.SupportControllerTest do
) == 1 ) == 1
assert has_element?(removal_view, "#removal-moderation-#{removal_notice.id}") assert has_element?(removal_view, "#removal-moderation-#{removal_notice.id}")
assert removal_response =~ "Same operator — #{first_duplicate.email}"
assert removal_response =~ "Same operator — #{second_duplicate.email}"
assert Enum.count( assert Enum.count(
LazyHTML.query( LazyHTML.query(

View File

@ -1556,12 +1556,14 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
"message_id" => message.id "message_id" => message.id
}) })
{:ok, _proposal} = {:ok, proposal} =
Catalog.propose(user_scope_fixture(requester), %{ Catalog.propose(user_scope_fixture(requester), %{
"proposed_name" => "Bicycle repair", "proposed_name" => "Bicycle repair",
"reason" => "Urgent roadside bicycle repairs need a reusable category." "reason" => "Urgent roadside bicycle repairs need a reusable category."
}) })
{:ok, _vote} = Catalog.vote(user_scope_fixture(requester), proposal.id)
{:ok, view, html} = live(conn, ~p"/moderation") {:ok, view, html} = live(conn, ~p"/moderation")
assert html =~ "Trust and safety" assert html =~ "Trust and safety"
assert html =~ "Reports" assert html =~ "Reports"
@ -1592,6 +1594,8 @@ defmodule WhoNeedHelpWeb.MutualAidLiveTest do
proposal_html = render_patch(view, ~p"/moderation?queue=proposals") proposal_html = render_patch(view, ~p"/moderation?queue=proposals")
assert proposal_html =~ "Bicycle repair" assert proposal_html =~ "Bicycle repair"
assert proposal_html =~ "1 vote"
refute proposal_html =~ "1 votes"
end end
test "message reports expose parent activity moderation and switch to restore", %{ test "message reports expose parent activity moderation and switch to restore", %{