feat: improve admin upgrade logs
This commit is contained in:
@@ -84,6 +84,7 @@ async function main() {
|
||||
message: '正在检查升级包与运行环境',
|
||||
startedAt: state.startedAt || new Date().toISOString(),
|
||||
});
|
||||
logStep('开始升级任务', `任务 ${jobId} 使用${mode === 'hot' ? '热更新' : '冷更新'}模式,升级包 ${packageName}`);
|
||||
|
||||
if (mode !== 'hot' && mode !== 'cold') {
|
||||
throw new Error('升级方式无效');
|
||||
@@ -95,6 +96,7 @@ async function main() {
|
||||
throw new Error('仅支持 .tar、.tar.gz、.tgz 升级包');
|
||||
}
|
||||
|
||||
logStep('校验升级包', '正在读取压缩包目录并检查格式');
|
||||
run('tar', tarReadArgs('list', packagePath), { cwd: projectRoot, label: '检查升级包结构' });
|
||||
|
||||
resetDir(extractDir);
|
||||
@@ -107,6 +109,7 @@ async function main() {
|
||||
}
|
||||
|
||||
const validation = validateFiles(files, mode);
|
||||
logStep('升级包内容', `校验通过,共 ${files.length} 个文件:${files.slice(0, 20).join('、')}${files.length > 20 ? ` 等 ${files.length} 个文件` : ''}`);
|
||||
updateState({
|
||||
step: 'validated',
|
||||
progress: 14,
|
||||
@@ -117,6 +120,7 @@ async function main() {
|
||||
});
|
||||
|
||||
updateState({ step: 'backup_data', progress: 22, message: '正在创建数据库、存储与环境配置备份' });
|
||||
logStep('创建数据备份', '开始备份数据库、存储目录和环境配置');
|
||||
const backupFile = runCapture('bash', ['./scripts/backup-create.sh'], {
|
||||
cwd: projectRoot,
|
||||
label: '创建数据备份',
|
||||
@@ -126,18 +130,25 @@ async function main() {
|
||||
throw new Error('数据备份创建失败');
|
||||
}
|
||||
updateState({ backupFile });
|
||||
logStep('数据备份完成', `备份文件:${backupFile}`);
|
||||
|
||||
updateState({ step: 'backup_source', progress: 30, message: '正在创建源码快照' });
|
||||
logStep('创建源码快照', '开始保存升级前源码状态');
|
||||
createSourceBackup(sourceBackupFile);
|
||||
updateState({ sourceBackupFile });
|
||||
logStep('源码快照完成', `快照文件:${sourceBackupFile}`);
|
||||
|
||||
updateState({ step: 'apply', progress: 42, message: '正在应用升级包文件' });
|
||||
logStep('应用升级文件', '开始覆盖升级包中的文件');
|
||||
updateState({ preExistingFiles: files.filter(file => fs.existsSync(path.join(projectRoot, file))) });
|
||||
applyFiles(payloadRoot, files);
|
||||
logStep('升级文件应用完成', `已应用 ${files.filter(file => file !== 'manifest.json').length} 个文件`);
|
||||
|
||||
if (mode === 'hot') {
|
||||
updateState({ step: 'verify_hot', progress: 70, message: '正在验证热更新文件' });
|
||||
logStep('热更新验证', '正在执行 TypeScript 校验,确认补丁不会破坏现有代码');
|
||||
run('pnpm', ['run', 'ts-check'], { cwd: projectRoot, label: 'TypeScript 校验' });
|
||||
logStep('热更新完成', '升级成功,平台未重启,前端业务不中断');
|
||||
updateState({
|
||||
status: 'succeeded',
|
||||
step: 'completed',
|
||||
@@ -152,20 +163,30 @@ async function main() {
|
||||
const dependencyChanged = files.some(file => file === 'package.json' || file === 'pnpm-lock.yaml');
|
||||
if (dependencyChanged) {
|
||||
updateState({ step: 'install', progress: 54, message: '依赖文件发生变化,正在安装依赖' });
|
||||
logStep('安装依赖', '检测到 package.json 或 pnpm-lock.yaml 变化,开始安装依赖');
|
||||
run('pnpm', ['install', '--frozen-lockfile', '--prod=false'], { cwd: projectRoot, label: '安装依赖' });
|
||||
logStep('依赖安装完成', '依赖安装已完成');
|
||||
}
|
||||
|
||||
updateState({ step: 'ts_check', progress: 64, message: '正在执行 TypeScript 校验' });
|
||||
logStep('代码校验', '开始执行 TypeScript 校验');
|
||||
run('pnpm', ['run', 'ts-check'], { cwd: projectRoot, label: 'TypeScript 校验' });
|
||||
logStep('代码校验完成', 'TypeScript 校验已通过');
|
||||
|
||||
updateState({ step: 'build', progress: 75, message: '正在构建平台' });
|
||||
logStep('平台构建', '开始构建生产版本');
|
||||
run('pnpm', ['run', 'build'], { cwd: projectRoot, label: '构建平台' });
|
||||
logStep('平台构建完成', '生产构建已完成');
|
||||
|
||||
updateState({ step: 'restart', progress: 86, message: '正在重启平台进程' });
|
||||
logStep('重启平台', '冷更新需要重启平台进程,重启后升级状态会从磁盘继续读取');
|
||||
restartPlatform();
|
||||
logStep('平台重启命令完成', '平台重启命令已执行,开始等待健康检查');
|
||||
|
||||
updateState({ step: 'health_check', progress: 94, message: '正在检查平台健康状态' });
|
||||
logStep('健康检查', '正在确认平台接口恢复正常');
|
||||
waitForHealth();
|
||||
logStep('冷更新完成', '平台已重启并通过健康检查');
|
||||
|
||||
updateState({
|
||||
status: 'succeeded',
|
||||
@@ -179,6 +200,7 @@ async function main() {
|
||||
|
||||
async function rollbackAfterFailure(message) {
|
||||
const originalError = message;
|
||||
logStep('升级失败', `失败原因:${originalError}`);
|
||||
updateState({
|
||||
status: 'rolling_back',
|
||||
step: 'rolling_back',
|
||||
@@ -188,27 +210,35 @@ async function rollbackAfterFailure(message) {
|
||||
});
|
||||
|
||||
if (fs.existsSync(sourceBackupFile)) {
|
||||
logStep('回滚源码', '正在恢复升级前源码快照,并移除升级中新建的文件');
|
||||
restoreSourceBackup(sourceBackupFile);
|
||||
logStep('源码回滚完成', '源码已恢复到升级开始前状态');
|
||||
}
|
||||
|
||||
if (state.backupFile && fs.existsSync(state.backupFile)) {
|
||||
logStep('回滚数据', '正在恢复数据库、存储目录和环境配置备份');
|
||||
run('bash', ['./scripts/backup-restore.sh', state.backupFile], {
|
||||
cwd: projectRoot,
|
||||
label: '恢复数据备份',
|
||||
env: { COZE_WORKSPACE_PATH: projectRoot },
|
||||
});
|
||||
logStep('数据回滚完成', '数据库、存储目录和环境配置已恢复');
|
||||
}
|
||||
|
||||
if (mode === 'cold') {
|
||||
try {
|
||||
logStep('回滚后重建', '冷更新失败后正在重新构建回滚版本');
|
||||
run('pnpm', ['run', 'build'], { cwd: projectRoot, label: '回滚后重新构建' });
|
||||
logStep('回滚后重启', '正在重启回滚后的平台版本');
|
||||
restartPlatform();
|
||||
waitForHealth();
|
||||
logStep('回滚后健康检查通过', '平台已恢复到升级前版本');
|
||||
} catch (error) {
|
||||
throw new Error(`回滚后平台恢复检查失败: ${error instanceof Error ? error.message : String(error)}`);
|
||||
}
|
||||
}
|
||||
|
||||
logStep('自动回滚完成', '升级失败,但已自动恢复到升级开始前状态');
|
||||
updateState({
|
||||
status: 'rolled_back',
|
||||
step: 'rolled_back',
|
||||
@@ -281,10 +311,14 @@ function updateState(patch) {
|
||||
|
||||
function log(line) {
|
||||
const timestamped = `[${new Date().toISOString()}] ${line}`;
|
||||
const logs = [...(state.logs || []), timestamped].slice(-300);
|
||||
const logs = [...(state.logs || []), timestamped].slice(-1000);
|
||||
updateState({ logs });
|
||||
}
|
||||
|
||||
function logStep(title, detail = '') {
|
||||
log(detail ? `${title}:${detail}` : title);
|
||||
}
|
||||
|
||||
function ensureDir(dir) {
|
||||
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
||||
}
|
||||
@@ -300,7 +334,7 @@ function run(command, commandArgs, options = {}) {
|
||||
|
||||
function runCapture(command, commandArgs, options = {}) {
|
||||
const label = options.label || command;
|
||||
log(`${label}: ${command} ${commandArgs.join(' ')}`);
|
||||
logStep(label, `执行命令 ${command} ${commandArgs.join(' ')}`);
|
||||
const result = spawnSync(command, commandArgs, {
|
||||
cwd: options.cwd || projectRoot,
|
||||
env: { ...process.env, COREPACK_HOME: process.env.COREPACK_HOME || '/tmp/corepack', ...(options.env || {}) },
|
||||
@@ -309,7 +343,7 @@ function runCapture(command, commandArgs, options = {}) {
|
||||
});
|
||||
const output = `${result.stdout || ''}${result.stderr || ''}`.trim();
|
||||
if (output) {
|
||||
for (const line of output.split(/\r?\n/).slice(-80)) log(`${label}: ${line}`);
|
||||
for (const line of output.split(/\r?\n/).slice(-180)) log(`${label}输出:${line}`);
|
||||
}
|
||||
if (result.status !== 0) {
|
||||
throw new Error(`${label}失败,退出码 ${result.status ?? 'unknown'}`);
|
||||
|
||||
Reference in New Issue
Block a user