Files
Genarrative/jenkins/Jenkinsfile.deploy
kdletters 1ccb8a710d
Some checks failed
CI / verify (push) Has been cancelled
Add migration token parameters to Jenkins deploy flows
2026-04-30 10:48:58 +08:00

156 lines
6.8 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
pipeline {
agent none
options {
disableConcurrentBuilds()
timestamps()
}
parameters {
string(name: 'SOURCE_NODE_NAME', defaultValue: '', description: '上游构建节点名')
string(name: 'SOURCE_WORKSPACE_ROOT', defaultValue: '', description: '上游源码根目录')
string(name: 'BUILD_VERSION', defaultValue: '', description: '待部署版本号')
string(name: 'DEPLOY_DIRECTORY', defaultValue: '/var/lib/jenkins/deploy/Genarrative', description: '固定部署目录')
string(name: 'WEB_PORT', defaultValue: '25001', description: '静态网站监听端口,默认 25001上游构建并部署流水线会透传同名参数')
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
booleanParam(name: 'MIGRATE_ON_CONFLICT', defaultValue: true, description: '普通发布遇到 SpacetimeDB schema 冲突时自动导出、清库发布并导入回灌')
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 执行')
string(name: 'EXPECTED_UPSTREAM_JOB', defaultValue: '', description: '允许触发本作业的上游作业名')
}
stages {
stage('校验触发来源') {
agent {
label 'built-in'
}
steps {
script {
// 部署流水线允许手动启动;如存在上游触发原因,则继续执行上游作业名门禁。
// Pipeline 的 build 步骤通常会把下游触发原因记录成 BuildUpstreamCause
// 直接只查经典 UpstreamCause 会把真实的上游触发误判成“人工执行”。
def pipelineUpstreamCauses = currentBuild.getBuildCauses('org.jenkinsci.plugins.workflow.support.steps.build.BuildUpstreamCause')
def classicUpstreamCauses = currentBuild.getBuildCauses('hudson.model.Cause$UpstreamCause')
def upstreamCause = null
if (pipelineUpstreamCauses && !pipelineUpstreamCauses.isEmpty()) {
upstreamCause = pipelineUpstreamCauses[0]
} else if (classicUpstreamCauses && !classicUpstreamCauses.isEmpty()) {
upstreamCause = classicUpstreamCauses[0]
}
def actualUpstreamJob = upstreamCause?.upstreamProject ?: ''
def expectedUpstreamJob = params.EXPECTED_UPSTREAM_JOB?.trim()
def allowedUpstreamJob = env.GENARRATIVE_ALLOWED_UPSTREAM_JOB?.trim()
if (!params.BUILD_VERSION?.trim()) {
error('BUILD_VERSION 不能为空。')
}
if (!params.SOURCE_WORKSPACE_ROOT?.trim()) {
error('SOURCE_WORKSPACE_ROOT 不能为空。')
}
if (!params.SOURCE_NODE_NAME?.trim()) {
error('SOURCE_NODE_NAME 不能为空。')
}
def webPort = params.WEB_PORT?.trim()
if (!webPort) {
error('WEB_PORT 不能为空。')
}
if (!(webPort ==~ /^[0-9]+$/)) {
error("WEB_PORT 必须是数字端口,当前值: ${webPort}")
}
if (webPort.length() > 5) {
error("WEB_PORT 必须在 1-65535 之间,当前值: ${webPort}")
}
def parsedWebPort = webPort.toInteger()
if (parsedWebPort < 1 || parsedWebPort > 65535) {
error("WEB_PORT 必须在 1-65535 之间,当前值: ${webPort}")
}
// 部署脚本只接收校验后的端口值,避免手工参数前后空格传到 Bash。
env.EFFECTIVE_WEB_PORT = webPort
if (upstreamCause && !actualUpstreamJob?.trim()) {
error('无法从上游触发原因中解析作业名,请检查 Jenkins Pipeline Build Step 插件版本与触发链。')
}
if (actualUpstreamJob && expectedUpstreamJob && actualUpstreamJob != expectedUpstreamJob) {
error("上游作业校验失败,期望 ${expectedUpstreamJob},实际 ${actualUpstreamJob}")
}
if (actualUpstreamJob && allowedUpstreamJob && actualUpstreamJob != allowedUpstreamJob) {
error("环境门禁校验失败,仅允许 ${allowedUpstreamJob} 触发,实际 ${actualUpstreamJob}")
}
env.UPSTREAM_JOB_NAME = actualUpstreamJob ?: 'MANUAL'
}
}
}
stage('部署指定版本') {
agent {
label "${params.SOURCE_NODE_NAME}"
}
steps {
script {
// 部署脚本使用当前 Deploy 作业 checkout 出来的版本,避免重放旧 build 目录时继续执行旧脚本。
env.DEPLOY_SCRIPT_PATH = "${pwd()}/scripts/jenkins-deploy-release.sh"
}
dir("${params.SOURCE_WORKSPACE_ROOT}") {
sh """
bash -lc '
set -euo pipefail
test -d "build/${params.BUILD_VERSION}"
deploy_script="${env.DEPLOY_SCRIPT_PATH}"
chmod +x "\${deploy_script}"
deploy_args=(
--source-dir "build/${params.BUILD_VERSION}"
--deploy-dir "${params.DEPLOY_DIRECTORY}"
--web-port "${env.EFFECTIVE_WEB_PORT}"
)
if [[ "${params.CLEAR_DATABASE}" == "true" ]]; then
deploy_args+=(--clear-database)
fi
if [[ "${params.MIGRATE_ON_CONFLICT}" == "true" ]]; then
deploy_args+=(--migrate-on-conflict)
else
deploy_args+=(--no-migrate-on-conflict)
fi
if [[ -n "${params.MIGRATION_DIRECTORY}" ]]; then
deploy_args+=(--migration-dir "${params.MIGRATION_DIRECTORY}")
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
deploy_args+=(--hook-with-sudo)
fi
# 只部署上游已构建好的版本目录,避免部署阶段再次构建产生漂移。
"\${deploy_script}" "\${deploy_args[@]}"
'
"""
}
}
}
}
post {
success {
echo "部署完成,版本号: ${params.BUILD_VERSION},上游作业: ${env.UPSTREAM_JOB_NAME}"
}
}
}