Add migration token parameters to Jenkins deploy flows
Some checks failed
CI / verify (push) Has been cancelled
Some checks failed
CI / verify (push) Has been cancelled
This commit is contained in:
@@ -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 私有运行目录阻断后续部署写入。
|
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`。
|
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/` 等目录产物必须递归复制。
|
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`,代表人工确认清库,不进入自动导出和回灌。
|
6. 如果 `CLEAR_DATABASE=true`,部署脚本会以 `./start.sh --clear-database` 启动新版本;这样发布阶段的 `spacetime publish` 会追加 `-c=on-conflict`,代表人工确认清库,不进入自动导出和回灌。
|
||||||
7. 执行新版本 `start.sh`;普通发布遇到 schema 冲突时,默认由发布包内迁移脚本自动导出旧库、清库发布新 wasm、导入回灌。
|
7. 执行新版本 `start.sh`;普通发布遇到 schema 冲突时,默认由发布包内迁移脚本自动导出旧库、清库发布新 wasm、导入回灌。
|
||||||
|
|
||||||
@@ -148,6 +148,8 @@ jenkins/Jenkinsfile.build-and-deploy
|
|||||||
6. `CLEAR_DATABASE`:部署阶段是否清空 SpacetimeDB 数据后再发布 wasm;默认 `false`。
|
6. `CLEAR_DATABASE`:部署阶段是否清空 SpacetimeDB 数据后再发布 wasm;默认 `false`。
|
||||||
7. `MIGRATE_ON_CONFLICT`:普通部署遇到 SpacetimeDB schema 冲突时是否自动导出、清库发布、导入回灌;默认 `true`。
|
7. `MIGRATE_ON_CONFLICT`:普通部署遇到 SpacetimeDB schema 冲突时是否自动导出、清库发布、导入回灌;默认 `true`。
|
||||||
8. `MIGRATION_DIRECTORY`:自动迁移 JSON 输出目录;留空时使用部署目录内 `database-migrations/<database>`。
|
8. `MIGRATION_DIRECTORY`:自动迁移 JSON 输出目录;留空时使用部署目录内 `database-migrations/<database>`。
|
||||||
|
9. `MIGRATION_EXPORT_TOKEN`:可选,旧库已授权迁移操作员 token,只在 schema 冲突导出旧库时使用。
|
||||||
|
10. `MIGRATION_IMPORT_TOKEN`:可选,新库已授权迁移操作员 token,只在清库发布新 wasm 后导入回灌时使用。
|
||||||
|
|
||||||
如果当前 Jenkins 没有额外配置独立 Agent,而是直接在控制器自身执行任务,`AGENT_LABEL` 应填写 `built-in`。
|
如果当前 Jenkins 没有额外配置独立 Agent,而是直接在控制器自身执行任务,`AGENT_LABEL` 应填写 `built-in`。
|
||||||
如果 Jenkins 进程以默认 `jenkins` 用户运行,部署目录建议直接放在 `/var/lib/jenkins/deploy/Genarrative` 这类 Jenkins 自有目录下,避免再依赖 `/home/ubuntu/*` 的额外写权限。
|
如果 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`
|
7. `RUN_DEPLOY_HOOKS_WITH_SUDO`
|
||||||
8. `EXPECTED_UPSTREAM_JOB`
|
8. `EXPECTED_UPSTREAM_JOB`
|
||||||
9. `WEB_PORT`
|
9. `WEB_PORT`
|
||||||
|
10. `MIGRATION_EXPORT_TOKEN`
|
||||||
|
11. `MIGRATION_IMPORT_TOKEN`
|
||||||
|
|
||||||
其中仅 `构建并部署` 流水线还需要:
|
其中仅 `构建并部署` 流水线还需要:
|
||||||
|
|
||||||
@@ -173,7 +177,9 @@ jenkins/Jenkinsfile.build-and-deploy
|
|||||||
4. `CLEAR_DATABASE`
|
4. `CLEAR_DATABASE`
|
||||||
5. `MIGRATE_ON_CONFLICT`
|
5. `MIGRATE_ON_CONFLICT`
|
||||||
6. `MIGRATION_DIRECTORY`
|
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 配置,例如:
|
如果你选择启用 `RUN_DEPLOY_HOOKS_WITH_SUDO=true`,推荐提前在服务器上增加一份最小 sudoers 配置,例如:
|
||||||
|
|
||||||
|
|||||||
@@ -175,6 +175,15 @@ Jenkins 参数 `CLEAR_DATABASE=true` 或手工执行 `./start.sh --clear-databas
|
|||||||
- 导出旧库:优先使用 `deploy-state/migration-bootstrap-secret.previous.txt`,也就是旧模块编译时注入的密钥。
|
- 导出旧库:优先使用 `deploy-state/migration-bootstrap-secret.previous.txt`,也就是旧模块编译时注入的密钥。
|
||||||
- 导入新库:使用当前发布包 `migration-bootstrap-secret.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 可能无法授权导出。
|
如果不是通过 Jenkins 部署脚本覆盖发布包,而是手工替换文件,必须在覆盖前保留旧 `migration-bootstrap-secret.txt`;否则旧库迁移 procedure 可能无法授权导出。
|
||||||
|
|
||||||
### 删除表和删除字段
|
### 删除表和删除字段
|
||||||
@@ -250,6 +259,8 @@ node scripts/spacetime-import-migration-json.mjs \
|
|||||||
|
|
||||||
如果你已经有可用的数据库连接 token,也可以显式传 `--token <web-api-token>`。这种情况下脚本不会自动授权;该 token 对应的 identity 必须已经是迁移操作员。
|
如果你已经有可用的数据库连接 token,也可以显式传 `--token <web-api-token>`。这种情况下脚本不会自动授权;该 token 对应的 identity 必须已经是迁移操作员。
|
||||||
|
|
||||||
|
如果 `authorize_database_migration_operator` 返回 `当前 identity 未被授权执行数据库迁移`,说明当前机器 `spacetime` CLI 登录身份不是既有迁移操作员。表内已经存在操作员时,即使提供了正确 bootstrap secret,也不会允许非操作员继续扩权;需要先让既有操作员授权当前部署机 identity,或直接使用既有操作员 token 执行导出/导入。
|
||||||
|
|
||||||
正式导入前建议先加 `--dry-run`,确认 JSON 可解析、版本匹配、表名都在迁移白名单内。
|
正式导入前建议先加 `--dry-run`,确认 JSON 可解析、版本匹配、表名都在迁移白名单内。
|
||||||
|
|
||||||
`--dry-run` 不会模拟目标库主键或唯一约束冲突,因此增量模式的 `skipped_row_count` 只有真实导入时才准确。
|
`--dry-run` 不会模拟目标库主键或唯一约束冲突,因此增量模式的 `skipped_row_count` 只有真实导入时才准确。
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ pipeline {
|
|||||||
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
|
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
|
||||||
booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌')
|
booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌')
|
||||||
string(name: 'MIGRATION_DIRECTORY', defaultValue: '', description: '自动迁移 JSON 输出目录,留空则使用部署目录内 database-migrations/<database>')
|
string(name: 'MIGRATION_DIRECTORY', defaultValue: '', description: '自动迁移 JSON 输出目录,留空则使用部署目录内 database-migrations/<database>')
|
||||||
|
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')
|
booleanParam(name: 'RUN_NPM_CI', defaultValue: false, description: '构建前是否执行 npm ci')
|
||||||
string(name: 'DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Deploy', description: '部署流水线作业名')
|
string(name: 'DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Deploy', description: '部署流水线作业名')
|
||||||
string(name: 'DEPLOY_DIRECTORY', defaultValue: '/var/lib/jenkins/deploy/Genarrative', 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: 'CLEAR_DATABASE', value: params.CLEAR_DATABASE),
|
||||||
booleanParam(name: 'MIGRATE_ON_CONFLICT', value: params.MIGRATE_ON_CONFLICT),
|
booleanParam(name: 'MIGRATE_ON_CONFLICT', value: params.MIGRATE_ON_CONFLICT),
|
||||||
string(name: 'MIGRATION_DIRECTORY', value: params.MIGRATION_DIRECTORY),
|
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),
|
booleanParam(name: 'RUN_DEPLOY_HOOKS_WITH_SUDO', value: params.RUN_DEPLOY_HOOKS_WITH_SUDO),
|
||||||
string(name: 'EXPECTED_UPSTREAM_JOB', value: env.JOB_NAME),
|
string(name: 'EXPECTED_UPSTREAM_JOB', value: env.JOB_NAME),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -15,6 +15,8 @@ pipeline {
|
|||||||
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
|
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
|
||||||
booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌')
|
booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌')
|
||||||
string(name: 'MIGRATION_DIRECTORY', defaultValue: '', description: '自动迁移 JSON 输出目录,留空则使用部署目录内 database-migrations/<database>')
|
string(name: 'MIGRATION_DIRECTORY', defaultValue: '', description: '自动迁移 JSON 输出目录,留空则使用部署目录内 database-migrations/<database>')
|
||||||
|
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 执行')
|
booleanParam(name: 'RUN_DEPLOY_HOOKS_WITH_SUDO', defaultValue: true, description: 'start.sh / stop.sh 是否通过 sudo -n 执行')
|
||||||
string(name: 'EXPECTED_UPSTREAM_JOB', defaultValue: '', description: '允许触发本作业的上游作业名')
|
string(name: 'EXPECTED_UPSTREAM_JOB', defaultValue: '', description: '允许触发本作业的上游作业名')
|
||||||
}
|
}
|
||||||
@@ -127,6 +129,12 @@ pipeline {
|
|||||||
if [[ -n "${params.MIGRATION_DIRECTORY}" ]]; then
|
if [[ -n "${params.MIGRATION_DIRECTORY}" ]]; then
|
||||||
deploy_args+=(--migration-dir "${params.MIGRATION_DIRECTORY}")
|
deploy_args+=(--migration-dir "${params.MIGRATION_DIRECTORY}")
|
||||||
fi
|
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
|
if [[ "${params.RUN_DEPLOY_HOOKS_WITH_SUDO}" == "true" ]]; then
|
||||||
deploy_args+=(--hook-with-sudo)
|
deploy_args+=(--hook-with-sudo)
|
||||||
fi
|
fi
|
||||||
|
|||||||
@@ -642,6 +642,8 @@ SPACETIME_DATABASE="${GENARRATIVE_SPACETIME_DATABASE:-__GENARRATIVE_DEFAULT_SPAC
|
|||||||
SPACETIME_TIMEOUT_SECONDS="${GENARRATIVE_SPACETIME_TIMEOUT_SECONDS:-60}"
|
SPACETIME_TIMEOUT_SECONDS="${GENARRATIVE_SPACETIME_TIMEOUT_SECONDS:-60}"
|
||||||
SPACETIME_MIGRATE_ON_CONFLICT="${GENARRATIVE_SPACETIME_MIGRATE_ON_CONFLICT:-true}"
|
SPACETIME_MIGRATE_ON_CONFLICT="${GENARRATIVE_SPACETIME_MIGRATE_ON_CONFLICT:-true}"
|
||||||
SPACETIME_MIGRATION_DIR="${GENARRATIVE_SPACETIME_MIGRATION_DIR:-}"
|
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_HOST="${GENARRATIVE_API_HOST:-__GENARRATIVE_DEFAULT_API_HOST__}"
|
||||||
API_PORT="${GENARRATIVE_API_PORT:-__GENARRATIVE_DEFAULT_API_PORT__}"
|
API_PORT="${GENARRATIVE_API_PORT:-__GENARRATIVE_DEFAULT_API_PORT__}"
|
||||||
API_LOG="${GENARRATIVE_API_LOG:-info,tower_http=info}"
|
API_LOG="${GENARRATIVE_API_LOG:-info,tower_http=info}"
|
||||||
@@ -766,13 +768,28 @@ run_publish() {
|
|||||||
run_conflict_migration_publish() {
|
run_conflict_migration_publish() {
|
||||||
local export_bootstrap_secret=""
|
local export_bootstrap_secret=""
|
||||||
local import_bootstrap_secret=""
|
local import_bootstrap_secret=""
|
||||||
|
local export_auth_args=()
|
||||||
|
local import_auth_args=()
|
||||||
local migration_database_slug=""
|
local migration_database_slug=""
|
||||||
local migration_root=""
|
local migration_root=""
|
||||||
local migration_file=""
|
local migration_file=""
|
||||||
local publish_log=""
|
local publish_log=""
|
||||||
|
|
||||||
export_bootstrap_secret="$(read_export_migration_bootstrap_secret)"
|
if [[ -n "${SPACETIME_MIGRATION_EXPORT_TOKEN}" ]]; then
|
||||||
import_bootstrap_secret="$(read_import_migration_bootstrap_secret)"
|
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_EXPORT_SCRIPT}"
|
||||||
require_migration_script "${MIGRATION_IMPORT_SCRIPT}"
|
require_migration_script "${MIGRATION_IMPORT_SCRIPT}"
|
||||||
|
|
||||||
@@ -787,7 +804,7 @@ run_conflict_migration_publish() {
|
|||||||
--server-url "${SPACETIME_SERVER_URL}" \
|
--server-url "${SPACETIME_SERVER_URL}" \
|
||||||
--root-dir "${SPACETIME_ROOT_DIR}" \
|
--root-dir "${SPACETIME_ROOT_DIR}" \
|
||||||
--database "${SPACETIME_DATABASE}" \
|
--database "${SPACETIME_DATABASE}" \
|
||||||
--bootstrap-secret "${export_bootstrap_secret}" \
|
"${export_auth_args[@]}" \
|
||||||
--out "${migration_file}" \
|
--out "${migration_file}" \
|
||||||
--note "deploy conflict export $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
--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}" \
|
--server-url "${SPACETIME_SERVER_URL}" \
|
||||||
--root-dir "${SPACETIME_ROOT_DIR}" \
|
--root-dir "${SPACETIME_ROOT_DIR}" \
|
||||||
--database "${SPACETIME_DATABASE}" \
|
--database "${SPACETIME_DATABASE}" \
|
||||||
--bootstrap-secret "${import_bootstrap_secret}" \
|
"${import_auth_args[@]}" \
|
||||||
--in "${migration_file}" \
|
--in "${migration_file}" \
|
||||||
--replace-existing \
|
--replace-existing \
|
||||||
--note "deploy conflict import $(date -u +%Y-%m-%dT%H:%M:%SZ)"; then
|
--note "deploy conflict import $(date -u +%Y-%m-%dT%H:%M:%SZ)"; then
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ set -euo pipefail
|
|||||||
usage() {
|
usage() {
|
||||||
cat <<'EOF'
|
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。
|
1. 如果部署目录已有旧版本且存在 stop.sh,则先执行旧版本 stop.sh。
|
||||||
@@ -23,6 +23,8 @@ usage() {
|
|||||||
--migrate-on-conflict 可选,普通发布遇到 schema 冲突时自动迁移,默认启用
|
--migrate-on-conflict 可选,普通发布遇到 schema 冲突时自动迁移,默认启用
|
||||||
--no-migrate-on-conflict 可选,禁用 schema 冲突自动迁移
|
--no-migrate-on-conflict 可选,禁用 schema 冲突自动迁移
|
||||||
--migration-dir <path> 可选,自动迁移 JSON 输出目录,默认部署目录内 database-migrations/<database>
|
--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 执行
|
--hook-with-sudo 可选,仅对 start.sh/stop.sh 使用 sudo -n 执行
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
@@ -173,6 +175,10 @@ CLEAR_DATABASE="0"
|
|||||||
MIGRATE_ON_CONFLICT="true"
|
MIGRATE_ON_CONFLICT="true"
|
||||||
MIGRATION_DIR=""
|
MIGRATION_DIR=""
|
||||||
HOOK_WITH_SUDO="0"
|
HOOK_WITH_SUDO="0"
|
||||||
|
MIGRATION_EXPORT_TOKEN=""
|
||||||
|
MIGRATION_IMPORT_TOKEN=""
|
||||||
|
PRESERVED_MIGRATION_EXPORT_TOKEN=""
|
||||||
|
PRESERVED_MIGRATION_IMPORT_TOKEN=""
|
||||||
DEPLOY_ITEMS=(
|
DEPLOY_ITEMS=(
|
||||||
".env"
|
".env"
|
||||||
".env.local"
|
".env.local"
|
||||||
@@ -222,6 +228,14 @@ while [[ $# -gt 0 ]]; do
|
|||||||
MIGRATION_DIR="${2:?缺少 --migration-dir 的值}"
|
MIGRATION_DIR="${2:?缺少 --migration-dir 的值}"
|
||||||
shift 2
|
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)
|
||||||
HOOK_WITH_SUDO="1"
|
HOOK_WITH_SUDO="1"
|
||||||
shift
|
shift
|
||||||
@@ -348,6 +362,8 @@ if [[ ! -f "${SOURCE_DIR}/start.sh" ]]; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
normalize_release_env_files "${SOURCE_DIR}"
|
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
|
if [[ -x "${DEPLOY_DIR}/stop.sh" ]]; then
|
||||||
echo "[jenkins-deploy] 先停止旧版本: ${DEPLOY_DIR}"
|
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_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_MIGRATE_ON_CONFLICT" "${MIGRATE_ON_CONFLICT}"
|
||||||
write_env_override "${DEPLOY_DIR}/.env.local" "GENARRATIVE_SPACETIME_MIGRATION_DIR" "${MIGRATION_DIR}"
|
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")"
|
DEPLOY_DATABASE="$(read_env_value "GENARRATIVE_SPACETIME_DATABASE" "${DEPLOY_DIR}/.env" "${DEPLOY_DIR}/.env.local")"
|
||||||
if [[ -z "${DEPLOY_DATABASE}" ]]; then
|
if [[ -z "${DEPLOY_DATABASE}" ]]; then
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
callSpacetimeProcedure,
|
||||||
callSpacetimeProcedureViaCli,
|
callSpacetimeProcedureViaCli,
|
||||||
ensureProcedureOk,
|
ensureProcedureOk,
|
||||||
parseArgs,
|
parseArgs,
|
||||||
@@ -17,11 +18,16 @@ try {
|
|||||||
operator_identity_hex: options.operatorIdentity,
|
operator_identity_hex: options.operatorIdentity,
|
||||||
note: options.note || '',
|
note: options.note || '',
|
||||||
};
|
};
|
||||||
const result = await callSpacetimeProcedureViaCli(
|
if (options.useHttp && !options.token) {
|
||||||
options,
|
throw new Error('--use-http 需要同时传入 --token。');
|
||||||
'authorize_database_migration_operator',
|
}
|
||||||
input,
|
const result = options.useHttp
|
||||||
);
|
? await callSpacetimeProcedure(options, 'authorize_database_migration_operator', input)
|
||||||
|
: await callSpacetimeProcedureViaCli(
|
||||||
|
options,
|
||||||
|
'authorize_database_migration_operator',
|
||||||
|
input,
|
||||||
|
);
|
||||||
ensureProcedureOk(result);
|
ensureProcedureOk(result);
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
|
|||||||
@@ -57,16 +57,24 @@ async function prepareWebExportOptions(options) {
|
|||||||
`[spacetime:migration:export] 已通过 Web API 创建临时 identity: ${identity.identity}`,
|
`[spacetime:migration:export] 已通过 Web API 创建临时 identity: ${identity.identity}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const authorizeResult = await callSpacetimeProcedureViaCli(
|
try {
|
||||||
options,
|
const authorizeResult = await callSpacetimeProcedureViaCli(
|
||||||
'authorize_database_migration_operator',
|
options,
|
||||||
{
|
'authorize_database_migration_operator',
|
||||||
bootstrap_secret: options.bootstrapSecret || '',
|
{
|
||||||
operator_identity_hex: identity.identity,
|
bootstrap_secret: options.bootstrapSecret || '',
|
||||||
note: options.note || 'temporary web api migration export',
|
operator_identity_hex: identity.identity,
|
||||||
},
|
note: options.note || 'temporary web api migration export',
|
||||||
);
|
},
|
||||||
ensureProcedureOk(authorizeResult);
|
);
|
||||||
|
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`);
|
console.log(`[spacetime:migration:export] 已授权临时 Web API identity`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -58,16 +58,24 @@ async function prepareWebImportOptions(options) {
|
|||||||
`[spacetime:migration:import] 已通过 Web API 创建临时 identity: ${identity.identity}`,
|
`[spacetime:migration:import] 已通过 Web API 创建临时 identity: ${identity.identity}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
const authorizeResult = await callSpacetimeProcedureViaCli(
|
try {
|
||||||
options,
|
const authorizeResult = await callSpacetimeProcedureViaCli(
|
||||||
'authorize_database_migration_operator',
|
options,
|
||||||
{
|
'authorize_database_migration_operator',
|
||||||
bootstrap_secret: options.bootstrapSecret || '',
|
{
|
||||||
operator_identity_hex: identity.identity,
|
bootstrap_secret: options.bootstrapSecret || '',
|
||||||
note: options.note || 'temporary web api migration import',
|
operator_identity_hex: identity.identity,
|
||||||
},
|
note: options.note || 'temporary web api migration import',
|
||||||
);
|
},
|
||||||
ensureProcedureOk(authorizeResult);
|
);
|
||||||
|
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`);
|
console.log(`[spacetime:migration:import] 已授权临时 Web API identity`);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user