fix(ci): confine the sweep self-test to its own run
CI / verify (pull_request) Successful in 1m10s
CI / infrastructure-smoke (pull_request) Successful in 42s
CI / package-release (pull_request) Successful in 49s
CI / migration-smoke (pull_request) Successful in 2m21s
CI / image-build (pull_request) Successful in 59s
CI / deploy-smoke (same-origin) (pull_request) Successful in 52s
CI / deploy-smoke (split) (pull_request) Successful in 2m35s
CI / package-publication (pull_request) Successful in 1m3s

scripts/test-ci-sweep.sh proves the stale sweep by running
`ci-sweep.sh --stale --max-age-seconds 0` on the runner's shared Docker
daemon. With a zero threshold that removes every `ekklesia-ci-` stack on
the daemon, not just the test's own: any job running at that moment
loses its containers, networks, and volumes. It reported "CI sweep
checks passed" while doing it. Core's infrastructure-smoke job runs the
self-test on every run, and twice tore down Signage's integration stack
mid-scenario (Zayin/Signage run 288: its first attempt, and the second
one during connectivity-blip).

`ci-sweep.sh --stale` now takes `--prefix P`, which narrows the sweep to
stacks whose names begin with P and must itself begin with the CI
prefix, so it can only narrow a sweep, never widen one. The self-test
confines every stale sweep to its own fake run and proves that another
run's stack survives even the zero threshold. The workflow's own sweeps
are unchanged.

Regression: a bystander stack from another run in the self-test, and a
Docker-free guard that every stale sweep in the self-test is confined
and the workflow's keep the default threshold.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
ekklesia
2026-10-13 22:46:38 -05:00
co-authored by Claude Opus 5.5
parent 437720f228
commit 94fa7a17a8
3 changed files with 60 additions and 7 deletions
+28
View File
@@ -128,6 +128,34 @@ describe("CI scripts on a shared Docker daemon", () => {
expect(workflow.jobs["deploy-smoke"]?.env?.EKKLESIA_CI_JOB_KEY).toContain("matrix.topology");
});
it("never sweep another run's live stack early, not even from the sweep's own self-test", async () => {
// The workflow's stale sweeps keep the default threshold, which is far above any job's run time.
const workflow = await source(".github/workflows/ci.yml");
const workflowSweeps = workflow
.split("\n")
.filter((line) => line.includes("ci-sweep.sh --stale"));
expect(workflowSweeps.length).toBeGreaterThan(0);
for (const line of workflowSweeps) {
expect(line).not.toContain("--max-age-seconds");
}
// The self-test does use short thresholds, so every stale sweep it runs is confined to its own
// fake run. Unconfined, `--max-age-seconds 0` removed every CI stack on the shared daemon —
// including a module repository's integration stack mid-run.
const selfTest = await source("scripts/test-ci-sweep.sh");
expect(selfTest).toMatch(/^own_run="\$\(ci_name_safe "ekklesia-ci-\$\{GITHUB_RUN_ID\}-"\)"$/mu);
expect(selfTest.indexOf('export GITHUB_RUN_ID="sweeptest')).toBeLessThan(
selfTest.indexOf("own_run="),
);
const selfTestSweeps = selfTest
.split("\n")
.filter((line) => /^\s*bash scripts\/ci-sweep\.sh --stale/u.test(line));
expect(selfTestSweeps.length).toBeGreaterThan(0);
for (const line of selfTestSweeps) {
expect(line).toContain('--prefix "$own_run"');
}
});
it("choose the deploy smoke's edge subnet from those not in use, and choose again on a race", async () => {
const deploy = await source("scripts/smoke-deploy.sh");
expect(deploy).not.toMatch(/^octet=\$\(\(RANDOM/mu);