diff --git a/src/lib/ci-deploy.test.ts b/src/lib/ci-deploy.test.ts index 303d9bfc..170fbc2b 100644 --- a/src/lib/ci-deploy.test.ts +++ b/src/lib/ci-deploy.test.ts @@ -169,6 +169,17 @@ describe("deployment transaction", () => { expect(result.output).toContain("No previous container exists"); expect(result.app).toBeNull(); }); + // The image is tagged with the commit SHA, so a dirty tree would ship + // uncommitted code under a label that claims otherwise. The guard has to + // stop the release before anything is built or migrated. + it("refuses to release from a dirty working tree", () => { + const result = simulate("dirty-tree"); + expect(result.status, result.output).not.toBe(0); + expect(result.output).toContain("de werkboom is niet schoon"); + expect(result.calls).not.toContain("docker build"); + expect(result.calls).not.toContain("pnpm db:migrate"); + expect(result.calls).not.toContain("docker run"); + }); }); describe("legacy and CI container coexistence", () => { diff --git a/src/test/ci-deploy-harness.sh b/src/test/ci-deploy-harness.sh index 64dfdb01..7c50faf1 100644 --- a/src/test/ci-deploy-harness.sh +++ b/src/test/ci-deploy-harness.sh @@ -1,6 +1,14 @@ # Sourced only by the deployment simulation tests; no external services are used. git() { if [ "$1" = rev-parse ]; then printf '%s\n' "$TEST_SHA"; return; fi + # `git status --porcelain` is the script's clean-tree guard: empty stdout + # means clean, any output aborts the release before it builds an image. It + # needs its own stub — falling through to the ls-remote-shaped printf below + # would always report a dirty tree and fail every scenario. + if [ "$1" = status ]; then + [ "$SCENARIO" = dirty-tree ] && printf ' M src/leaked-file.ts\n' + return 0 + fi local n=0 if [ -f "$TEST_DIR/remote-count" ]; then read -r n < "$TEST_DIR/remote-count"; fi n=$((n+1)); printf '%s\n' "$n" > "$TEST_DIR/remote-count"