From cf27686e1771f88b738e62bc7ed977a9340d5405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8E=86=E5=86=B0=E9=83=81-hermes=E7=89=88?= Date: Wed, 6 May 2026 17:03:12 +0800 Subject: [PATCH] ci: propagate database to api deploy --- .../PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md | 2 + jenkins/Jenkinsfile.production-api-deploy | 17 ++++- ...nkinsfile.production-full-build-and-deploy | 2 + scripts/deploy/production-api-deploy.sh | 62 ++++++++++++++++++- 4 files changed, 81 insertions(+), 2 deletions(-) diff --git a/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md b/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md index a530af70..d0429041 100644 --- a/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md +++ b/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md @@ -93,6 +93,8 @@ `api-server` 不放入 Docker,也不直接暴露公网端口。发布时替换版本目录并重启 `genarrative-api.service`。 +全量发布流水线的 `DATABASE` 参数必须同时传给 Stdb 发布和 API 发布:Stdb 发布负责把 wasm 发布到目标数据库,API 发布必须在重启 `genarrative-api.service` 前把同一个库名写入 `/etc/genarrative/api-server.env` 的 `GENARRATIVE_SPACETIME_DATABASE`,并同步 `GENARRATIVE_SPACETIME_SERVER_URL`。否则 api-server 会继续读取环境文件中的旧库名,出现 wasm 已发布到新库但 HTTP facade 仍访问旧库的错位。 + ## Nginx 规则 生产正式入口只保留必要路由: diff --git a/jenkins/Jenkinsfile.production-api-deploy b/jenkins/Jenkinsfile.production-api-deploy index 7d227b6d..928d1a70 100644 --- a/jenkins/Jenkinsfile.production-api-deploy +++ b/jenkins/Jenkinsfile.production-api-deploy @@ -24,6 +24,9 @@ pipeline { string(name: 'CURRENT_LINK', defaultValue: '/opt/genarrative/current', description: '当前版本软链接') string(name: 'SERVICE_NAME', defaultValue: 'genarrative-api.service', description: 'systemd 服务名') string(name: 'HEALTH_URL', defaultValue: 'http://127.0.0.1:8082/healthz', description: '本机健康检查地址') + string(name: 'API_ENV_FILE', defaultValue: '/etc/genarrative/api-server.env', description: 'api-server 环境文件') + string(name: 'DATABASE', defaultValue: 'genarrative-prod', description: 'api-server 连接的 SpacetimeDB database') + string(name: 'SPACETIME_SERVER_URL', defaultValue: 'http://127.0.0.1:3101', description: 'api-server 连接的 SpacetimeDB server URL') } stages { @@ -45,6 +48,15 @@ pipeline { if (!params.BUILD_NUMBER_TO_DEPLOY?.trim()) { error('BUILD_NUMBER_TO_DEPLOY 不能为空。') } + if (!params.DATABASE?.trim()) { + error('DATABASE 不能为空。') + } + if (!(params.DATABASE.trim() ==~ /^[a-z0-9]+(-[a-z0-9]+)*$/)) { + error("DATABASE 必须匹配 ^[a-z0-9]+(-[a-z0-9]+)*$: ${params.DATABASE}") + } + if (!params.API_ENV_FILE?.trim()) { + error('API_ENV_FILE 不能为空。') + } } } } @@ -105,7 +117,10 @@ pipeline { --release-root "${RELEASE_ROOT}" \ --current-link "${CURRENT_LINK}" \ --service "${SERVICE_NAME}" \ - --health-url "${HEALTH_URL}" + --health-url "${HEALTH_URL}" \ + --api-env-file "${API_ENV_FILE}" \ + --database "${DATABASE}" \ + --spacetime-server-url "${SPACETIME_SERVER_URL}" ' ''' } diff --git a/jenkins/Jenkinsfile.production-full-build-and-deploy b/jenkins/Jenkinsfile.production-full-build-and-deploy index a8a76878..2e12870a 100644 --- a/jenkins/Jenkinsfile.production-full-build-and-deploy +++ b/jenkins/Jenkinsfile.production-full-build-and-deploy @@ -165,6 +165,8 @@ pipeline { booleanParam(name: 'CONFIRM_RELEASE_DEPLOY_AGENT', value: params.CONFIRM_RELEASE_DEPLOY_AGENT), string(name: 'BUILD_JOB_NAME', value: params.API_BUILD_JOB_NAME), string(name: 'BUILD_NUMBER_TO_DEPLOY', value: env.API_BUILD_NUMBER), + string(name: 'DATABASE', value: params.DATABASE), + string(name: 'SPACETIME_SERVER_URL', value: params.SPACETIME_SERVER_URL ?: ''), ] } } diff --git a/scripts/deploy/production-api-deploy.sh b/scripts/deploy/production-api-deploy.sh index 1ee32a16..30c26e0f 100644 --- a/scripts/deploy/production-api-deploy.sh +++ b/scripts/deploy/production-api-deploy.sh @@ -5,10 +5,11 @@ set -euo pipefail usage() { cat <<'EOF' 用法: - ./scripts/deploy/production-api-deploy.sh --source-dir build/ [--version ] [--release-root /opt/genarrative/releases] [--current-link /opt/genarrative/current] [--service genarrative-api.service] [--health-url http://127.0.0.1:8082/healthz] + ./scripts/deploy/production-api-deploy.sh --source-dir build/ [--version ] [--release-root /opt/genarrative/releases] [--current-link /opt/genarrative/current] [--service genarrative-api.service] [--health-url http://127.0.0.1:8082/healthz] [--api-env-file /etc/genarrative/api-server.env] [--database genarrative-prod] [--spacetime-server-url http://127.0.0.1:3101] 说明: 进入维护模式,校验并发布 api-server 单文件,更新 current 链接,重启 systemd 服务并执行 healthz 检查。 + 若传入 --database,会在重启前把 GENARRATIVE_SPACETIME_DATABASE 写入 api-server 环境文件,避免服务继续读取旧库。 失败时保留维护模式。 EOF } @@ -23,6 +24,36 @@ require_argument() { fi } +validate_spacetime_database_name() { + local database="$1" + + if [[ ! "${database}" =~ ^[a-z0-9]+(-[a-z0-9]+)*$ ]]; then + echo "[production-api-deploy] --database 必须匹配 SpacetimeDB 数据库名规则 ^[a-z0-9]+(-[a-z0-9]+)*$,只能使用小写字母、数字,并用单个短横线分隔: ${database}" >&2 + exit 1 + fi +} + +write_env_value() { + local file_path="$1" + local key="$2" + local value="$3" + local temp_path + + mkdir -p "$(dirname "${file_path}")" + touch "${file_path}" + temp_path="$(mktemp)" + + if grep -qE "^${key}=" "${file_path}"; then + sed -E "s|^${key}=.*|${key}=${value}|" "${file_path}" >"${temp_path}" + else + cp "${file_path}" "${temp_path}" + printf "%s=%s\n" "${key}" "${value}" >>"${temp_path}" + fi + + cat "${temp_path}" >"${file_path}" + rm -f "${temp_path}" +} + SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" SOURCE_DIR="" VERSION="" @@ -30,6 +61,9 @@ RELEASE_ROOT="/opt/genarrative/releases" CURRENT_LINK="/opt/genarrative/current" SERVICE_NAME="genarrative-api.service" HEALTH_URL="http://127.0.0.1:8082/healthz" +API_ENV_FILE="/etc/genarrative/api-server.env" +DATABASE="" +SPACETIME_SERVER_URL="" DEPLOY_COMPLETED=0 while [[ $# -gt 0 ]]; do @@ -62,6 +96,18 @@ while [[ $# -gt 0 ]]; do HEALTH_URL="${2:?缺少 --health-url 的值}" shift 2 ;; + --api-env-file) + API_ENV_FILE="${2:?缺少 --api-env-file 的值}" + shift 2 + ;; + --database) + DATABASE="${2:?缺少 --database 的值}" + shift 2 + ;; + --spacetime-server-url) + SPACETIME_SERVER_URL="${2:?缺少 --spacetime-server-url 的值}" + shift 2 + ;; *) echo "[production-api-deploy] 未知参数: $1" >&2 usage >&2 @@ -72,6 +118,10 @@ done require_argument "${SOURCE_DIR}" "--source-dir" +if [[ -n "${DATABASE}" ]]; then + validate_spacetime_database_name "${DATABASE}" +fi + if [[ ! -d "${SOURCE_DIR}" ]]; then echo "[production-api-deploy] 发布目录不存在: ${SOURCE_DIR}" >&2 exit 1 @@ -117,6 +167,16 @@ if [[ -f "${SOURCE_DIR}/release-manifest.json" ]]; then cp "${SOURCE_DIR}/release-manifest.json" "${RELEASE_DIR}/release-manifest.api-server.json" fi +if [[ -n "${DATABASE}" ]]; then + echo "[production-api-deploy] 写入 api-server SpacetimeDB database: ${DATABASE} -> ${API_ENV_FILE}" + write_env_value "${API_ENV_FILE}" "GENARRATIVE_SPACETIME_DATABASE" "${DATABASE}" +fi + +if [[ -n "${SPACETIME_SERVER_URL}" ]]; then + echo "[production-api-deploy] 写入 api-server SpacetimeDB server: ${SPACETIME_SERVER_URL} -> ${API_ENV_FILE}" + write_env_value "${API_ENV_FILE}" "GENARRATIVE_SPACETIME_SERVER_URL" "${SPACETIME_SERVER_URL}" +fi + mkdir -p "$(dirname "${CURRENT_LINK}")" ln -sfn "${RELEASE_DIR}" "${CURRENT_LINK}"