chore: test deploy snapshot

This commit is contained in:
2026-05-27 11:35:26 +09:00
parent 7e9c3bd097
commit 4a88d3f430
2 changed files with 87 additions and 12 deletions

View File

@@ -193,7 +193,8 @@ function normalizeTestServerDeploymentSnapshot(value: unknown): TestServerDeploy
export async function readTestServerDeploymentState(): Promise<TestServerDeploymentSnapshot | null> {
try {
const raw = await readFile(getTestServerDeploymentStatePath(), 'utf8');
return normalizeTestServerDeploymentSnapshot(JSON.parse(raw));
const snapshot = normalizeTestServerDeploymentSnapshot(JSON.parse(raw));
return await resolveStaleRunningTestDeployment(snapshot);
} catch {
return null;
}
@@ -209,6 +210,62 @@ async function clearTestServerDeploymentState() {
await rm(getTestServerDeploymentStatePath(), { force: true }).catch(() => undefined);
}
function buildStaleTestServerDeploymentFailure(snapshot: TestServerDeploymentSnapshot) {
const stalledAt = snapshot.updatedAt ?? snapshot.startedAt;
const stalledLabel = stalledAt ? `마지막 상태 갱신 ${stalledAt}` : '상태 갱신 시각 확인 불가';
return trimPreview(`TEST 배포 상태가 오래 갱신되지 않았고 잠금 파일도 없어 중단된 배포로 처리했습니다. ${stalledLabel}`, 500)
?? 'TEST 배포 상태가 오래 갱신되지 않아 중단된 배포로 처리했습니다.';
}
async function finalizeStaleRunningTestDeployment(snapshot: TestServerDeploymentSnapshot) {
const failureMessage = buildStaleTestServerDeploymentFailure(snapshot);
const now = new Date().toISOString();
const activeStep = snapshot.steps.find((step) => step.status === 'running')?.key;
snapshot.status = 'failed';
snapshot.phase = 'failed';
snapshot.summary = buildTestServerDeploymentSummary('failed');
snapshot.completedAt = now;
snapshot.updatedAt = now;
snapshot.lastError = failureMessage;
if (activeStep) {
updateTestServerDeploymentStep(snapshot, activeStep, 'failed', failureMessage);
}
await writeTestServerDeploymentState(snapshot);
return snapshot;
}
async function resolveStaleRunningTestDeployment(snapshot: TestServerDeploymentSnapshot) {
if (snapshot.status !== 'running') {
return snapshot;
}
const freshnessSource = snapshot.updatedAt ?? snapshot.startedAt;
if (!freshnessSource) {
return snapshot;
}
const staleForMs = Date.now() - Date.parse(freshnessSource);
if (!Number.isFinite(staleForMs) || staleForMs < TEST_SERVER_DEPLOYMENT_LOCK_STALE_MS) {
return snapshot;
}
const lockPath = getTestServerDeploymentLockPath();
const lockStat = await stat(lockPath).catch(() => null);
if (lockStat?.isFile()) {
const lockFreshnessSource = normalizeDateTimeValue(lockStat.mtime.toISOString() ?? null);
if (lockFreshnessSource && Date.now() - Date.parse(lockFreshnessSource) < TEST_SERVER_DEPLOYMENT_LOCK_STALE_MS) {
return snapshot;
}
}
await rm(lockPath, { force: true }).catch(() => undefined);
return finalizeStaleRunningTestDeployment(snapshot);
}
async function acquireTestServerDeploymentLock() {
const lockPath = getTestServerDeploymentLockPath();
await mkdir(path.dirname(lockPath), { recursive: true });

View File

@@ -3552,6 +3552,17 @@ export function ChatSharePage() {
const nextCustomContextContent = editingRoomCustomContextContent.trim();
const shouldPersistRoomDefaultContextIds = !areStringListsEqual(normalizedDefaultContextIds, inheritedDefaultContextIds);
const shouldPersistRoomCustomContext = Boolean(nextCustomContextTitle || nextCustomContextContent);
const currentRoomDefaultContextIds = activeRoomContextSettings?.defaultContextIds ?? [];
const currentRoomCustomContextTitle = activeRoomContextSettings?.customContextTitle?.trim() ?? '';
const currentRoomCustomContextContent = activeRoomContextSettings?.customContextContent?.trim() ?? '';
const currentRoomCodexParticipants = activeRoomContextSettings?.codexParticipants ?? [];
const shouldSaveRoomContextSettings =
canManageSharedRoomSettings
&& (
!areStringListsEqual(normalizedDefaultContextIds, currentRoomDefaultContextIds)
|| nextCustomContextTitle !== currentRoomCustomContextTitle
|| nextCustomContextContent !== currentRoomCustomContextContent
);
const normalizedRoomTitle = editingRoomTitle.trim();
const normalizedAccessPin = editingRoomAccessPin.trim();
const currentHasAccessPin = snapshot?.share.hasAccessPin === true;
@@ -3596,23 +3607,26 @@ export function ChatSharePage() {
await ensureRoomNotificationRegistration();
}
if (canManageSharedRoomSettings && nextChatType) {
const nextRoomContexts =
shouldPersistRoomDefaultContextIds || shouldPersistRoomCustomContext
? upsertChatRoomContextSettings(roomContexts, {
sessionId: snapshot.conversation.sessionId,
defaultContextIds: normalizedDefaultContextIds,
customContextTitle: nextCustomContextTitle,
customContextContent: nextCustomContextContent,
})
: roomContexts.filter((item) => item.sessionId !== snapshot.conversation.sessionId);
if (shouldSaveRoomContextSettings) {
const shouldKeepRoomContextRecord =
shouldPersistRoomDefaultContextIds
|| shouldPersistRoomCustomContext
|| currentRoomCodexParticipants.length > 0;
const nextRoomContexts = shouldKeepRoomContextRecord
? upsertChatRoomContextSettings(roomContexts, {
sessionId: snapshot.conversation.sessionId,
defaultContextIds: normalizedDefaultContextIds,
customContextTitle: nextCustomContextTitle,
customContextContent: nextCustomContextContent,
codexParticipants: currentRoomCodexParticipants,
})
: roomContexts.filter((item) => item.sessionId !== snapshot.conversation.sessionId);
await setChatContextSettingsStore({
defaultContexts,
chatTypeDefaults,
roomContexts: nextRoomContexts,
});
}
const shouldSaveAccessPinSettings =
@@ -3703,6 +3717,10 @@ export function ChatSharePage() {
message,
normalizedToken,
roomNotificationClientStatus.tone,
activeRoomContextSettings?.codexParticipants,
activeRoomContextSettings?.customContextContent,
activeRoomContextSettings?.customContextTitle,
activeRoomContextSettings?.defaultContextIds,
roomContexts,
setChatContextSettingsStore,
snapshot?.conversation.notifyOffline,