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> { export async function readTestServerDeploymentState(): Promise<TestServerDeploymentSnapshot | null> {
try { try {
const raw = await readFile(getTestServerDeploymentStatePath(), 'utf8'); const raw = await readFile(getTestServerDeploymentStatePath(), 'utf8');
return normalizeTestServerDeploymentSnapshot(JSON.parse(raw)); const snapshot = normalizeTestServerDeploymentSnapshot(JSON.parse(raw));
return await resolveStaleRunningTestDeployment(snapshot);
} catch { } catch {
return null; return null;
} }
@@ -209,6 +210,62 @@ async function clearTestServerDeploymentState() {
await rm(getTestServerDeploymentStatePath(), { force: true }).catch(() => undefined); 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() { async function acquireTestServerDeploymentLock() {
const lockPath = getTestServerDeploymentLockPath(); const lockPath = getTestServerDeploymentLockPath();
await mkdir(path.dirname(lockPath), { recursive: true }); await mkdir(path.dirname(lockPath), { recursive: true });

View File

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