test(e2e): guard staging orphan cleanup coverage
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
CI / Python Lint & Test (pull_request) Successful in 11s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 12s
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 10s
E2E API Smoke Test / detect-changes (pull_request) Successful in 21s
CI / Detect changes (pull_request) Successful in 22s
E2E Chat / detect-changes (pull_request) Successful in 19s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 17s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 13s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 13s
gate-check-v3 / gate-check (pull_request) Successful in 7s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 11s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / review-refire (pull_request) Has been skipped
sop-checklist / all-items-acked (pull_request) Successful in 13s
security-review / approved (pull_request) Failing after 13s
qa-review / approved (pull_request) Failing after 13s
sop-tier-check / tier-check (pull_request) Successful in 11s
CI / Platform (Go) (pull_request) Successful in 6s
CI / Canvas (Next.js) (pull_request) Successful in 4s
E2E Chat / E2E Chat (pull_request) Successful in 4s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 16s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m4s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 9s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
CI / all-required (pull_request) Successful in 1m40s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 1m8s
audit-force-merge / audit (pull_request) Successful in 7s
ci-arm64-advisory / fast-checks (pull_request) Waiting to run
CI / Python Lint & Test (pull_request) Successful in 11s
Block internal-flavored paths / Block forbidden paths (pull_request) Successful in 12s
Lint shellcheck (arm64 pilot) / shellcheck-arm64 (pilot) (pull_request) Successful in 10s
E2E API Smoke Test / detect-changes (pull_request) Successful in 21s
CI / Detect changes (pull_request) Successful in 22s
E2E Chat / detect-changes (pull_request) Successful in 19s
Handlers Postgres Integration / detect-changes (pull_request) Successful in 12s
E2E Staging Canvas (Playwright) / detect-changes (pull_request) Successful in 17s
Lint no tenant GITEA or GITHUB token write / Scan for repo-host token write into tenant workspace surface (pull_request) Successful in 13s
Lint forbidden tenant-env keys / Scan workspace_secrets writers for forbidden env keys (pull_request) Successful in 13s
gate-check-v3 / gate-check (pull_request) Successful in 7s
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 11s
sop-checklist / na-declarations (pull_request) N/A: (none)
sop-checklist / review-refire (pull_request) Has been skipped
sop-checklist / all-items-acked (pull_request) Successful in 13s
security-review / approved (pull_request) Failing after 13s
qa-review / approved (pull_request) Failing after 13s
sop-tier-check / tier-check (pull_request) Successful in 11s
CI / Platform (Go) (pull_request) Successful in 6s
CI / Canvas (Next.js) (pull_request) Successful in 4s
E2E Chat / E2E Chat (pull_request) Successful in 4s
CI / Shellcheck (E2E scripts) (pull_request) Successful in 16s
lint-required-no-paths / lint-required-no-paths (pull_request) Successful in 1m4s
Handlers Postgres Integration / Handlers Postgres Integration (pull_request) Successful in 9s
CI / Canvas Deploy Reminder (pull_request) Has been skipped
E2E Staging Canvas (Playwright) / Canvas tabs E2E (pull_request) Successful in 3s
CI / all-required (pull_request) Successful in 1m40s
E2E API Smoke Test / E2E API Smoke Test (pull_request) Successful in 1m8s
audit-force-merge / audit (pull_request) Successful in 7s
This commit is contained in:
@@ -2,10 +2,16 @@
|
|||||||
# lint_cleanup_traps.sh — regression gate for the OSS-shape program's
|
# lint_cleanup_traps.sh — regression gate for the OSS-shape program's
|
||||||
# "all E2E tests must have proper cleanup" bar (RFC #2873).
|
# "all E2E tests must have proper cleanup" bar (RFC #2873).
|
||||||
#
|
#
|
||||||
# Asserts: every shell file under tests/e2e/ that calls `mktemp` ALSO
|
# Asserts:
|
||||||
# installs an `EXIT` trap somewhere in the file. The trap is the
|
# 1. every shell file under tests/e2e/ that calls `mktemp` ALSO
|
||||||
# minimum-viable guarantee that scratch files won't leak when an
|
# installs an `EXIT` trap somewhere in the file.
|
||||||
# assertion or curl exits the script non-zero.
|
# 2. every staging tenant E2E script that provisions a real org uses a
|
||||||
|
# slug prefix caught by sweep-stale-e2e-orgs.yml and installs an
|
||||||
|
# EXIT trap.
|
||||||
|
#
|
||||||
|
# These are the minimum-viable guarantees that scratch files and real
|
||||||
|
# staging EC2 tenants converge back to zero when an assertion or curl
|
||||||
|
# exits the script non-zero.
|
||||||
#
|
#
|
||||||
# Why this lints (instead of the test runner enforcing): shell scripts
|
# Why this lints (instead of the test runner enforcing): shell scripts
|
||||||
# can't easily be wrapped by an outer harness without breaking the
|
# can't easily be wrapped by an outer harness without breaking the
|
||||||
@@ -21,6 +27,7 @@
|
|||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
cd "$(dirname "$0")"
|
||||||
|
repo_root="$(cd ../.. && pwd)"
|
||||||
|
|
||||||
violations=0
|
violations=0
|
||||||
for f in test_*.sh; do
|
for f in test_*.sh; do
|
||||||
@@ -32,6 +39,76 @@ for f in test_*.sh; do
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
if ! python3 - "$repo_root" <<'PY'
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
repo = Path(sys.argv[1])
|
||||||
|
e2e_dir = repo / "tests" / "e2e"
|
||||||
|
sweeper = repo / ".gitea" / "workflows" / "sweep-stale-e2e-orgs.yml"
|
||||||
|
|
||||||
|
errors: list[str] = []
|
||||||
|
sweeper_text = sweeper.read_text()
|
||||||
|
|
||||||
|
required_sweeper_prefixes = ('"e2e-"', '"rt-e2e-"')
|
||||||
|
for prefix in required_sweeper_prefixes:
|
||||||
|
if prefix not in sweeper_text:
|
||||||
|
errors.append(
|
||||||
|
f"::error file=.gitea/workflows/sweep-stale-e2e-orgs.yml::"
|
||||||
|
f"missing stale-org sweeper prefix {prefix}"
|
||||||
|
)
|
||||||
|
|
||||||
|
slug_assignment_re = re.compile(r'^\s*SLUG=(["\'])(?P<value>.+?)\1', re.MULTILINE)
|
||||||
|
covered_prefixes = ("e2e-", "rt-e2e-")
|
||||||
|
|
||||||
|
for path in sorted(e2e_dir.glob("test_*staging*.sh")):
|
||||||
|
text = path.read_text()
|
||||||
|
creates_org = "/cp/admin/orgs" in text and re.search(r"\bPOST\b", text)
|
||||||
|
deletes_org = "/cp/admin/tenants" in text and re.search(r"\bDELETE\b", text)
|
||||||
|
if not (creates_org or deletes_org):
|
||||||
|
continue
|
||||||
|
|
||||||
|
rel = path.relative_to(repo)
|
||||||
|
if not re.search(r"trap\s+.*\bEXIT\b", text):
|
||||||
|
errors.append(
|
||||||
|
f"::error file={rel}::staging tenant E2E touches CP org lifecycle "
|
||||||
|
"but has no EXIT trap for teardown"
|
||||||
|
)
|
||||||
|
|
||||||
|
assignments = [m.group("value") for m in slug_assignment_re.finditer(text)]
|
||||||
|
if not assignments:
|
||||||
|
errors.append(
|
||||||
|
f"::error file={rel}::staging tenant E2E touches CP org lifecycle "
|
||||||
|
"but has no quoted SLUG=... assignment for scoped cleanup"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
for value in assignments:
|
||||||
|
literal_prefix = re.split(r"[$`]", value, maxsplit=1)[0]
|
||||||
|
if not literal_prefix:
|
||||||
|
errors.append(
|
||||||
|
f"::error file={rel}::SLUG assignment starts with dynamic data "
|
||||||
|
f"({value!r}); use a fixed e2e-* or rt-e2e-* prefix so "
|
||||||
|
"sweep-stale-e2e-orgs can reap orphans"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
if not literal_prefix.startswith(covered_prefixes):
|
||||||
|
errors.append(
|
||||||
|
f"::error file={rel}::SLUG prefix {literal_prefix!r} is not "
|
||||||
|
"covered by sweep-stale-e2e-orgs.yml; use e2e-* or rt-e2e-*"
|
||||||
|
)
|
||||||
|
|
||||||
|
if errors:
|
||||||
|
print("\n".join(errors))
|
||||||
|
raise SystemExit(1)
|
||||||
|
|
||||||
|
print("✓ staging tenant E2E slug prefixes are covered by sweep-stale-e2e-orgs and use EXIT traps")
|
||||||
|
PY
|
||||||
|
then
|
||||||
|
violations=$((violations + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
if [ "$violations" -gt 0 ]; then
|
if [ "$violations" -gt 0 ]; then
|
||||||
echo "::error::$violations shell E2E file(s) leak scratch on early exit. See above."
|
echo "::error::$violations shell E2E file(s) leak scratch on early exit. See above."
|
||||||
exit 1
|
exit 1
|
||||||
|
|||||||
Reference in New Issue
Block a user