Scope project refreshes and harden deploy script

This commit is contained in:
kris
2026-04-07 17:20:53 +08:00
parent 1de9ae0492
commit 8fc94f1849
8 changed files with 96 additions and 7 deletions

View File

@@ -73,10 +73,18 @@ exec "$@"
.split("\0")
.map((line) => line.trim())
.filter(Boolean);
const rsyncLog = await readFile(path.join(logDir, "rsync.log"), "utf8");
const rsyncArgs = rsyncLog
.split("\0")
.map((line) => line.trim())
.filter(Boolean)
.join(" ");
assert.equal(sshCalls.length, 2);
assert.match(sshCalls[0] ?? "", /sudo mkdir -p \/opt\/boss/);
assert.match(rsyncArgs, /--rsync-path=sudo rsync/);
assert.match(sshCalls[1] ?? "", /bootstrap-server\.sh/);
assert.match(sshCalls[1] ?? "", /sudo chown -R ubuntu:ubuntu \/opt\/boss\/data \/opt\/boss\/public\/downloads/);
assert.match(sshCalls[1] ?? "", /npm install --omit=dev/);
assert.match(sshCalls[1] ?? "", /systemctl restart boss-web/);
assert.match(sshCalls[1] ?? "", /curl -fsS http:\/\/127\.0\.0\.1:3000\/api\/health/);
@@ -159,6 +167,7 @@ exec "$@"
.join(" ");
assert.match(rsyncArgs, /--exclude \.next/);
assert.match(rsyncArgs, /--rsync-path=sudo rsync/);
const sshLog = await readFile(path.join(logDir, "ssh.log"), "utf8");
const sshCalls = sshLog
@@ -167,6 +176,7 @@ exec "$@"
.filter(Boolean);
assert.equal(sshCalls.length, 2);
assert.match(sshCalls[1] ?? "", /sudo chown -R ubuntu:ubuntu \/opt\/boss\/data \/opt\/boss\/public\/downloads/);
assert.match(sshCalls[1] ?? "", /npm install && BOSS_RUNTIME_ROOT=\/opt\/boss BOSS_STATE_FILE=\/opt\/boss\/data\/boss-state\.json npm run build/);
assert.match(sshCalls[1] ?? "", /npm prune --omit=dev/);
assert.doesNotMatch(sshCalls[1] ?? "", /npm install --omit=dev/);

View File

@@ -0,0 +1,39 @@
import test from "node:test";
import assert from "node:assert/strict";
import path from "node:path";
import { readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
const testsDir = path.dirname(fileURLToPath(import.meta.url));
async function readWorkspaceFile(relativePath: string) {
return readFile(path.join(testsDir, "..", relativePath), "utf8");
}
test("RealtimeRefresh supports project-scoped refresh filtering", async () => {
const source = await readWorkspaceFile("src/components/app-runtime.tsx");
assert.match(source, /projectId\?: string/, "expected RealtimeRefresh to accept an optional projectId");
assert.match(source, /payload\.projectId === projectId/, "expected RealtimeRefresh to filter by matching projectId");
});
test("project conversation pages wire project-scoped realtime refresh", async () => {
const [projectPage, goalsPage, versionsPage, threadStatusPage] = await Promise.all([
readWorkspaceFile("src/app/conversations/[projectId]/page.tsx"),
readWorkspaceFile("src/app/conversations/[projectId]/goals/page.tsx"),
readWorkspaceFile("src/app/conversations/[projectId]/versions/page.tsx"),
readWorkspaceFile("src/app/conversations/[projectId]/thread-status/page.tsx"),
]);
assert.match(projectPage, /projectId=\{detail\.project\.id\}/, "expected project chat page to pass projectId into RealtimeRefresh");
for (const [label, source] of [
["goals", goalsPage],
["versions", versionsPage],
["thread-status", threadStatusPage],
] as const) {
assert.match(source, /<RealtimeRefresh/, `expected ${label} page to render RealtimeRefresh`);
assert.match(source, /projectId=\{projectId\}/, `expected ${label} page to scope refreshes to the current project`);
assert.match(source, /"conversation\.updated"/, `expected ${label} page to listen to conversation updates`);
}
});