Add migration token parameters to Jenkins deploy flows
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-30 10:48:58 +08:00
parent 816e674307
commit 1ccb8a710d
9 changed files with 128 additions and 32 deletions

View File

@@ -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

View File

@@ -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 <token>] [--migration-import-token <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 <path> 可选,自动迁移 JSON 输出目录,默认部署目录内 database-migrations/<database>
--migration-export-token <token> 可选,旧库已授权迁移操作员 token仅用于 schema 冲突导出
--migration-import-token <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

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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 {