master #14

Merged
kdletters merged 226 commits from master into release 2026-05-13 13:23:09 +08:00
2 changed files with 106 additions and 12 deletions
Showing only changes of commit 7160e90909 - Show all commits

View File

@@ -350,12 +350,16 @@ WASM_SOURCE="${CARGO_TARGET_DIR}/wasm32-unknown-unknown/release/spacetime_module
- 创建 `/stdb``/opt/genarrative``/srv/genarrative``/etc/genarrative``/var/lib/genarrative/maintenance`
- 安装或更新 SpacetimeDB。
- 安装 systemd unit。
- 安装 Nginx 配置和维护模式 snippet。
- 执行 `nginx -t`
- 可选安装 Nginx 配置和维护模式 snippet。
- 安装 Nginx 配置时执行 `nginx -t`
- 启用并启动 `spacetimedb.service``genarrative-api.service`
该流水线属于高风险操作,默认要求人工确认后执行。
已落地的 Jenkinsfile 为 `jenkins/Jenkinsfile.production-server-provision`。该流水线默认 `DRY_RUN=true`只打印将执行的初始化动作真正写入系统用户、目录、systemd、Nginx 配置并启动服务时,必须设置 `DRY_RUN=false` 且勾选 `CONFIRM_PROVISION`。当 `DEPLOY_TARGET=release` 时,还必须勾选 `CONFIRM_RELEASE_DEPLOY_AGENT`,并通过 `linux && genarrative-release-deploy` 调度到独立 release 部署 agent。
已落地的 Jenkinsfile 为 `jenkins/Jenkinsfile.production-server-provision`。该流水线默认 `DRY_RUN=true`只打印将执行的初始化动作真正写入系统用户、目录、systemd、环境文件并启动服务时,必须设置 `DRY_RUN=false` 且勾选 `CONFIRM_PROVISION`。当 `DEPLOY_TARGET=release` 时,还必须勾选 `CONFIRM_RELEASE_DEPLOY_AGENT`,并通过 `linux && genarrative-release-deploy` 调度到独立 release 部署 agent。
首次真实初始化默认保持 `INSTALL_NGINX_CONFIG=false`先完成系统用户、目录、SpacetimeDB、systemd unit 与 `/etc/genarrative/api-server.env` 落盘。等真实域名确定,并且目标机已经存在 `/etc/letsencrypt/live/<SERVER_NAME>/fullchain.pem``/etc/letsencrypt/live/<SERVER_NAME>/privkey.pem` 后,再把 `SERVER_NAME``genarrative.example.com` 改成真实域名,并设置 `INSTALL_NGINX_CONFIG=true` 安装 Nginx HTTPS 配置。流水线会拒绝在真实初始化中用占位域名写入 Nginx 配置,也会在证书缺失时提前失败。
若误用占位域名执行过真实初始化,失败通常发生在 `nginx -t`,错误表现为找不到 `/etc/letsencrypt/live/genarrative.example.com/fullchain.pem``privkey.pem`。新版初始化在 `INSTALL_NGINX_CONFIG=false` 时会检测并禁用上一轮留下的占位域名 Nginx 配置,避免它继续影响后续 `nginx -t`
### Web Build / Deploy

View File

@@ -26,7 +26,7 @@ pipeline {
string(name: 'WEB_LINK', defaultValue: '/srv/genarrative/web', description: 'Nginx 静态站点目录或软链接')
string(name: 'API_ENV_FILE', defaultValue: '/etc/genarrative/api-server.env', description: 'api-server 环境文件')
string(name: 'API_PORT', defaultValue: '8082', description: 'api-server 本机监听端口')
booleanParam(name: 'INSTALL_NGINX_CONFIG', defaultValue: true, description: '安装 Nginx 配置并执行 nginx -t')
booleanParam(name: 'INSTALL_NGINX_CONFIG', defaultValue: false, description: '安装 Nginx 配置并执行 nginx -t;首次初始化且证书未准备好时保持关闭')
booleanParam(name: 'ENABLE_SERVICES', defaultValue: true, description: '启用并启动 spacetimedb 与 api-server systemd 服务')
}
@@ -49,6 +49,9 @@ pipeline {
if (!params.SPACETIME_BIN_SOURCE?.trim()) {
error('SPACETIME_BIN_SOURCE 不能为空。')
}
if (!params.DRY_RUN && params.INSTALL_NGINX_CONFIG && params.SERVER_NAME?.trim() == 'genarrative.example.com') {
error('真实初始化安装 Nginx 配置时必须把 SERVER_NAME 改成真实域名,不能使用 genarrative.example.com 占位值。证书未准备好时请先保持 INSTALL_NGINX_CONFIG=false。')
}
}
}
}
@@ -124,6 +127,98 @@ pipeline {
deploy/env/api-server.env.example
}
validate_nginx_tls() {
local cert_dir="/etc/letsencrypt/live/${SERVER_NAME}"
if [[ "${SERVER_NAME}" == "genarrative.example.com" ]]; then
echo "[server-provision] SERVER_NAME 仍是占位域名,拒绝写入 Nginx HTTPS 配置。请填写真实域名,或先设置 INSTALL_NGINX_CONFIG=false。" >&2
exit 1
fi
if [[ ! -f "${cert_dir}/fullchain.pem" || ! -f "${cert_dir}/privkey.pem" ]]; then
echo "[server-provision] 未找到 Nginx HTTPS 证书: ${cert_dir}/fullchain.pem 或 ${cert_dir}/privkey.pem" >&2
echo "[server-provision] 请先完成证书申请,或首次初始化时设置 INSTALL_NGINX_CONFIG=false避免写入无法通过 nginx -t 的配置。" >&2
exit 1
fi
}
install_nginx_config_with_rollback() {
local config_target="/etc/nginx/conf.d/genarrative.conf"
local snippet_target="/etc/nginx/snippets/genarrative-maintenance.conf"
local rendered_config rendered_snippet config_backup snippet_backup
local had_config="false"
local had_snippet="false"
run_cmd mkdir -p /etc/nginx/snippets /etc/nginx/conf.d
echo "+ render deploy/nginx/genarrative.conf -> ${config_target}"
echo "+ install -m 0644 deploy/nginx/snippets/genarrative-maintenance.conf ${snippet_target}"
if [[ "${DRY_RUN}" == "true" ]]; then
echo "+ nginx -t"
return
fi
validate_nginx_tls
rendered_config="$(mktemp)"
rendered_snippet="$(mktemp)"
config_backup="$(mktemp)"
snippet_backup="$(mktemp)"
render_nginx_config >"${rendered_config}"
cp deploy/nginx/snippets/genarrative-maintenance.conf "${rendered_snippet}"
if [[ -f "${config_target}" ]]; then
cp -p "${config_target}" "${config_backup}"
had_config="true"
fi
if [[ -f "${snippet_target}" ]]; then
cp -p "${snippet_target}" "${snippet_backup}"
had_snippet="true"
fi
install -m 0644 "${rendered_config}" "${config_target}"
install -m 0644 "${rendered_snippet}" "${snippet_target}"
if ! nginx -t; then
echo "[server-provision] nginx -t 失败,恢复写入前的 Nginx 配置。" >&2
if [[ "${had_config}" == "true" ]]; then
cp -p "${config_backup}" "${config_target}"
else
rm -f "${config_target}"
fi
if [[ "${had_snippet}" == "true" ]]; then
cp -p "${snippet_backup}" "${snippet_target}"
else
rm -f "${snippet_target}"
fi
rm -f "${rendered_config}" "${rendered_snippet}" "${config_backup}" "${snippet_backup}"
exit 1
fi
rm -f "${rendered_config}" "${rendered_snippet}" "${config_backup}" "${snippet_backup}"
}
cleanup_placeholder_nginx_config() {
local config_target="/etc/nginx/conf.d/genarrative.conf"
local disabled_target
if [[ ! -f "${config_target}" ]]; then
return
fi
if ! grep -q "/etc/letsencrypt/live/genarrative.example.com/" "${config_target}"; then
return
fi
disabled_target="${config_target}.disabled-placeholder-$(date +%Y%m%d%H%M%S)"
echo "[server-provision] 发现上一轮初始化留下的占位域名 Nginx 配置,禁用: ${config_target} -> ${disabled_target}"
if [[ "${DRY_RUN}" != "true" ]]; then
mv "${config_target}" "${disabled_target}"
if command -v nginx >/dev/null 2>&1; then
if ! nginx -t; then
echo "[server-provision] 占位配置已禁用,但 nginx -t 仍失败;请检查其他 Nginx 配置。" >&2
fi
fi
fi
}
escape_sed_replacement() {
printf "%s" "$1" | sed "s/[&|]/\\\\&/g"
}
@@ -205,14 +300,9 @@ pipeline {
fi
if [[ "${INSTALL_NGINX_CONFIG}" == "true" ]]; then
run_cmd mkdir -p /etc/nginx/snippets /etc/nginx/conf.d
echo "+ render deploy/nginx/genarrative.conf -> /etc/nginx/conf.d/genarrative.conf"
if [[ "${DRY_RUN}" != "true" ]]; then
render_nginx_config >/etc/nginx/conf.d/genarrative.conf
chmod 0644 /etc/nginx/conf.d/genarrative.conf
fi
install_file deploy/nginx/snippets/genarrative-maintenance.conf /etc/nginx/snippets/genarrative-maintenance.conf 0644
run_cmd nginx -t
install_nginx_config_with_rollback
else
cleanup_placeholder_nginx_config
fi
run_cmd systemctl daemon-reload