Deferred from #1815 (which closes #1592). #1815 hardened 1 of the 3 aws ecr delete-repository --force sites in .github/workflows/; the other two are unchanged and carry the same defect class #1592 was filed for.
Enumerated by predicate across .github/workflows/*.yml (rg -c 'delete-repository'), not sampled:
| file |
sites |
cleanup-staging.yml |
2 |
destroy-fargate-dev.yml |
1 (hardened by #1815) |
What
Both cleanup-staging.yml sites select repositories with a case "$REPO" in cudly-staging*) prefix allow-list and swallow failures with 2>/dev/null || echo.
Why it matters
A prefix allow-list is the exact shape #1592 argued against. cudly-staging* also selects cudly-staging-prod-mirror, cudly-staging-1a2b3c4d-backup, and any other repository an operator names with that prefix, and the workflow force-deletes every image in them. Swallowing the failure means a partial deletion reports success.
The state-lock half of #1592 was already fixed by #1806; this is the remaining name-matching half in the sibling workflow. The recurrence mode for this class is precisely "the guard landed in one workflow and not the sibling", which is how #1592 arose after cleanup-staging.yml was hardened in #1235/#1330.
Fix direction
Reuse scripts/select-ecr-repos-to-delete.sh, added by #1815. It compares by exact equality against a name read from terraform output, exits 2 rather than degrading on an empty or whitespace-bearing name, and has a self-test asserting both directions. The staging workflow needs the same treatment: resolve the owned repository name from the state being torn down and pipe the account listing through the selector.
Note the selector's stdin contract when adopting it: see the trailing-newline handling that #1815 addresses.
Verification bar
Assert both directions, as the existing selector suite does: a substring or prefix near-miss is refused, AND the legitimate owned repository is still selected. A selector that returns nothing passes a refusal-only suite while silently leaving the repository behind.
Deferred from #1815 (which closes #1592). #1815 hardened 1 of the 3
aws ecr delete-repository --forcesites in.github/workflows/; the other two are unchanged and carry the same defect class #1592 was filed for.Enumerated by predicate across
.github/workflows/*.yml(rg -c 'delete-repository'), not sampled:cleanup-staging.ymldestroy-fargate-dev.ymlWhat
Both
cleanup-staging.ymlsites select repositories with acase "$REPO" in cudly-staging*)prefix allow-list and swallow failures with2>/dev/null || echo.Why it matters
A prefix allow-list is the exact shape #1592 argued against.
cudly-staging*also selectscudly-staging-prod-mirror,cudly-staging-1a2b3c4d-backup, and any other repository an operator names with that prefix, and the workflow force-deletes every image in them. Swallowing the failure means a partial deletion reports success.The state-lock half of #1592 was already fixed by #1806; this is the remaining name-matching half in the sibling workflow. The recurrence mode for this class is precisely "the guard landed in one workflow and not the sibling", which is how #1592 arose after
cleanup-staging.ymlwas hardened in #1235/#1330.Fix direction
Reuse
scripts/select-ecr-repos-to-delete.sh, added by #1815. It compares by exact equality against a name read fromterraform output, exits 2 rather than degrading on an empty or whitespace-bearing name, and has a self-test asserting both directions. The staging workflow needs the same treatment: resolve the owned repository name from the state being torn down and pipe the account listing through the selector.Note the selector's stdin contract when adopting it: see the trailing-newline handling that #1815 addresses.
Verification bar
Assert both directions, as the existing selector suite does: a substring or prefix near-miss is refused, AND the legitimate owned repository is still selected. A selector that returns nothing passes a refusal-only suite while silently leaving the repository behind.