From 6793f77733fba54081fa5b77f42e66203a1f17cc Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 10 Oct 2026 17:26:12 +0200 Subject: [PATCH] fix(deploy): stub git status in the simulation harness MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clean-tree guard in ci-deploy.sh aborts the release when `git status --porcelain` prints anything. The harness stubs `git` as a shell function that only special-cases `rev-parse`; every other subcommand fell through to its `ls-remote`-shaped printf, so `git status` emitted a fake refs/heads/main line and the guard failed on every scenario. Introduced in fdb7af7e, which added the guard without teaching the harness about it, so all 21 deploy tests have been failing since. This stubs `status` to report a clean tree, and adds a scenario that asserts the guard actually stops a release before it builds, migrates or starts anything — the guard itself had no coverage, which is how it could break silently in the first place. --- src/lib/ci-deploy.test.ts | 11 +++++++++++ src/test/ci-deploy-harness.sh | 8 ++++++++ 2 files changed, 19 insertions(+) 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"