[codex] add automatic update-center reminders (#333)

* add automatic update-center reminders

* fix update-center reminder review issues
This commit is contained in:
Cita
2026-03-30 22:37:24 +08:00
committed by GitHub
parent 26f9717ad7
commit 9eb33df6d4
21 changed files with 1009 additions and 176 deletions
@@ -200,6 +200,15 @@ describe('update center routes', () => {
},
],
},
runtime: {
lastCheckedAt: null,
lastCheckError: null,
lastResolvedSource: null,
lastResolvedDisplayVersion: null,
lastResolvedCandidateKey: null,
lastNotifiedCandidateKey: null,
lastNotifiedAt: null,
},
});
});
+6 -78
View File
@@ -4,17 +4,10 @@ import { config as runtimeConfig } from '../../config.js';
import {
appendBackgroundTaskLog,
getBackgroundTask,
listBackgroundTasks,
startBackgroundTask,
subscribeToBackgroundTaskLogs,
} from '../../services/backgroundTaskService.js';
import {
fetchLatestDockerHubTag,
fetchLatestStableGitHubRelease,
getCurrentRuntimeVersion,
type UpdateCenterVersionCandidate,
type UpdateCenterVersionSource,
} from '../../services/updateCenterVersionService.js';
import { type UpdateCenterVersionSource } from '../../services/updateCenterVersionService.js';
import {
getUpdateCenterDeployBlockMessage,
normalizeUpdateCenterTargetDigest,
@@ -27,10 +20,14 @@ import {
type UpdateCenterConfig,
} from '../../services/updateCenterConfigService.js';
import {
getUpdateCenterHelperStatus,
streamUpdateCenterDeploy,
streamUpdateCenterRollback,
} from '../../services/updateCenterHelperClient.js';
import { buildUpdateCenterStatus } from '../../services/updateCenterStatusService.js';
import {
UPDATE_CENTER_DEPLOY_DEDUPE_KEY,
UPDATE_CENTER_DEPLOY_TASK_TYPE,
} from '../../services/updateCenterTaskConstants.js';
type DeployBody = {
source?: UpdateCenterVersionSource;
@@ -43,9 +40,6 @@ type RollbackBody = {
targetRevision?: string;
};
const UPDATE_CENTER_DEPLOY_TASK_TYPE = 'update-center.deploy';
const UPDATE_CENTER_DEPLOY_DEDUPE_KEY = 'update-center.deploy';
function getUpdateCenterHelperToken(): string {
return String(
runtimeConfig.deployHelperToken
@@ -69,77 +63,11 @@ function assertDeployableConfig(config: UpdateCenterConfig) {
if (!config.imageRepository) throw new Error('imageRepository is required');
}
function getDeployTasks() {
return listBackgroundTasks(50).filter((task) => task.type === UPDATE_CENTER_DEPLOY_TASK_TYPE);
}
function summarizeHelperError(error: unknown) {
if (error instanceof Error && error.message) return error.message;
return String(error || 'unknown helper error');
}
async function settleOptional<T>(enabled: boolean, loader: () => Promise<T>): Promise<{
value: T | null;
error: string | null;
}> {
if (!enabled) {
return {
value: null,
error: null,
};
}
try {
return {
value: await loader(),
error: null,
};
} catch (error) {
return {
value: null,
error: summarizeHelperError(error),
};
}
}
async function buildUpdateCenterStatus() {
const config = await loadUpdateCenterConfig();
const helperToken = getUpdateCenterHelperToken();
const [githubLookup, dockerLookup, helperLookup] = await Promise.all([
settleOptional(config.githubReleasesEnabled, async () => await fetchLatestStableGitHubRelease()),
settleOptional(config.dockerHubTagsEnabled, async () => await fetchLatestDockerHubTag()),
settleOptional(!!config.helperBaseUrl, async () => {
if (!helperToken) {
throw new Error('DEPLOY_HELPER_TOKEN is required');
}
return await getUpdateCenterHelperStatus(config, helperToken);
}),
]);
const githubRelease = githubLookup.value as UpdateCenterVersionCandidate | null;
const dockerHubTag = dockerLookup.value as UpdateCenterVersionCandidate | null;
const helper: Record<string, unknown> = helperLookup.value || {
ok: false,
healthy: false,
error: helperLookup.error,
};
const tasks = getDeployTasks();
const runningTask = tasks.find((task) => task.status === 'pending' || task.status === 'running') || null;
const lastFinishedTask = tasks.find((task) => task.status === 'succeeded' || task.status === 'failed') || null;
return {
currentVersion: getCurrentRuntimeVersion(),
config,
githubRelease,
dockerHubTag,
helper,
runningTask,
lastFinishedTask,
};
}
function writeSseEvent(reply: { raw: NodeJS.WritableStream & { write: (chunk: string) => void } }, event: string, data: unknown) {
reply.raw.write(`event: ${event}\ndata: ${JSON.stringify(data)}\n\n`);
}