fix(jenkins): add git fallback and nginx aliases
This commit is contained in:
@@ -438,6 +438,14 @@
|
|||||||
- 验证:发布链路使用当前 `deploy/systemd`、`deploy/nginx`、`scripts/deploy` 和 `jenkins/Jenkinsfile.production-*`。
|
- 验证:发布链路使用当前 `deploy/systemd`、`deploy/nginx`、`scripts/deploy` 和 `jenkins/Jenkinsfile.production-*`。
|
||||||
- 关联:`docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`。
|
- 关联:`docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`。
|
||||||
|
|
||||||
|
## Jenkins 生产流水线拉 Git 先本机再内网备用
|
||||||
|
|
||||||
|
- 现象:生产发布、数据库导入导出或服务器配置流水线在目标 Linux agent 上执行 `GitSCM checkout` 时,`http://127.0.0.1:3000/GenarrativeAI/Genarrative.git` 不可达,导致脚本还没拉下来就失败。
|
||||||
|
- 原因:`127.0.0.1` 只代表当前执行阶段的 agent 自身;当 release agent 与 Git 服务不在同一台机器,或本机 Git/Web 服务临时不可用时,固定写死 localhost 会阻断 Jenkinsfile 内部源码/脚本 checkout。
|
||||||
|
- 处理:需要在运行于内网 Linux agent 的 Jenkinsfile 首次 `checkout([$class: 'GitSCM', ...])` 层先尝试 `GIT_REMOTE_URL=http://127.0.0.1:3000/GenarrativeAI/Genarrative.git`,失败后再尝试 `GIT_REMOTE_FALLBACK_URL=http://10.2.0.10:3000/GenarrativeAI/Genarrative.git`;后续统一走 `scripts/jenkins-checkout-source.sh`,该脚本也按主地址、备用地址顺序重新 fetch 并把 `origin` 切到实际可用地址。`10.2.0.10` 是内网地址,Windows controller 或 Windows 构建节点不要接入这个 fallback。
|
||||||
|
- 验证:扫描所有以 `127.0.0.1:3000` 拉 Git 且运行在 Linux agent 的生产 Jenkinsfile,确认存在 `GIT_REMOTE_FALLBACK_URL`、`EFFECTIVE_GIT_REMOTE_URL` 和脚本层 `GIT_REMOTE_FALLBACK_URL` 透传;运行 `bash -n scripts/jenkins-checkout-source.sh`。
|
||||||
|
- 关联:`jenkins/Jenkinsfile.production-web-deploy`、`jenkins/Jenkinsfile.production-api-deploy`、`jenkins/Jenkinsfile.production-stdb-module-publish`、`jenkins/Jenkinsfile.production-server-provision`、`jenkins/Jenkinsfile.production-database-export`、`jenkins/Jenkinsfile.production-database-import`、`scripts/jenkins-checkout-source.sh`、`docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md`。
|
||||||
|
|
||||||
## Jenkins 可选参数在 set -u 下不能裸读
|
## Jenkins 可选参数在 set -u 下不能裸读
|
||||||
|
|
||||||
- 现象:数据库导入或导出流水线报 `INCLUDE_TABLES: unbound variable`,或其它可选参数在 Bash 中未定义即退出。
|
- 现象:数据库导入或导出流水线报 `INCLUDE_TABLES: unbound variable`,或其它可选参数在 Bash 中未定义即退出。
|
||||||
|
|||||||
@@ -132,8 +132,8 @@ SpacetimeDB 公网路由默认保持收敛,只按实际前端 SDK 需要暴露
|
|||||||
|
|
||||||
Nginx 配置文件分为两类:
|
Nginx 配置文件分为两类:
|
||||||
|
|
||||||
- `deploy/nginx/genarrative.conf`:生产正式域名 HTTPS 配置,`genarrative.example.com` 只是占位域名,安装时必须替换为真实 `SERVER_NAME`,并要求 `/etc/letsencrypt/live/<SERVER_NAME>/fullchain.pem` 与 `privkey.pem` 已存在。
|
- `deploy/nginx/genarrative.conf`:生产正式域名 HTTPS 配置,`genarrative.example.com` 只是占位域名,安装时必须替换为真实 `SERVER_NAME`,并要求 `/etc/letsencrypt/live/<SERVER_NAME>/fullchain.pem` 与 `privkey.pem` 已存在。`SERVER_NAME` 只填证书主目录名对应的单个域名;`www` 等额外域名通过 `SERVER_ALIASES` 写入 Nginx `server_name`,不参与证书目录拼接。
|
||||||
- `deploy/nginx/genarrative-dev-http.conf`:开发服无域名时的 HTTP-only 配置,只允许 `DEPLOY_TARGET=development` 使用。没有域名时,`SERVER_NAME` 填开发机 IP 或临时主机名。它仍复用同一套静态目录、后台 API 反代、临时主站 `/api/*` 反代和 SpacetimeDB SDK 最小公网路由,不恢复旧 `/generated-*` 或公网 `/healthz`。
|
- `deploy/nginx/genarrative-dev-http.conf`:开发服无域名时的 HTTP-only 配置,只允许 `DEPLOY_TARGET=development` 使用。没有域名时,`SERVER_NAME` 填开发机 IP 或临时主机名;如有多个入口,额外域名或 IP 填 `SERVER_ALIASES`。它仍复用同一套静态目录、后台 API 反代、临时主站 `/api/*` 反代和 SpacetimeDB SDK 最小公网路由,不恢复旧 `/generated-*` 或公网 `/healthz`。
|
||||||
|
|
||||||
## 维护模式
|
## 维护模式
|
||||||
|
|
||||||
@@ -273,12 +273,13 @@ journalctl -u 'jenkins-agent@*.service' -f
|
|||||||
Jenkins controller 与 Linux agent 看到的 Git 服务地址不同,必须拆成两层配置:
|
Jenkins controller 与 Linux agent 看到的 Git 服务地址不同,必须拆成两层配置:
|
||||||
|
|
||||||
- Jenkins Job 的 `Pipeline script from SCM` 由 controller 执行,SCM URL 使用 controller 可访问的公网地址:`http://82.157.175.59:3000/GenarrativeAI/Genarrative.git`。
|
- Jenkins Job 的 `Pipeline script from SCM` 由 controller 执行,SCM URL 使用 controller 可访问的公网地址:`http://82.157.175.59:3000/GenarrativeAI/Genarrative.git`。
|
||||||
- Jenkinsfile 内部的源码、脚本 checkout 在 Linux agent 上执行,`GIT_REMOTE_URL` 使用 agent 本机可访问地址:`http://127.0.0.1:3000/GenarrativeAI/Genarrative.git`。
|
- Jenkinsfile 内部的源码、脚本 checkout 在 Linux agent 上执行,`GIT_REMOTE_URL` 优先使用 agent 本机可访问地址:`http://127.0.0.1:3000/GenarrativeAI/Genarrative.git`。
|
||||||
|
- 若 `127.0.0.1` Git 服务在当前 Linux agent 上不可达,发布、数据库和服务器配置类 Jenkinsfile 会用 `GIT_REMOTE_FALLBACK_URL=http://10.2.0.10:3000/GenarrativeAI/Genarrative.git` 重新 checkout;`scripts/jenkins-checkout-source.sh` 后续 fetch 也会按主地址、备用地址顺序重试,并在日志中输出最终使用的远端。`10.2.0.10` 是内网地址,Windows controller 或 Windows 构建节点不使用该 fallback。
|
||||||
- 这里的 `3000` 是 Git/Web 服务端口,不是 SpacetimeDB 端口;生产 SpacetimeDB 固定使用 `http://127.0.0.1:3101`,避免流水线部署时与本机 Git 服务抢端口。
|
- 这里的 `3000` 是 Git/Web 服务端口,不是 SpacetimeDB 端口;生产 SpacetimeDB 固定使用 `http://127.0.0.1:3101`,避免流水线部署时与本机 Git 服务抢端口。
|
||||||
|
|
||||||
因此生产 Jenkinsfile 不使用 `checkout scm` 作为构建源码入口,而是显式 `checkout([$class: 'GitSCM', userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]], ...])`。后续 `scripts/jenkins-checkout-source.sh` 会继续把 `origin` 设置为 `GIT_REMOTE_URL`,并按 `SOURCE_BRANCH` / `COMMIT_HASH` 拉取和校验目标提交。
|
因此生产 Jenkinsfile 不使用 `checkout scm` 作为构建源码入口,而是显式 `checkout([$class: 'GitSCM', userRemoteConfigs: [[url: remoteUrl]], ...])`。首次 checkout 先尝试 `GIT_REMOTE_URL`,失败后尝试 `GIT_REMOTE_FALLBACK_URL`;后续 `scripts/jenkins-checkout-source.sh` 会继续把 `origin` 设置为实际可用远端,并按 `SOURCE_BRANCH` / `COMMIT_HASH` 拉取和校验目标提交。
|
||||||
|
|
||||||
`127.0.0.1` 只代表当前执行该阶段的 Linux agent 自身;如果 release agent 与 Git 服务不在同一台机器,必须把对应 Jenkinsfile 的 `GIT_REMOTE_URL` 改成 release agent 可访问的内网地址,不能让 release 发布阶段回退到 controller 公网拉取。
|
`127.0.0.1` 只代表当前执行该阶段的 Linux agent 自身;如果 release agent 与 Git 服务不在同一台机器,应优先确认 `10.2.0.10` 这类内网备用地址是否可达,并按实际网络拓扑更新对应 Jenkinsfile 的 `GIT_REMOTE_FALLBACK_URL`。release 发布阶段不能回退到 controller 公网拉取。
|
||||||
|
|
||||||
### SSH PEM 凭证
|
### SSH PEM 凭证
|
||||||
|
|
||||||
@@ -462,7 +463,7 @@ WASM_SOURCE="${CARGO_TARGET_DIR}/wasm32-unknown-unknown/release/spacetime_module
|
|||||||
该流水线属于高风险操作,默认要求人工确认后执行。
|
该流水线属于高风险操作,默认要求人工确认后执行。
|
||||||
已落地的 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。
|
已落地的 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。
|
||||||
|
|
||||||
首次真实初始化默认保持 `NGINX_CONFIG_MODE=none`,先完成系统用户、目录、SpacetimeDB、systemd unit 与 `/etc/genarrative/api-server.env` 落盘。开发服没有域名时,使用 `DEPLOY_TARGET=development` + `NGINX_CONFIG_MODE=development-http` 安装 `deploy/nginx/genarrative-dev-http.conf`,并把 `SERVER_NAME` 填为开发机 IP 或临时主机名。等正式域名确定,并且目标机已经存在 `/etc/letsencrypt/live/<SERVER_NAME>/fullchain.pem` 与 `/etc/letsencrypt/live/<SERVER_NAME>/privkey.pem` 后,再把 `SERVER_NAME` 改成真实域名,并设置 `NGINX_CONFIG_MODE=production-https` 安装 Nginx HTTPS 配置。流水线会拒绝 release 目标安装 `development-http`,也会拒绝用占位域名或缺失证书安装 `production-https`。Nginx 配置写入后必须先 `nginx -t`,再 `nginx -s reload`,不能只验证配置而不重载当前进程。
|
首次真实初始化默认保持 `NGINX_CONFIG_MODE=none`,先完成系统用户、目录、SpacetimeDB、systemd unit 与 `/etc/genarrative/api-server.env` 落盘。开发服没有域名时,使用 `DEPLOY_TARGET=development` + `NGINX_CONFIG_MODE=development-http` 安装 `deploy/nginx/genarrative-dev-http.conf`,并把 `SERVER_NAME` 填为开发机 IP 或临时主机名。等正式域名确定,并且目标机已经存在 `/etc/letsencrypt/live/<SERVER_NAME>/fullchain.pem` 与 `/etc/letsencrypt/live/<SERVER_NAME>/privkey.pem` 后,再把 `SERVER_NAME` 改成证书主域名,并设置 `NGINX_CONFIG_MODE=production-https` 安装 Nginx HTTPS 配置。如果同一张证书同时覆盖根域名和 `www` 域名,`SERVER_NAME` 仍只填证书目录名,例如 `genarrative.world`,`SERVER_ALIASES` 填 `www.genarrative.world`。流水线会拒绝 release 目标安装 `development-http`,也会拒绝用占位域名或缺失证书安装 `production-https`。Nginx 配置写入后必须先 `nginx -t`,再 `nginx -s reload`,不能只验证配置而不重载当前进程。
|
||||||
|
|
||||||
若误用占位域名执行过真实初始化,失败通常发生在 `nginx -t`,错误表现为找不到 `/etc/letsencrypt/live/genarrative.example.com/fullchain.pem` 或 `privkey.pem`。新版初始化在 `NGINX_CONFIG_MODE=none` 时会检测并禁用上一轮留下的占位域名 Nginx 配置,避免它继续影响后续 `nginx -t`。
|
若误用占位域名执行过真实初始化,失败通常发生在 `nginx -t`,错误表现为找不到 `/etc/letsencrypt/live/genarrative.example.com/fullchain.pem` 或 `privkey.pem`。新版初始化在 `NGINX_CONFIG_MODE=none` 时会检测并禁用上一轮留下的占位域名 Nginx 配置,避免它继续影响后续 `nginx -t`。
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
||||||
|
GIT_REMOTE_FALLBACK_URL = 'http://10.2.0.10:3000/GenarrativeAI/Genarrative.git'
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
@@ -66,13 +67,25 @@ pipeline {
|
|||||||
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def checkoutFromRemote = { String remoteUrl ->
|
||||||
checkout([
|
checkout([
|
||||||
$class: 'GitSCM',
|
$class: 'GitSCM',
|
||||||
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
||||||
doGenerateSubmoduleConfigurations: false,
|
doGenerateSubmoduleConfigurations: false,
|
||||||
extensions: [[$class: 'CleanBeforeCheckout']],
|
extensions: [[$class: 'CleanBeforeCheckout']],
|
||||||
userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]],
|
userRemoteConfigs: [[url: remoteUrl]],
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_URL
|
||||||
|
} catch (error) {
|
||||||
|
echo "Git 主地址拉取失败: ${env.GIT_REMOTE_URL},改用备用地址: ${env.GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_FALLBACK_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_FALLBACK_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
script {
|
script {
|
||||||
if (params.COMMIT_HASH?.trim()) {
|
if (params.COMMIT_HASH?.trim()) {
|
||||||
echo "API 发布脚本 checkout 将忽略上游构建 commit=${params.COMMIT_HASH},改用 ${params.SOURCE_BRANCH ?: 'master'} 最新提交,避免发布阶段回退到旧部署脚本。构建产物仍由 BUILD_NUMBER_TO_DEPLOY 决定。"
|
echo "API 发布脚本 checkout 将忽略上游构建 commit=${params.COMMIT_HASH},改用 ${params.SOURCE_BRANCH ?: 'master'} 最新提交,避免发布阶段回退到旧部署脚本。构建产物仍由 BUILD_NUMBER_TO_DEPLOY 决定。"
|
||||||
@@ -84,7 +97,8 @@ pipeline {
|
|||||||
chmod +x scripts/jenkins-checkout-source.sh
|
chmod +x scripts/jenkins-checkout-source.sh
|
||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
||||||
COMMIT_HASH="" \
|
COMMIT_HASH="" \
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL}" \
|
GIT_REMOTE_URL="${EFFECTIVE_GIT_REMOTE_URL:-${GIT_REMOTE_URL}}" \
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}" \
|
||||||
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
||||||
scripts/jenkins-checkout-source.sh
|
scripts/jenkins-checkout-source.sh
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
||||||
|
GIT_REMOTE_FALLBACK_URL = 'http://10.2.0.10:3000/GenarrativeAI/Genarrative.git'
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
@@ -82,20 +83,33 @@ pipeline {
|
|||||||
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def checkoutFromRemote = { String remoteUrl ->
|
||||||
checkout([
|
checkout([
|
||||||
$class: 'GitSCM',
|
$class: 'GitSCM',
|
||||||
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
||||||
doGenerateSubmoduleConfigurations: false,
|
doGenerateSubmoduleConfigurations: false,
|
||||||
extensions: [[$class: 'CleanBeforeCheckout']],
|
extensions: [[$class: 'CleanBeforeCheckout']],
|
||||||
userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]],
|
userRemoteConfigs: [[url: remoteUrl]],
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_URL
|
||||||
|
} catch (error) {
|
||||||
|
echo "Git 主地址拉取失败: ${env.GIT_REMOTE_URL},改用备用地址: ${env.GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_FALLBACK_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_FALLBACK_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
sh '''
|
sh '''
|
||||||
bash -lc '
|
bash -lc '
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
chmod +x scripts/jenkins-checkout-source.sh
|
chmod +x scripts/jenkins-checkout-source.sh
|
||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
||||||
COMMIT_HASH="${COMMIT_HASH:-}" \
|
COMMIT_HASH="${COMMIT_HASH:-}" \
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL}" \
|
GIT_REMOTE_URL="${EFFECTIVE_GIT_REMOTE_URL:-${GIT_REMOTE_URL}}" \
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}" \
|
||||||
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
||||||
scripts/jenkins-checkout-source.sh
|
scripts/jenkins-checkout-source.sh
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
||||||
|
GIT_REMOTE_FALLBACK_URL = 'http://10.2.0.10:3000/GenarrativeAI/Genarrative.git'
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
@@ -140,20 +141,33 @@ pipeline {
|
|||||||
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def checkoutFromRemote = { String remoteUrl ->
|
||||||
checkout([
|
checkout([
|
||||||
$class: 'GitSCM',
|
$class: 'GitSCM',
|
||||||
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
||||||
doGenerateSubmoduleConfigurations: false,
|
doGenerateSubmoduleConfigurations: false,
|
||||||
extensions: [[$class: 'CleanBeforeCheckout']],
|
extensions: [[$class: 'CleanBeforeCheckout']],
|
||||||
userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]],
|
userRemoteConfigs: [[url: remoteUrl]],
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_URL
|
||||||
|
} catch (error) {
|
||||||
|
echo "Git 主地址拉取失败: ${env.GIT_REMOTE_URL},改用备用地址: ${env.GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_FALLBACK_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_FALLBACK_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
sh '''
|
sh '''
|
||||||
bash -lc '
|
bash -lc '
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
chmod +x scripts/jenkins-checkout-source.sh
|
chmod +x scripts/jenkins-checkout-source.sh
|
||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
||||||
COMMIT_HASH="${COMMIT_HASH:-}" \
|
COMMIT_HASH="${COMMIT_HASH:-}" \
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL}" \
|
GIT_REMOTE_URL="${EFFECTIVE_GIT_REMOTE_URL:-${GIT_REMOTE_URL}}" \
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}" \
|
||||||
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
||||||
scripts/jenkins-checkout-source.sh
|
scripts/jenkins-checkout-source.sh
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
||||||
|
GIT_REMOTE_FALLBACK_URL = 'http://10.2.0.10:3000/GenarrativeAI/Genarrative.git'
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
@@ -19,7 +20,8 @@ pipeline {
|
|||||||
booleanParam(name: 'DRY_RUN', defaultValue: true, description: '只打印将执行的服务器初始化命令,不写入系统配置')
|
booleanParam(name: 'DRY_RUN', defaultValue: true, description: '只打印将执行的服务器初始化命令,不写入系统配置')
|
||||||
string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '部署脚本来源分支')
|
string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '部署脚本来源分支')
|
||||||
string(name: 'COMMIT_HASH', defaultValue: '', description: '部署脚本来源 commit')
|
string(name: 'COMMIT_HASH', defaultValue: '', description: '部署脚本来源 commit')
|
||||||
string(name: 'SERVER_NAME', defaultValue: 'genarrative.example.com', description: 'Nginx server_name 与证书域名')
|
string(name: 'SERVER_NAME', defaultValue: 'genarrative.example.com', description: '证书主域名;也作为 Nginx server_name 的第一个域名')
|
||||||
|
string(name: 'SERVER_ALIASES', defaultValue: '', description: '可选,额外 Nginx server_name,多个用空格或逗号分隔,例如 www.genarrative.world')
|
||||||
string(name: 'SPACETIME_BIN_SOURCE', defaultValue: '/usr/local/bin/spacetime', description: '服务器上已有 spacetime CLI 路径')
|
string(name: 'SPACETIME_BIN_SOURCE', defaultValue: '/usr/local/bin/spacetime', description: '服务器上已有 spacetime CLI 路径')
|
||||||
string(name: 'SPACETIME_ROOT', defaultValue: '/stdb', description: 'SpacetimeDB root-dir')
|
string(name: 'SPACETIME_ROOT', defaultValue: '/stdb', description: 'SpacetimeDB root-dir')
|
||||||
string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: 'release 根目录')
|
string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: 'release 根目录')
|
||||||
@@ -47,6 +49,17 @@ pipeline {
|
|||||||
if (!params.SERVER_NAME?.trim()) {
|
if (!params.SERVER_NAME?.trim()) {
|
||||||
error('SERVER_NAME 不能为空。')
|
error('SERVER_NAME 不能为空。')
|
||||||
}
|
}
|
||||||
|
if (!(params.SERVER_NAME.trim() ==~ /^[A-Za-z0-9][A-Za-z0-9.-]*$/)) {
|
||||||
|
error("SERVER_NAME 只能填写单个域名或 IP,不能包含空格、路径或协议: ${params.SERVER_NAME}")
|
||||||
|
}
|
||||||
|
def serverAliases = params.SERVER_ALIASES?.trim()
|
||||||
|
if (serverAliases) {
|
||||||
|
serverAliases.split(/[,\s]+/).findAll { it }.each { aliasName ->
|
||||||
|
if (!(aliasName ==~ /^[A-Za-z0-9][A-Za-z0-9.-]*$/)) {
|
||||||
|
error("SERVER_ALIASES 只能填写域名或 IP,多个用空格或逗号分隔: ${aliasName}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!params.SPACETIME_BIN_SOURCE?.trim()) {
|
if (!params.SPACETIME_BIN_SOURCE?.trim()) {
|
||||||
error('SPACETIME_BIN_SOURCE 不能为空。')
|
error('SPACETIME_BIN_SOURCE 不能为空。')
|
||||||
}
|
}
|
||||||
@@ -69,20 +82,33 @@ pipeline {
|
|||||||
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def checkoutFromRemote = { String remoteUrl ->
|
||||||
checkout([
|
checkout([
|
||||||
$class: 'GitSCM',
|
$class: 'GitSCM',
|
||||||
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
||||||
doGenerateSubmoduleConfigurations: false,
|
doGenerateSubmoduleConfigurations: false,
|
||||||
extensions: [[$class: 'CleanBeforeCheckout']],
|
extensions: [[$class: 'CleanBeforeCheckout']],
|
||||||
userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]],
|
userRemoteConfigs: [[url: remoteUrl]],
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_URL
|
||||||
|
} catch (error) {
|
||||||
|
echo "Git 主地址拉取失败: ${env.GIT_REMOTE_URL},改用备用地址: ${env.GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_FALLBACK_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_FALLBACK_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
sh '''
|
sh '''
|
||||||
bash <<'BASH'
|
bash <<'BASH'
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
chmod +x scripts/jenkins-checkout-source.sh
|
chmod +x scripts/jenkins-checkout-source.sh
|
||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
||||||
COMMIT_HASH="${COMMIT_HASH:-}" \
|
COMMIT_HASH="${COMMIT_HASH:-}" \
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL}" \
|
GIT_REMOTE_URL="${EFFECTIVE_GIT_REMOTE_URL:-${GIT_REMOTE_URL}}" \
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}" \
|
||||||
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
||||||
scripts/jenkins-checkout-source.sh
|
scripts/jenkins-checkout-source.sh
|
||||||
BASH
|
BASH
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
||||||
|
GIT_REMOTE_FALLBACK_URL = 'http://10.2.0.10:3000/GenarrativeAI/Genarrative.git'
|
||||||
}
|
}
|
||||||
|
|
||||||
parameters {
|
parameters {
|
||||||
@@ -78,20 +79,33 @@ pipeline {
|
|||||||
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def checkoutFromRemote = { String remoteUrl ->
|
||||||
checkout([
|
checkout([
|
||||||
$class: 'GitSCM',
|
$class: 'GitSCM',
|
||||||
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
||||||
doGenerateSubmoduleConfigurations: false,
|
doGenerateSubmoduleConfigurations: false,
|
||||||
extensions: [[$class: 'CleanBeforeCheckout']],
|
extensions: [[$class: 'CleanBeforeCheckout']],
|
||||||
userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]],
|
userRemoteConfigs: [[url: remoteUrl]],
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_URL
|
||||||
|
} catch (error) {
|
||||||
|
echo "Git 主地址拉取失败: ${env.GIT_REMOTE_URL},改用备用地址: ${env.GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_FALLBACK_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_FALLBACK_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
sh '''
|
sh '''
|
||||||
bash -lc '
|
bash -lc '
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
chmod +x scripts/jenkins-checkout-source.sh
|
chmod +x scripts/jenkins-checkout-source.sh
|
||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
||||||
COMMIT_HASH="${COMMIT_HASH:-}" \
|
COMMIT_HASH="${COMMIT_HASH:-}" \
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL}" \
|
GIT_REMOTE_URL="${EFFECTIVE_GIT_REMOTE_URL:-${GIT_REMOTE_URL}}" \
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}" \
|
||||||
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
||||||
scripts/jenkins-checkout-source.sh
|
scripts/jenkins-checkout-source.sh
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ pipeline {
|
|||||||
|
|
||||||
environment {
|
environment {
|
||||||
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
GIT_REMOTE_URL = 'http://127.0.0.1:3000/GenarrativeAI/Genarrative.git'
|
||||||
|
GIT_REMOTE_FALLBACK_URL = 'http://10.2.0.10:3000/GenarrativeAI/Genarrative.git'
|
||||||
WEB_ARTIFACT_ROOT = '/var/cache/genarrative-build/web-artifacts'
|
WEB_ARTIFACT_ROOT = '/var/cache/genarrative-build/web-artifacts'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,20 +55,33 @@ pipeline {
|
|||||||
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-build' : 'linux && genarrative-release-deploy'}"
|
||||||
}
|
}
|
||||||
steps {
|
steps {
|
||||||
|
script {
|
||||||
|
def checkoutFromRemote = { String remoteUrl ->
|
||||||
checkout([
|
checkout([
|
||||||
$class: 'GitSCM',
|
$class: 'GitSCM',
|
||||||
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
branches: [[name: "*/${params.SOURCE_BRANCH}"]],
|
||||||
doGenerateSubmoduleConfigurations: false,
|
doGenerateSubmoduleConfigurations: false,
|
||||||
extensions: [[$class: 'CleanBeforeCheckout']],
|
extensions: [[$class: 'CleanBeforeCheckout']],
|
||||||
userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]],
|
userRemoteConfigs: [[url: remoteUrl]],
|
||||||
])
|
])
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_URL
|
||||||
|
} catch (error) {
|
||||||
|
echo "Git 主地址拉取失败: ${env.GIT_REMOTE_URL},改用备用地址: ${env.GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
checkoutFromRemote(env.GIT_REMOTE_FALLBACK_URL)
|
||||||
|
env.EFFECTIVE_GIT_REMOTE_URL = env.GIT_REMOTE_FALLBACK_URL
|
||||||
|
}
|
||||||
|
}
|
||||||
sh '''
|
sh '''
|
||||||
bash -lc '
|
bash -lc '
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
chmod +x scripts/jenkins-checkout-source.sh
|
chmod +x scripts/jenkins-checkout-source.sh
|
||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \
|
||||||
COMMIT_HASH="${COMMIT_HASH:-}" \
|
COMMIT_HASH="${COMMIT_HASH:-}" \
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL}" \
|
GIT_REMOTE_URL="${EFFECTIVE_GIT_REMOTE_URL:-${GIT_REMOTE_URL}}" \
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}" \
|
||||||
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
SOURCE_COMMIT_FILE=".jenkins-source-commit" \
|
||||||
scripts/jenkins-checkout-source.sh
|
scripts/jenkins-checkout-source.sh
|
||||||
'
|
'
|
||||||
|
|||||||
@@ -5,11 +5,14 @@ set -euo pipefail
|
|||||||
SOURCE_BRANCH="${SOURCE_BRANCH:-master}"
|
SOURCE_BRANCH="${SOURCE_BRANCH:-master}"
|
||||||
COMMIT_HASH="${COMMIT_HASH:-}"
|
COMMIT_HASH="${COMMIT_HASH:-}"
|
||||||
GIT_REMOTE_URL="${GIT_REMOTE_URL:-}"
|
GIT_REMOTE_URL="${GIT_REMOTE_URL:-}"
|
||||||
|
GIT_REMOTE_FALLBACK_URL="${GIT_REMOTE_FALLBACK_URL:-}"
|
||||||
SOURCE_COMMIT_FILE="${SOURCE_COMMIT_FILE:-.jenkins-source-commit}"
|
SOURCE_COMMIT_FILE="${SOURCE_COMMIT_FILE:-.jenkins-source-commit}"
|
||||||
|
|
||||||
# Windows PowerShell 5.1 的 UTF-8 输出可能带 BOM;下游参数校验前先剥离不可见字节。
|
# Windows PowerShell 5.1 的 UTF-8 输出可能带 BOM;下游参数校验前先剥离不可见字节。
|
||||||
SOURCE_BRANCH="$(printf "%s" "${SOURCE_BRANCH}" | sed $'s/^\xef\xbb\xbf//' | tr -d '\r\n')"
|
SOURCE_BRANCH="$(printf "%s" "${SOURCE_BRANCH}" | sed $'s/^\xef\xbb\xbf//' | tr -d '\r\n')"
|
||||||
COMMIT_HASH="$(printf "%s" "${COMMIT_HASH}" | sed $'s/^\xef\xbb\xbf//' | tr -d '\r\n')"
|
COMMIT_HASH="$(printf "%s" "${COMMIT_HASH}" | sed $'s/^\xef\xbb\xbf//' | tr -d '\r\n')"
|
||||||
|
GIT_REMOTE_URL="$(printf "%s" "${GIT_REMOTE_URL}" | sed $'s/^\xef\xbb\xbf//' | tr -d '\r\n')"
|
||||||
|
GIT_REMOTE_FALLBACK_URL="$(printf "%s" "${GIT_REMOTE_FALLBACK_URL}" | sed $'s/^\xef\xbb\xbf//' | tr -d '\r\n')"
|
||||||
|
|
||||||
if [[ ! "${SOURCE_BRANCH}" =~ ^[0-9A-Za-z._/-]+$ ]]; then
|
if [[ ! "${SOURCE_BRANCH}" =~ ^[0-9A-Za-z._/-]+$ ]]; then
|
||||||
echo "[jenkins-checkout-source] SOURCE_BRANCH 只能包含数字、字母、点、下划线、短横线和斜杠: ${SOURCE_BRANCH}" >&2
|
echo "[jenkins-checkout-source] SOURCE_BRANCH 只能包含数字、字母、点、下划线、短横线和斜杠: ${SOURCE_BRANCH}" >&2
|
||||||
@@ -26,12 +29,52 @@ if [[ -n "${COMMIT_HASH}" && ! "${COMMIT_HASH}" =~ ^[0-9a-fA-F]{7,40}$ ]]; then
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -n "${GIT_REMOTE_URL}" ]]; then
|
GIT_REMOTE_CANDIDATES=()
|
||||||
git remote set-url origin "${GIT_REMOTE_URL}"
|
add_git_remote_candidate() {
|
||||||
|
local candidate="$1"
|
||||||
|
local existing
|
||||||
|
if [[ -z "${candidate}" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
for existing in "${GIT_REMOTE_CANDIDATES[@]}"; do
|
||||||
|
if [[ "${existing}" == "${candidate}" ]]; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
GIT_REMOTE_CANDIDATES+=("${candidate}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch_source_branch() {
|
||||||
|
local remote_url="$1"
|
||||||
|
if [[ -n "${remote_url}" ]]; then
|
||||||
|
git remote set-url origin "${remote_url}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
git reset --hard HEAD
|
echo "[jenkins-checkout-source] 尝试 Git 远端: ${remote_url:-origin}"
|
||||||
git fetch --tags --prune origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
|
git fetch --tags --prune origin "+refs/heads/${SOURCE_BRANCH}:refs/remotes/origin/${SOURCE_BRANCH}"
|
||||||
|
}
|
||||||
|
|
||||||
|
add_git_remote_candidate "${GIT_REMOTE_URL}"
|
||||||
|
add_git_remote_candidate "${GIT_REMOTE_FALLBACK_URL}"
|
||||||
|
|
||||||
|
git reset --hard HEAD
|
||||||
|
if [[ "${#GIT_REMOTE_CANDIDATES[@]}" -eq 0 ]]; then
|
||||||
|
fetch_source_branch ""
|
||||||
|
else
|
||||||
|
fetch_ok=0
|
||||||
|
for git_remote_candidate in "${GIT_REMOTE_CANDIDATES[@]}"; do
|
||||||
|
if fetch_source_branch "${git_remote_candidate}"; then
|
||||||
|
GIT_REMOTE_URL="${git_remote_candidate}"
|
||||||
|
fetch_ok=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
echo "[jenkins-checkout-source] Git 远端拉取失败: ${git_remote_candidate}" >&2
|
||||||
|
done
|
||||||
|
if [[ "${fetch_ok}" -ne 1 ]]; then
|
||||||
|
echo "[jenkins-checkout-source] 所有 Git 远端均拉取失败。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ "$(git rev-parse --is-shallow-repository 2>/dev/null || echo false)" == "true" ]]; then
|
if [[ "$(git rev-parse --is-shallow-repository 2>/dev/null || echo false)" == "true" ]]; then
|
||||||
git fetch --unshallow --tags || true
|
git fetch --unshallow --tags || true
|
||||||
@@ -55,4 +98,4 @@ git reset --hard HEAD
|
|||||||
git clean -fd
|
git clean -fd
|
||||||
|
|
||||||
printf "%s\n" "${RESOLVED_COMMIT}" >"${SOURCE_COMMIT_FILE}"
|
printf "%s\n" "${RESOLVED_COMMIT}" >"${SOURCE_COMMIT_FILE}"
|
||||||
echo "[jenkins-checkout-source] 使用源码: branch=${SOURCE_BRANCH} commit=${RESOLVED_COMMIT}"
|
echo "[jenkins-checkout-source] 使用源码: branch=${SOURCE_BRANCH} commit=${RESOLVED_COMMIT} remote=${GIT_REMOTE_URL:-origin}"
|
||||||
|
|||||||
@@ -9,6 +9,28 @@ require_path() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalize_server_aliases() {
|
||||||
|
printf "%s" "${SERVER_ALIASES:-}" | tr ',' ' ' | xargs
|
||||||
|
}
|
||||||
|
|
||||||
|
validate_server_names() {
|
||||||
|
local alias_name
|
||||||
|
if [[ -z "${SERVER_NAME:-}" ]]; then
|
||||||
|
echo "[server-provision] SERVER_NAME 不能为空。" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [[ ! "${SERVER_NAME}" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*$ ]]; then
|
||||||
|
echo "[server-provision] SERVER_NAME 只能填写单个域名或 IP,不能包含空格、路径或协议: ${SERVER_NAME}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
for alias_name in $(normalize_server_aliases); do
|
||||||
|
if [[ ! "${alias_name}" =~ ^[A-Za-z0-9][A-Za-z0-9.-]*$ ]]; then
|
||||||
|
echo "[server-provision] SERVER_ALIASES 只能填写域名或 IP,多个用空格或逗号分隔: ${alias_name}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
run_cmd() {
|
run_cmd() {
|
||||||
echo "+ $*"
|
echo "+ $*"
|
||||||
if [[ "${DRY_RUN}" != "true" ]]; then
|
if [[ "${DRY_RUN}" != "true" ]]; then
|
||||||
@@ -336,10 +358,15 @@ EOF
|
|||||||
|
|
||||||
render_nginx_template() {
|
render_nginx_template() {
|
||||||
local template="$1"
|
local template="$1"
|
||||||
local rendered_brotli
|
local rendered_brotli server_names
|
||||||
rendered_brotli="$(render_nginx_brotli_directives)"
|
rendered_brotli="$(render_nginx_brotli_directives)"
|
||||||
|
server_names="${SERVER_NAME}"
|
||||||
|
if [[ -n "${SERVER_ALIASES:-}" ]]; then
|
||||||
|
server_names="${server_names} $(normalize_server_aliases)"
|
||||||
|
fi
|
||||||
sed \
|
sed \
|
||||||
-e "s/genarrative.example.com/${SERVER_NAME}/g" \
|
-e "s/server_name genarrative.example.com;/server_name ${server_names};/g" \
|
||||||
|
-e "s|/etc/letsencrypt/live/genarrative.example.com/|/etc/letsencrypt/live/${SERVER_NAME}/|g" \
|
||||||
-e "/# __GENARRATIVE_BROTLI_DIRECTIVES__/r /dev/stdin" \
|
-e "/# __GENARRATIVE_BROTLI_DIRECTIVES__/r /dev/stdin" \
|
||||||
-e "/# __GENARRATIVE_BROTLI_DIRECTIVES__/d" \
|
-e "/# __GENARRATIVE_BROTLI_DIRECTIVES__/d" \
|
||||||
"${template}" <<<"${rendered_brotli}"
|
"${template}" <<<"${rendered_brotli}"
|
||||||
@@ -504,6 +531,8 @@ require_path scripts/deploy/maintenance-on.sh
|
|||||||
require_path scripts/deploy/maintenance-off.sh
|
require_path scripts/deploy/maintenance-off.sh
|
||||||
require_path scripts/deploy/maintenance-status.sh
|
require_path scripts/deploy/maintenance-status.sh
|
||||||
|
|
||||||
|
validate_server_names
|
||||||
|
|
||||||
echo "[server-provision] target=${DEPLOY_TARGET}, dry_run=${DRY_RUN}, nginx_config_mode=${NGINX_CONFIG_MODE}, source_commit=$(cat .jenkins-source-commit)"
|
echo "[server-provision] target=${DEPLOY_TARGET}, dry_run=${DRY_RUN}, nginx_config_mode=${NGINX_CONFIG_MODE}, source_commit=$(cat .jenkins-source-commit)"
|
||||||
|
|
||||||
run_cmd id
|
run_cmd id
|
||||||
|
|||||||
Reference in New Issue
Block a user