071faa482c
纳入 AGC Cargo workspace 的统一 rustfmt 检查与格式化入口 完成项目 TypeScript/Prettier 与 Rust 全量格式化 修复 Pingora expected executable 门禁的空白敏感误报 同步开发运维文档与 AGC skill pack 格式化忽略规则
126 lines
3.8 KiB
JavaScript
126 lines
3.8 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import {
|
|
callSpacetimeProcedureViaCli,
|
|
ensureProcedureOk,
|
|
} from './spacetime-migration-common.mjs';
|
|
|
|
const ACTIONS = new Map([
|
|
['backfill', 'backfill_editor_canvas_layout_and_return'],
|
|
['activate', 'activate_editor_canvas_layout_and_return'],
|
|
['rollback', 'rollback_editor_canvas_layout_and_return'],
|
|
]);
|
|
|
|
function usage() {
|
|
return `用法:
|
|
node scripts/spacetime-migrate-editor-canvas-layout.mjs \\
|
|
--database <name> --project-id <projectId> --owner-user-id <userId> \\
|
|
--action <backfill|activate|rollback> [--server <name-or-url>] [--apply]
|
|
|
|
默认只执行 dry-run 校验,不修改数据;追加 --apply 才写入。
|
|
必须使用已授权 database migration operator 的 spacetime CLI 登录态,
|
|
并显式传入 --server 或设置 GENARRATIVE_SPACETIME_SERVER。
|
|
|
|
存量项目的安全顺序:
|
|
1. backfill dry-run
|
|
2. backfill --apply
|
|
3. activate dry-run
|
|
4. activate --apply
|
|
需要回滚时先执行 rollback dry-run,确认 hash / revision / 2 MiB 门禁通过后再 --apply。`;
|
|
}
|
|
|
|
function parseOptions(argv) {
|
|
const options = {
|
|
action: '',
|
|
apply: false,
|
|
database: process.env.GENARRATIVE_SPACETIME_DATABASE || '',
|
|
ownerUserId: '',
|
|
passthrough: [],
|
|
projectId: '',
|
|
server: process.env.GENARRATIVE_SPACETIME_SERVER || '',
|
|
serverUrl: process.env.GENARRATIVE_SPACETIME_SERVER_URL || '',
|
|
};
|
|
for (let index = 0; index < argv.length; index += 1) {
|
|
const arg = argv[index];
|
|
const readValue = () => {
|
|
const value = argv[index + 1];
|
|
if (!value || value.startsWith('--')) {
|
|
throw new Error(`${arg} 缺少参数值。`);
|
|
}
|
|
index += 1;
|
|
return value.trim();
|
|
};
|
|
if (arg === '--action') {
|
|
options.action = readValue();
|
|
} else if (arg === '--database') {
|
|
options.database = readValue();
|
|
} else if (arg === '--project-id') {
|
|
options.projectId = readValue();
|
|
} else if (arg === '--owner-user-id') {
|
|
options.ownerUserId = readValue();
|
|
} else if (arg === '--server') {
|
|
options.server = readValue();
|
|
} else if (arg === '--server-url') {
|
|
options.serverUrl = readValue();
|
|
} else if (arg === '--apply') {
|
|
options.apply = true;
|
|
} else if (arg === '--help' || arg === '-h') {
|
|
options.help = true;
|
|
} else if (arg === '--no-config' || arg === '--anonymous') {
|
|
options.passthrough.push(arg);
|
|
} else {
|
|
throw new Error(`未知参数: ${arg}`);
|
|
}
|
|
}
|
|
return options;
|
|
}
|
|
|
|
try {
|
|
const options = parseOptions(process.argv.slice(2));
|
|
if (options.help) {
|
|
console.log(usage());
|
|
process.exit(0);
|
|
}
|
|
if (!options.database || !options.projectId || !options.ownerUserId) {
|
|
throw new Error('--database、--project-id 和 --owner-user-id 均为必填。');
|
|
}
|
|
if (!options.server && !options.serverUrl) {
|
|
throw new Error(
|
|
'必须显式传入 --server / --server-url,不使用默认 cloud target。',
|
|
);
|
|
}
|
|
const procedureName = ACTIONS.get(options.action);
|
|
if (!procedureName) {
|
|
throw new Error('--action 只支持 backfill、activate 或 rollback。');
|
|
}
|
|
const result = await callSpacetimeProcedureViaCli(options, procedureName, {
|
|
project_id: options.projectId,
|
|
owner_user_id: options.ownerUserId,
|
|
updated_at_micros: Date.now() * 1_000,
|
|
dry_run: !options.apply,
|
|
});
|
|
ensureProcedureOk(result);
|
|
console.log(
|
|
JSON.stringify(
|
|
{
|
|
action: options.action,
|
|
applied: options.apply,
|
|
procedure: procedureName,
|
|
...result,
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
);
|
|
if (!options.apply) {
|
|
console.log('dry-run 已通过;确认输出后追加 --apply 重跑同一动作。');
|
|
}
|
|
} catch (error) {
|
|
console.error(
|
|
`[spacetime:editor-canvas-layout:migrate] ${
|
|
error instanceof Error ? error.message : String(error)
|
|
}`,
|
|
);
|
|
process.exit(1);
|
|
}
|