From 1ccb8a710d158f035353c8e5f3ac8525bde1e4b2 Mon Sep 17 00:00:00 2001 From: kdletters Date: Thu, 30 Apr 2026 10:48:58 +0800 Subject: [PATCH] Add migration token parameters to Jenkins deploy flows --- ..._RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md | 10 +++++-- ...N_STRING_MIGRATION_PROCEDURE_2026-04-27.md | 11 +++++++ jenkins/Jenkinsfile.build-and-deploy | 4 +++ jenkins/Jenkinsfile.deploy | 8 +++++ scripts/deploy-rust-remote.sh | 25 +++++++++++++--- scripts/jenkins-deploy-release.sh | 30 ++++++++++++++++++- ...spacetime-authorize-migration-operator.mjs | 16 ++++++---- scripts/spacetime-export-migration-json.mjs | 28 ++++++++++------- scripts/spacetime-import-migration-json.mjs | 28 ++++++++++------- 9 files changed, 128 insertions(+), 32 deletions(-) diff --git a/docs/technical/JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md b/docs/technical/JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md index 80fbee19..3cd8fa18 100644 --- a/docs/technical/JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md +++ b/docs/technical/JENKINS_RUST_BUILD_DEPLOY_PIPELINES_2026-04-23.md @@ -101,7 +101,7 @@ scripts/jenkins-deploy-release.sh \ 2. 覆盖前如果旧部署目录存在 `migration-bootstrap-secret.txt`,先复制到 `deploy-state/migration-bootstrap-secret.previous.txt`,供新版本 `start.sh` 在 schema 冲突自动迁移时授权导出旧库。该文件属于 Jenkins 部署状态,不放入 `run/`,避免 `sudo` 启停脚本生成的 root 私有运行目录阻断后续部署写入。 3. 只删除发布产物白名单中的旧文件,例如 `web/`、`api-server`、`spacetime_module.wasm`、`migration-bootstrap-secret.txt`、`scripts/`、`.env*`、`start.sh`、`stop.sh`、`web-server.mjs`、`README.md`。 4. 将指定版本目录中的同名发布产物复制到部署目录;文件产物使用普通复制,`web/`、`scripts/` 等目录产物必须递归复制。 -5. 把 `WEB_PORT`、`MIGRATE_ON_CONFLICT`、`MIGRATION_DIRECTORY` 写入部署目录 `.env.local`,确保通过 sudo 执行 `start.sh` 时仍能读取 Jenkins 参数;启动前读取 `.env` 与 `.env.local` 中最终的 `GENARRATIVE_SPACETIME_DATABASE`,打印并校验其符合 SpacetimeDB 数据库名规则。 +5. 把 `WEB_PORT`、`MIGRATE_ON_CONFLICT`、`MIGRATION_DIRECTORY` 写入部署目录 `.env.local`,确保通过 sudo 执行 `start.sh` 时仍能读取 Jenkins 参数;启动前读取 `.env` 与 `.env.local` 中最终的 `GENARRATIVE_SPACETIME_DATABASE`,打印并校验其符合 SpacetimeDB 数据库名规则。Jenkins 参数 `MIGRATION_EXPORT_TOKEN` / `MIGRATION_IMPORT_TOKEN` 会分别写入 `GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN` / `GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN`;如果参数为空,部署目录已有同名变量时会尽量保留。 6. 如果 `CLEAR_DATABASE=true`,部署脚本会以 `./start.sh --clear-database` 启动新版本;这样发布阶段的 `spacetime publish` 会追加 `-c=on-conflict`,代表人工确认清库,不进入自动导出和回灌。 7. 执行新版本 `start.sh`;普通发布遇到 schema 冲突时,默认由发布包内迁移脚本自动导出旧库、清库发布新 wasm、导入回灌。 @@ -148,6 +148,8 @@ jenkins/Jenkinsfile.build-and-deploy 6. `CLEAR_DATABASE`:部署阶段是否清空 SpacetimeDB 数据后再发布 wasm;默认 `false`。 7. `MIGRATE_ON_CONFLICT`:普通部署遇到 SpacetimeDB schema 冲突时是否自动导出、清库发布、导入回灌;默认 `true`。 8. `MIGRATION_DIRECTORY`:自动迁移 JSON 输出目录;留空时使用部署目录内 `database-migrations/`。 +9. `MIGRATION_EXPORT_TOKEN`:可选,旧库已授权迁移操作员 token,只在 schema 冲突导出旧库时使用。 +10. `MIGRATION_IMPORT_TOKEN`:可选,新库已授权迁移操作员 token,只在清库发布新 wasm 后导入回灌时使用。 如果当前 Jenkins 没有额外配置独立 Agent,而是直接在控制器自身执行任务,`AGENT_LABEL` 应填写 `built-in`。 如果 Jenkins 进程以默认 `jenkins` 用户运行,部署目录建议直接放在 `/var/lib/jenkins/deploy/Genarrative` 这类 Jenkins 自有目录下,避免再依赖 `/home/ubuntu/*` 的额外写权限。 @@ -164,6 +166,8 @@ jenkins/Jenkinsfile.build-and-deploy 7. `RUN_DEPLOY_HOOKS_WITH_SUDO` 8. `EXPECTED_UPSTREAM_JOB` 9. `WEB_PORT` +10. `MIGRATION_EXPORT_TOKEN` +11. `MIGRATION_IMPORT_TOKEN` 其中仅 `构建并部署` 流水线还需要: @@ -173,7 +177,9 @@ jenkins/Jenkinsfile.build-and-deploy 4. `CLEAR_DATABASE` 5. `MIGRATE_ON_CONFLICT` 6. `MIGRATION_DIRECTORY` -7. `DATABASE`:发布包默认数据库名,默认 `genarrative-pipeline-local-test`,必须匹配 `^[a-z0-9]+(-[a-z0-9]+)*$`。 +7. `MIGRATION_EXPORT_TOKEN` +8. `MIGRATION_IMPORT_TOKEN` +9. `DATABASE`:发布包默认数据库名,默认 `genarrative-pipeline-local-test`,必须匹配 `^[a-z0-9]+(-[a-z0-9]+)*$`。 如果你选择启用 `RUN_DEPLOY_HOOKS_WITH_SUDO=true`,推荐提前在服务器上增加一份最小 sudoers 配置,例如: diff --git a/docs/technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md b/docs/technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md index e23f7051..92bf4c85 100644 --- a/docs/technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md +++ b/docs/technical/SPACETIMEDB_JSON_STRING_MIGRATION_PROCEDURE_2026-04-27.md @@ -175,6 +175,15 @@ Jenkins 参数 `CLEAR_DATABASE=true` 或手工执行 `./start.sh --clear-databas - 导出旧库:优先使用 `deploy-state/migration-bootstrap-secret.previous.txt`,也就是旧模块编译时注入的密钥。 - 导入新库:使用当前发布包 `migration-bootstrap-secret.txt`,也就是新模块编译时注入的密钥。 +如果旧库或新库的 `database_migration_operator` 表已经不为空,bootstrap secret 不能再越权授权新的操作员;此时必须由已有迁移操作员发起授权,或在部署目录 `.env.local` 中配置已授权操作员的连接 token: + +```text +GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN=<旧库迁移操作员 token> +GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN=<新库迁移操作员 token> +``` + +`GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN` 只用于 schema 冲突时导出旧库;`GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN` 只用于清库发布新 wasm 后导入回灌。Jenkins 覆盖部署会尽量保留部署目录现有 `.env.local` 中的这两个 token,除非新发布包已经显式提供同名变量。 + 如果不是通过 Jenkins 部署脚本覆盖发布包,而是手工替换文件,必须在覆盖前保留旧 `migration-bootstrap-secret.txt`;否则旧库迁移 procedure 可能无法授权导出。 ### 删除表和删除字段 @@ -250,6 +259,8 @@ node scripts/spacetime-import-migration-json.mjs \ 如果你已经有可用的数据库连接 token,也可以显式传 `--token `。这种情况下脚本不会自动授权;该 token 对应的 identity 必须已经是迁移操作员。 +如果 `authorize_database_migration_operator` 返回 `当前 identity 未被授权执行数据库迁移`,说明当前机器 `spacetime` CLI 登录身份不是既有迁移操作员。表内已经存在操作员时,即使提供了正确 bootstrap secret,也不会允许非操作员继续扩权;需要先让既有操作员授权当前部署机 identity,或直接使用既有操作员 token 执行导出/导入。 + 正式导入前建议先加 `--dry-run`,确认 JSON 可解析、版本匹配、表名都在迁移白名单内。 `--dry-run` 不会模拟目标库主键或唯一约束冲突,因此增量模式的 `skipped_row_count` 只有真实导入时才准确。 diff --git a/jenkins/Jenkinsfile.build-and-deploy b/jenkins/Jenkinsfile.build-and-deploy index cc54b7e4..eab4e6e8 100644 --- a/jenkins/Jenkinsfile.build-and-deploy +++ b/jenkins/Jenkinsfile.build-and-deploy @@ -17,6 +17,8 @@ pipeline { booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm') booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌') string(name: 'MIGRATION_DIRECTORY', defaultValue: '', description: '自动迁移 JSON 输出目录,留空则使用部署目录内 database-migrations/') + password(name: 'MIGRATION_EXPORT_TOKEN', defaultValue: '', description: '可选,旧库已授权迁移操作员 token,仅部署阶段用于 schema 冲突导出') + password(name: 'MIGRATION_IMPORT_TOKEN', defaultValue: '', description: '可选,新库已授权迁移操作员 token,仅部署阶段用于 schema 冲突导入') booleanParam(name: 'RUN_NPM_CI', defaultValue: false, description: '构建前是否执行 npm ci') string(name: 'DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Deploy', description: '部署流水线作业名') string(name: 'DEPLOY_DIRECTORY', defaultValue: '/var/lib/jenkins/deploy/Genarrative', description: '固定部署目录') @@ -149,6 +151,8 @@ pipeline { booleanParam(name: 'CLEAR_DATABASE', value: params.CLEAR_DATABASE), booleanParam(name: 'MIGRATE_ON_CONFLICT', value: params.MIGRATE_ON_CONFLICT), string(name: 'MIGRATION_DIRECTORY', value: params.MIGRATION_DIRECTORY), + password(name: 'MIGRATION_EXPORT_TOKEN', value: params.MIGRATION_EXPORT_TOKEN), + password(name: 'MIGRATION_IMPORT_TOKEN', value: params.MIGRATION_IMPORT_TOKEN), booleanParam(name: 'RUN_DEPLOY_HOOKS_WITH_SUDO', value: params.RUN_DEPLOY_HOOKS_WITH_SUDO), string(name: 'EXPECTED_UPSTREAM_JOB', value: env.JOB_NAME), ] diff --git a/jenkins/Jenkinsfile.deploy b/jenkins/Jenkinsfile.deploy index 87293eee..6af8dffb 100644 --- a/jenkins/Jenkinsfile.deploy +++ b/jenkins/Jenkinsfile.deploy @@ -15,6 +15,8 @@ pipeline { booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm') booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌') string(name: 'MIGRATION_DIRECTORY', defaultValue: '', description: '自动迁移 JSON 输出目录,留空则使用部署目录内 database-migrations/') + password(name: 'MIGRATION_EXPORT_TOKEN', defaultValue: '', description: '可选,旧库已授权迁移操作员 token,仅用于 schema 冲突导出') + password(name: 'MIGRATION_IMPORT_TOKEN', defaultValue: '', description: '可选,新库已授权迁移操作员 token,仅用于 schema 冲突导入') booleanParam(name: 'RUN_DEPLOY_HOOKS_WITH_SUDO', defaultValue: true, description: 'start.sh / stop.sh 是否通过 sudo -n 执行') string(name: 'EXPECTED_UPSTREAM_JOB', defaultValue: '', description: '允许触发本作业的上游作业名') } @@ -127,6 +129,12 @@ pipeline { if [[ -n "${params.MIGRATION_DIRECTORY}" ]]; then deploy_args+=(--migration-dir "${params.MIGRATION_DIRECTORY}") fi + if [[ -n "${params.MIGRATION_EXPORT_TOKEN}" ]]; then + deploy_args+=(--migration-export-token "${params.MIGRATION_EXPORT_TOKEN}") + fi + if [[ -n "${params.MIGRATION_IMPORT_TOKEN}" ]]; then + deploy_args+=(--migration-import-token "${params.MIGRATION_IMPORT_TOKEN}") + fi if [[ "${params.RUN_DEPLOY_HOOKS_WITH_SUDO}" == "true" ]]; then deploy_args+=(--hook-with-sudo) fi diff --git a/scripts/deploy-rust-remote.sh b/scripts/deploy-rust-remote.sh index 278f9ffb..5f65e158 100644 --- a/scripts/deploy-rust-remote.sh +++ b/scripts/deploy-rust-remote.sh @@ -642,6 +642,8 @@ SPACETIME_DATABASE="${GENARRATIVE_SPACETIME_DATABASE:-__GENARRATIVE_DEFAULT_SPAC SPACETIME_TIMEOUT_SECONDS="${GENARRATIVE_SPACETIME_TIMEOUT_SECONDS:-60}" SPACETIME_MIGRATE_ON_CONFLICT="${GENARRATIVE_SPACETIME_MIGRATE_ON_CONFLICT:-true}" SPACETIME_MIGRATION_DIR="${GENARRATIVE_SPACETIME_MIGRATION_DIR:-}" +SPACETIME_MIGRATION_EXPORT_TOKEN="${GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN:-}" +SPACETIME_MIGRATION_IMPORT_TOKEN="${GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN:-}" API_HOST="${GENARRATIVE_API_HOST:-__GENARRATIVE_DEFAULT_API_HOST__}" API_PORT="${GENARRATIVE_API_PORT:-__GENARRATIVE_DEFAULT_API_PORT__}" API_LOG="${GENARRATIVE_API_LOG:-info,tower_http=info}" @@ -766,13 +768,28 @@ run_publish() { run_conflict_migration_publish() { local export_bootstrap_secret="" local import_bootstrap_secret="" + local export_auth_args=() + local import_auth_args=() local migration_database_slug="" local migration_root="" local migration_file="" local publish_log="" - export_bootstrap_secret="$(read_export_migration_bootstrap_secret)" - import_bootstrap_secret="$(read_import_migration_bootstrap_secret)" + if [[ -n "${SPACETIME_MIGRATION_EXPORT_TOKEN}" ]]; then + echo "[start] 使用 GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN 导出旧库" + export_auth_args=(--token "${SPACETIME_MIGRATION_EXPORT_TOKEN}") + else + export_bootstrap_secret="$(read_export_migration_bootstrap_secret)" + export_auth_args=(--bootstrap-secret "${export_bootstrap_secret}") + fi + + if [[ -n "${SPACETIME_MIGRATION_IMPORT_TOKEN}" ]]; then + echo "[start] 使用 GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN 导入新库" + import_auth_args=(--token "${SPACETIME_MIGRATION_IMPORT_TOKEN}") + else + import_bootstrap_secret="$(read_import_migration_bootstrap_secret)" + import_auth_args=(--bootstrap-secret "${import_bootstrap_secret}") + fi require_migration_script "${MIGRATION_EXPORT_SCRIPT}" require_migration_script "${MIGRATION_IMPORT_SCRIPT}" @@ -787,7 +804,7 @@ run_conflict_migration_publish() { --server-url "${SPACETIME_SERVER_URL}" \ --root-dir "${SPACETIME_ROOT_DIR}" \ --database "${SPACETIME_DATABASE}" \ - --bootstrap-secret "${export_bootstrap_secret}" \ + "${export_auth_args[@]}" \ --out "${migration_file}" \ --note "deploy conflict export $(date -u +%Y-%m-%dT%H:%M:%SZ)" @@ -812,7 +829,7 @@ run_conflict_migration_publish() { --server-url "${SPACETIME_SERVER_URL}" \ --root-dir "${SPACETIME_ROOT_DIR}" \ --database "${SPACETIME_DATABASE}" \ - --bootstrap-secret "${import_bootstrap_secret}" \ + "${import_auth_args[@]}" \ --in "${migration_file}" \ --replace-existing \ --note "deploy conflict import $(date -u +%Y-%m-%dT%H:%M:%SZ)"; then diff --git a/scripts/jenkins-deploy-release.sh b/scripts/jenkins-deploy-release.sh index cd49cad6..3e3155c4 100644 --- a/scripts/jenkins-deploy-release.sh +++ b/scripts/jenkins-deploy-release.sh @@ -5,7 +5,7 @@ set -euo pipefail usage() { cat <<'EOF' 用法: - ./scripts/jenkins-deploy-release.sh --source-dir /path/to/build/123 --deploy-dir /var/lib/jenkins/deploy/Genarrative --web-port 25001 [--clear-database] [--no-migrate-on-conflict] [--migration-dir /path/to/migrations] [--hook-with-sudo] + ./scripts/jenkins-deploy-release.sh --source-dir /path/to/build/123 --deploy-dir /var/lib/jenkins/deploy/Genarrative --web-port 25001 [--clear-database] [--no-migrate-on-conflict] [--migration-dir /path/to/migrations] [--migration-export-token ] [--migration-import-token ] [--hook-with-sudo] 说明: 1. 如果部署目录已有旧版本且存在 stop.sh,则先执行旧版本 stop.sh。 @@ -23,6 +23,8 @@ usage() { --migrate-on-conflict 可选,普通发布遇到 schema 冲突时自动迁移,默认启用 --no-migrate-on-conflict 可选,禁用 schema 冲突自动迁移 --migration-dir 可选,自动迁移 JSON 输出目录,默认部署目录内 database-migrations/ + --migration-export-token 可选,旧库已授权迁移操作员 token,仅用于 schema 冲突导出 + --migration-import-token 可选,新库已授权迁移操作员 token,仅用于 schema 冲突导入 --hook-with-sudo 可选,仅对 start.sh/stop.sh 使用 sudo -n 执行 EOF } @@ -173,6 +175,10 @@ CLEAR_DATABASE="0" MIGRATE_ON_CONFLICT="true" MIGRATION_DIR="" HOOK_WITH_SUDO="0" +MIGRATION_EXPORT_TOKEN="" +MIGRATION_IMPORT_TOKEN="" +PRESERVED_MIGRATION_EXPORT_TOKEN="" +PRESERVED_MIGRATION_IMPORT_TOKEN="" DEPLOY_ITEMS=( ".env" ".env.local" @@ -222,6 +228,14 @@ while [[ $# -gt 0 ]]; do MIGRATION_DIR="${2:?缺少 --migration-dir 的值}" shift 2 ;; + --migration-export-token) + MIGRATION_EXPORT_TOKEN="${2:?缺少 --migration-export-token 的值}" + shift 2 + ;; + --migration-import-token) + MIGRATION_IMPORT_TOKEN="${2:?缺少 --migration-import-token 的值}" + shift 2 + ;; --hook-with-sudo) HOOK_WITH_SUDO="1" shift @@ -348,6 +362,8 @@ if [[ ! -f "${SOURCE_DIR}/start.sh" ]]; then fi normalize_release_env_files "${SOURCE_DIR}" +PRESERVED_MIGRATION_EXPORT_TOKEN="$(read_env_value "GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN" "${DEPLOY_DIR}/.env" "${DEPLOY_DIR}/.env.local")" +PRESERVED_MIGRATION_IMPORT_TOKEN="$(read_env_value "GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN" "${DEPLOY_DIR}/.env" "${DEPLOY_DIR}/.env.local")" if [[ -x "${DEPLOY_DIR}/stop.sh" ]]; then echo "[jenkins-deploy] 先停止旧版本: ${DEPLOY_DIR}" @@ -396,6 +412,18 @@ normalize_release_env_files "${DEPLOY_DIR}" write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_WEB_PORT" "${WEB_PORT}" write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATE_ON_CONFLICT" "${MIGRATE_ON_CONFLICT}" write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATION_DIR" "${MIGRATION_DIR}" +if [[ -n "${MIGRATION_EXPORT_TOKEN}" ]]; then + write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN" "${MIGRATION_EXPORT_TOKEN}" +elif [[ -n "${PRESERVED_MIGRATION_EXPORT_TOKEN}" ]] \ + && [[ -z "$(read_env_value "GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN" "${DEPLOY_DIR}/.env" "${DEPLOY_DIR}/.env.local")" ]]; then + write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATION_EXPORT_TOKEN" "${PRESERVED_MIGRATION_EXPORT_TOKEN}" +fi +if [[ -n "${MIGRATION_IMPORT_TOKEN}" ]]; then + write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN" "${MIGRATION_IMPORT_TOKEN}" +elif [[ -n "${PRESERVED_MIGRATION_IMPORT_TOKEN}" ]] \ + && [[ -z "$(read_env_value "GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN" "${DEPLOY_DIR}/.env" "${DEPLOY_DIR}/.env.local")" ]]; then + write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATION_IMPORT_TOKEN" "${PRESERVED_MIGRATION_IMPORT_TOKEN}" +fi DEPLOY_DATABASE="$(read_env_value "GENARRATIVE_SPACETIME_DATABASE" "${DEPLOY_DIR}/.env" "${DEPLOY_DIR}/.env.local")" if [[ -z "${DEPLOY_DATABASE}" ]]; then diff --git a/scripts/spacetime-authorize-migration-operator.mjs b/scripts/spacetime-authorize-migration-operator.mjs index 82a074c5..99db8436 100644 --- a/scripts/spacetime-authorize-migration-operator.mjs +++ b/scripts/spacetime-authorize-migration-operator.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node import { + callSpacetimeProcedure, callSpacetimeProcedureViaCli, ensureProcedureOk, parseArgs, @@ -17,11 +18,16 @@ try { operator_identity_hex: options.operatorIdentity, note: options.note || '', }; - const result = await callSpacetimeProcedureViaCli( - options, - 'authorize_database_migration_operator', - input, - ); + if (options.useHttp && !options.token) { + throw new Error('--use-http 需要同时传入 --token。'); + } + const result = options.useHttp + ? await callSpacetimeProcedure(options, 'authorize_database_migration_operator', input) + : await callSpacetimeProcedureViaCli( + options, + 'authorize_database_migration_operator', + input, + ); ensureProcedureOk(result); console.log( diff --git a/scripts/spacetime-export-migration-json.mjs b/scripts/spacetime-export-migration-json.mjs index 3221c85c..42158657 100644 --- a/scripts/spacetime-export-migration-json.mjs +++ b/scripts/spacetime-export-migration-json.mjs @@ -57,16 +57,24 @@ async function prepareWebExportOptions(options) { `[spacetime:migration:export] 已通过 Web API 创建临时 identity: ${identity.identity}`, ); - const authorizeResult = await callSpacetimeProcedureViaCli( - options, - 'authorize_database_migration_operator', - { - bootstrap_secret: options.bootstrapSecret || '', - operator_identity_hex: identity.identity, - note: options.note || 'temporary web api migration export', - }, - ); - ensureProcedureOk(authorizeResult); + try { + const authorizeResult = await callSpacetimeProcedureViaCli( + options, + 'authorize_database_migration_operator', + { + bootstrap_secret: options.bootstrapSecret || '', + operator_identity_hex: identity.identity, + note: options.note || 'temporary web api migration export', + }, + ); + ensureProcedureOk(authorizeResult); + } catch (error) { + throw new Error( + `授权临时 Web API identity 失败。当前 spacetime CLI identity 必须已经是迁移操作员;如果旧库迁移操作员表不为空,bootstrap secret 不会越权授权新的操作员。可先用已有迁移操作员授权当前部署机 identity,或为导出脚本提供已有迁移操作员的 --token。原始错误: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } console.log(`[spacetime:migration:export] 已授权临时 Web API identity`); return { diff --git a/scripts/spacetime-import-migration-json.mjs b/scripts/spacetime-import-migration-json.mjs index 448308bc..0f14c12e 100644 --- a/scripts/spacetime-import-migration-json.mjs +++ b/scripts/spacetime-import-migration-json.mjs @@ -58,16 +58,24 @@ async function prepareWebImportOptions(options) { `[spacetime:migration:import] 已通过 Web API 创建临时 identity: ${identity.identity}`, ); - const authorizeResult = await callSpacetimeProcedureViaCli( - options, - 'authorize_database_migration_operator', - { - bootstrap_secret: options.bootstrapSecret || '', - operator_identity_hex: identity.identity, - note: options.note || 'temporary web api migration import', - }, - ); - ensureProcedureOk(authorizeResult); + try { + const authorizeResult = await callSpacetimeProcedureViaCli( + options, + 'authorize_database_migration_operator', + { + bootstrap_secret: options.bootstrapSecret || '', + operator_identity_hex: identity.identity, + note: options.note || 'temporary web api migration import', + }, + ); + ensureProcedureOk(authorizeResult); + } catch (error) { + throw new Error( + `授权临时 Web API identity 失败。当前 spacetime CLI identity 必须已经是迁移操作员;如果目标库迁移操作员表不为空,bootstrap secret 不会越权授权新的操作员。可先用已有迁移操作员授权当前部署机 identity,或为导入脚本提供已有迁移操作员的 --token。原始错误: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } console.log(`[spacetime:migration:import] 已授权临时 Web API identity`); return {