chore(scripts): dry-run-first staging test-account purge (BUNYIP-273) #336

Merged
vas2000-work merged 3 commits from chore/BUNYIP-273-purge-test-accounts into main 2026-07-04 06:36:07 +02:00
Owner

What

Adds scripts/purge-staging-test-accounts.sh to clear the ~400 accumulated seeded / manual test accounts from a non-production bunyip database (BUNYIP-273). Since BUNYIP-273 is destructive and needs staging DB access, this is an operator-run tool rather than an automated job.

Why a script and not the admin endpoint

DELETE /admin/users/{id} is a soft delete (UserRepository::soft_delete). That leaves soft-delete residue and eventually collides with the BUNYIP-161 partial unique index on (email) WHERE deleted_at IS NULL. The script instead mirrors the supported hard-delete path (UserRepository::clear_deps_and_delete_users, the BUNYIP-246 semantics) statement for statement: clear audit_logs.actor_id and admin_notifications (user_id + read_by), then DELETE FROM users, letting FK cascades remove the rest. It touches no encryption keys, so it cannot orphan retained encrypted data (the rotation risk called out in the issue).

Safety model

  • Dry-run by default; deletes only with an explicit --commit.
  • --pattern is required (a raw SQL LIKE against users.email) and has no default, so a bare run can never match everything.
  • role = 'admin' rows are always excluded.
  • --commit additionally requires --i-know-this-is-not-prod and a typed confirmation phrase.
  • Prints the target DB host (never credentials) and the matched count/list so the operator can confirm no intentional QA-seed accounts (PMS-331 / PMS-239) are caught before committing.

Usage

# preview (read-only)
scripts/purge-staging-test-accounts.sh --database-url "$STAGING_DATABASE_URL" --pattern '%+test%'
# commit
scripts/purge-staging-test-accounts.sh --database-url "$STAGING_DATABASE_URL" --pattern '%+test%' --i-know-this-is-not-prod --commit

Satisfies the hard-delete / no-residue / no-key-rotation acceptance criteria. The optional "recurring purge / naming convention" AC is left as a follow-up.

#BUNYIP-273

## What Adds `scripts/purge-staging-test-accounts.sh` to clear the ~400 accumulated seeded / manual test accounts from a non-production bunyip database (BUNYIP-273). Since BUNYIP-273 is destructive and needs staging DB access, this is an operator-run tool rather than an automated job. ## Why a script and not the admin endpoint `DELETE /admin/users/{id}` is a **soft** delete (`UserRepository::soft_delete`). That leaves soft-delete residue and eventually collides with the BUNYIP-161 partial unique index on `(email) WHERE deleted_at IS NULL`. The script instead mirrors the supported **hard**-delete path (`UserRepository::clear_deps_and_delete_users`, the BUNYIP-246 semantics) statement for statement: clear `audit_logs.actor_id` and `admin_notifications` (`user_id` + `read_by`), then `DELETE FROM users`, letting FK cascades remove the rest. It touches no encryption keys, so it cannot orphan retained encrypted data (the rotation risk called out in the issue). ## Safety model - Dry-run by default; deletes only with an explicit `--commit`. - `--pattern` is required (a raw SQL LIKE against `users.email`) and has no default, so a bare run can never match everything. - `role = 'admin'` rows are always excluded. - `--commit` additionally requires `--i-know-this-is-not-prod` and a typed confirmation phrase. - Prints the target DB host (never credentials) and the matched count/list so the operator can confirm no intentional QA-seed accounts (PMS-331 / PMS-239) are caught before committing. ## Usage ``` # preview (read-only) scripts/purge-staging-test-accounts.sh --database-url "$STAGING_DATABASE_URL" --pattern '%+test%' # commit scripts/purge-staging-test-accounts.sh --database-url "$STAGING_DATABASE_URL" --pattern '%+test%' --i-know-this-is-not-prod --commit ``` Satisfies the hard-delete / no-residue / no-key-rotation acceptance criteria. The optional "recurring purge / naming convention" AC is left as a follow-up. #BUNYIP-273
chore(scripts): add dry-run-first staging test-account purge (BUNYIP-273)
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 1m11s
Check / fmt + clippy + build + tests (pull_request) Successful in 9m59s
86cf01ee81
Adds scripts/purge-staging-test-accounts.sh to hard-delete accumulated seeded/manual test accounts from a non-production bunyip database. The admin DELETE /admin/users/{id} endpoint is a soft delete, which leaves residue and eventually clashes with the BUNYIP-161 partial unique index; this script instead mirrors the supported BUNYIP-246 hard-delete path (UserRepository::clear_deps_and_delete_users) statement for statement: clear audit_logs.actor_id and admin_notifications (user_id + read_by), then DELETE FROM users, letting FK cascades handle the rest. It touches no encryption keys, so it cannot orphan retained encrypted data.

Safety: dry-run by default; --pattern is required (no default, so a bare run can never match everything); role='admin' rows are always excluded; --commit additionally requires --i-know-this-is-not-prod plus a typed confirmation phrase. Operator previews the matched count/list before committing to confirm no intentional QA-seed accounts are caught.

#BUNYIP-273
Merge branch 'main' into chore/BUNYIP-273-purge-test-accounts
Some checks failed
E2E / Playwright against deployment (pull_request) Successful in 34s
Check / fmt + clippy + build + tests (pull_request) Has been cancelled
3785315a8a
vas2000-work scheduled this pull request to auto merge when all checks succeed 2026-07-04 06:21:13 +02:00
Merge branch 'main' into chore/BUNYIP-273-purge-test-accounts
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 1m12s
Check / fmt + clippy + build + tests (pull_request) Successful in 10m49s
Create release / Create release from merged PR (pull_request) Has been skipped
5885130dd1
vas2000-work deleted branch chore/BUNYIP-273-purge-test-accounts 2026-07-04 06:36:07 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!336
No description provided.