fix(ci): harden website deploy rollback
Some checks failed
Dimension Lab website / ci (pull_request) Failing after 15s
Dimension Lab website / deploy (pull_request) Has been skipped

This commit is contained in:
vince 2026-06-20 16:54:18 +02:00
parent 96fe4d26c7
commit 7e4d13a85c
3 changed files with 193 additions and 5 deletions

View file

@ -1,4 +1,6 @@
import { existsSync, readFileSync } from "node:fs";
import { spawnSync } from "node:child_process";
import { chmodSync, existsSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { describe, expect, test } from "vitest";
@ -292,6 +294,59 @@ describe("workspace boundaries", () => {
expect(deployScript).toContain("--dry-run");
expect(deployScript).toContain("DEPLOY_RESTART_STRATEGY");
});
test("rolls back the latest image when production smoke checks fail", () => {
const result = runDeployScriptWithFakes(
{
curl: failingCurlCommand,
git: fakeGitCommand,
podman: fakePodmanCommand("dimensionlab-website.service"),
},
{
DEPLOY_CONTAINER_CLI: "podman",
DEPLOY_EVENT_NAME: "push",
DEPLOY_REF: "refs/heads/main",
DEPLOY_RESTART_STRATEGY: "quadlet-container",
DEPLOY_SHA: "1234567890abcdef",
DEPLOY_SMOKE_TIMEOUT_SECONDS: "0",
},
);
expect(result.status).toBe(1);
expect(result.stderr).toContain("smoke checks failed");
expect(result.stderr).toContain("rolling back to localhost/dimensionlab-website:rollback-");
expect(result.log).toMatch(
/tag localhost\/dimensionlab-website:latest localhost\/dimensionlab-website:rollback-\d{14}/,
);
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("refuses stop-based deploys unless the container belongs to the expected unit", () => {
const result = runDeployScriptWithFakes(
{
curl: passingCurlCommand,
git: fakeGitCommand,
podman: fakePodmanCommand("other.service"),
},
{
DEPLOY_CONTAINER_CLI: "podman",
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(
"refusing to stop dimensionlab-website; expected PODMAN_SYSTEMD_UNIT=dimensionlab-website.service",
);
expect(result.log).not.toContain("build ");
expect(result.log).not.toContain("stop dimensionlab-website");
});
});
type WorkspacePackageExport =
@ -308,3 +363,86 @@ function expectPackageExport(
): void {
expect(actual).toMatchObject(expected);
}
function runDeployScriptWithFakes(
commands: Record<string, string>,
env: Record<string, string>,
): { log: string; status: number | null; stderr: string; stdout: string } {
const tempDir = mkdtempSync(join(tmpdir(), "dimensionlab-deploy-test-"));
const logPath = join(tempDir, "commands.log");
for (const [name, source] of Object.entries(commands)) {
const commandPath = join(tempDir, name);
writeFileSync(commandPath, source);
chmodSync(commandPath, 0o755);
}
const result = spawnSync("bash", [join(root, "scripts/deploy-dimensionlab-website.sh")], {
cwd: root,
encoding: "utf8",
env: {
...process.env,
...env,
DEPLOY_TEST_LOG: logPath,
PATH: `${tempDir}:${process.env.PATH ?? ""}`,
},
});
return {
log: existsSync(logPath) ? readFileSync(logPath, "utf8") : "",
status: result.status,
stderr: result.stderr,
stdout: result.stdout,
};
}
const fakeGitCommand = `#!/usr/bin/env bash
case "$1" in
branch)
echo main
;;
rev-parse)
echo 1234567890abcdef
;;
config|submodule)
exit 0
;;
esac
`;
function fakePodmanCommand(systemdUnit: string): string {
return `#!/usr/bin/env bash
printf '%s\\n' "$*" >> "$DEPLOY_TEST_LOG"
if [ "$1" = "image" ] && [ "$2" = "inspect" ]; then
if [ "$4" = "--format" ]; then
echo sha256:new
fi
exit 0
fi
if [ "$1" = "inspect" ]; then
case "$*" in
*PODMAN_SYSTEMD_UNIT*)
echo ${systemdUnit}
;;
*State.Running*)
echo true
;;
*'.Image'*|*'{{.Image}}'*)
echo sha256:new
;;
esac
fi
`;
}
const failingCurlCommand = `#!/usr/bin/env bash
printf 'curl %s\\n' "$*" >> "$DEPLOY_TEST_LOG"
exit 22
`;
const passingCurlCommand = `#!/usr/bin/env bash
printf 'curl %s\\n' "$*" >> "$DEPLOY_TEST_LOG"
if [ "$*" = *'/api/dashboard/tiles'* ]; then
printf '{"state":"ready","tiles":[]}'
fi
`;