From 006c7e041f39d8c445fa8467b6f6b76dba9594e6 Mon Sep 17 00:00:00 2001 From: vince Date: Sat, 20 Jun 2026 16:59:36 +0200 Subject: [PATCH] fix(ci): guard deploy auto fallback --- apps/web/src/lib/workspace-boundary.test.ts | 96 +++++++++++++++++++++ scripts/deploy-dimensionlab-website.sh | 1 + 2 files changed, 97 insertions(+) diff --git a/apps/web/src/lib/workspace-boundary.test.ts b/apps/web/src/lib/workspace-boundary.test.ts index b70aafe..77ee6a6 100644 --- a/apps/web/src/lib/workspace-boundary.test.ts +++ b/apps/web/src/lib/workspace-boundary.test.ts @@ -326,6 +326,60 @@ describe("workspace boundaries", () => { expect(result.log.match(/^stop dimensionlab-website$/gm)).toHaveLength(2); }); + test("rolls back the latest image when the container fails to restart", () => { + const result = runDeployScriptWithFakes( + { + curl: passingCurlCommand, + git: fakeGitCommand, + podman: fakePodmanCommand("dimensionlab-website.service"), + }, + { + DEPLOY_CONTAINER_CLI: "podman", + DEPLOY_CONTAINER_START_TIMEOUT_SECONDS: "0", + DEPLOY_EVENT_NAME: "push", + DEPLOY_REF: "refs/heads/main", + DEPLOY_RESTART_STRATEGY: "quadlet-container", + DEPLOY_SHA: "1234567890abcdef", + }, + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain("did not restart on localhost/dimensionlab-website:latest"); + expect(result.stderr).toContain("rolling back to localhost/dimensionlab-website:rollback-"); + expect(result.log).toMatch( + /tag localhost\/dimensionlab-website:rollback-\d{14} localhost\/dimensionlab-website:latest/, + ); + expect(result.log.match(/^stop dimensionlab-website$/gm)).toHaveLength(2); + }); + + test.each([ + { + env: { DEPLOY_EVENT_NAME: "pull_request", DEPLOY_REF: "refs/heads/main" }, + message: "production deploys only run for push", + }, + { + env: { DEPLOY_EVENT_NAME: "push", DEPLOY_REF: "refs/heads/codex/test" }, + message: "expected refs/heads/main", + }, + ])("refuses guarded deploy contexts before host mutations", ({ env, message }) => { + const result = runDeployScriptWithFakes( + { + curl: passingCurlCommand, + git: fakeGitCommand, + podman: fakePodmanCommand("dimensionlab-website.service"), + }, + { + DEPLOY_CONTAINER_CLI: "podman", + DEPLOY_SHA: "1234567890abcdef", + ...env, + }, + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain(message); + expect(result.log).toBe(""); + }); + test("refuses stop-based deploys unless the container belongs to the expected unit", () => { const result = runDeployScriptWithFakes( { @@ -349,6 +403,30 @@ describe("workspace boundaries", () => { expect(result.log).not.toContain("build "); expect(result.log).not.toContain("stop dimensionlab-website"); }); + + test("checks the expected unit before auto falls back to stopping the container", () => { + const result = runDeployScriptWithFakes( + { + curl: passingCurlCommand, + git: fakeGitCommand, + podman: fakePodmanCommand("other.service"), + systemctl: fakeSystemctlCommand({ active: false, show: true }), + }, + { + DEPLOY_CONTAINER_CLI: "podman", + DEPLOY_EVENT_NAME: "push", + DEPLOY_REF: "refs/heads/main", + DEPLOY_RESTART_STRATEGY: "auto", + DEPLOY_SHA: "1234567890abcdef", + }, + ); + + expect(result.status).toBe(1); + expect(result.stderr).toContain( + "refusing to stop dimensionlab-website; expected PODMAN_SYSTEMD_UNIT=dimensionlab-website.service", + ); + expect(result.log).not.toContain("stop dimensionlab-website"); + }); }); type WorkspacePackageExport = @@ -448,3 +526,21 @@ if [ "$*" = *'/api/dashboard/tiles'* ]; then printf '{"state":"ready","tiles":[]}' fi `; + +function fakeSystemctlCommand(options: { active: boolean; show: boolean }): string { + const activeStatus = options.active ? 0 : 3; + const showStatus = options.show ? 0 : 1; + + return `#!/usr/bin/env bash +printf 'systemctl %s\\n' "$*" >> "$DEPLOY_TEST_LOG" +if [ "$1" = "--user" ] && [ "$2" = "is-active" ]; then + exit ${activeStatus} +fi +if [ "$1" = "--user" ] && [ "$2" = "show" ]; then + exit ${showStatus} +fi +if [ "$1" = "--user" ] && [ "$2" = "restart" ]; then + exit 0 +fi +`; +} diff --git a/scripts/deploy-dimensionlab-website.sh b/scripts/deploy-dimensionlab-website.sh index 05178e0..1101e90 100755 --- a/scripts/deploy-dimensionlab-website.sh +++ b/scripts/deploy-dimensionlab-website.sh @@ -215,6 +215,7 @@ restart_service() { if command -v systemctl >/dev/null 2>&1 && systemctl --user is-active "$SERVICE_NAME" >/dev/null 2>&1; then run systemctl --user restart "$SERVICE_NAME" else + require_container_managed_by_service run "$container_cli" stop "$CONTAINER_NAME" fi ;;