Files
Genarrative/jenkins/Jenkinsfile.build-and-deploy
高物 805d6f8cae
Some checks failed
CI / verify (push) Has been cancelled
1
2026-04-30 00:51:44 +08:00

164 lines
7.7 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: 'AGENT_LABEL', defaultValue: 'built-in', description: '构建节点标签')
string(name: 'GENARRATIVE_WORKSPACE_ROOT', defaultValue: '', description: '源码根目录,留空则使用当前 Jenkins 工作区')
string(name: 'BUILD_VERSION', defaultValue: '', description: '发布版本号,留空则使用 Jenkins BUILD_NUMBER')
string(name: 'DATABASE', defaultValue: 'genarrative-pipeline-local-test', description: '发布包默认 SpacetimeDB database')
string(name: 'API_PORT', defaultValue: '8082', description: '发布包内 api-server 端口')
string(name: 'WEB_PORT', defaultValue: '25001', description: '发布包内静态网站端口,默认 25001')
string(name: 'SPACETIME_PORT', defaultValue: '3101', description: '发布包内本地 SpacetimeDB 端口')
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>')
booleanParam(name: 'RUN_NPM_CI', defaultValue: false, description: '构建前是否执行 npm ci')
string(name: 'DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Deploy', description: '部署流水线作业名')
string(name: 'DEPLOY_DIRECTORY', defaultValue: '/var/lib/jenkins/deploy/Genarrative', description: '固定部署目录')
booleanParam(name: 'RUN_DEPLOY_HOOKS_WITH_SUDO', defaultValue: true, description: 'start.sh / stop.sh 是否通过 sudo -n 执行')
}
stages {
stage('构建发布包') {
agent {
label "${params.AGENT_LABEL}"
}
steps {
script {
// 统一在脚本块里计算版本号,避免 declarative environment 对表达式求值不一致。
env.EFFECTIVE_BUILD_VERSION = params.BUILD_VERSION?.trim() ? params.BUILD_VERSION.trim() : env.BUILD_NUMBER
// 允许 Jenkins Job 直接指定固定源码目录,未指定时回退到当前工作区。
env.WORKSPACE_ROOT = params.GENARRATIVE_WORKSPACE_ROOT?.trim() ? params.GENARRATIVE_WORKSPACE_ROOT.trim() : pwd()
def database = params.DATABASE?.trim()
if (!database) {
error('DATABASE 不能为空。')
}
if (!(database ==~ /^[0-9A-Za-z.-]+$/)) {
error("DATABASE 只能包含数字、字母、点和短横线,不能包含下划线,当前值: ${database}")
}
env.EFFECTIVE_DATABASE = database
def apiPort = params.API_PORT?.trim()
if (!apiPort) {
error('API_PORT 不能为空。')
}
if (!(apiPort ==~ /^[0-9]+$/)) {
error("API_PORT 必须是数字端口,当前值: ${apiPort}")
}
if (apiPort.length() > 5) {
error("API_PORT 必须在 1-65535 之间,当前值: ${apiPort}")
}
def parsedApiPort = apiPort.toInteger()
if (parsedApiPort < 1 || parsedApiPort > 65535) {
error("API_PORT 必须在 1-65535 之间,当前值: ${apiPort}")
}
env.EFFECTIVE_API_PORT = apiPort
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}")
}
// 后续构建与下游部署都使用校验后的同一端口值,避免参数空格导致上下游不一致。
env.EFFECTIVE_WEB_PORT = webPort
def spacetimePort = params.SPACETIME_PORT?.trim()
if (!spacetimePort) {
error('SPACETIME_PORT 不能为空。')
}
if (!(spacetimePort ==~ /^[0-9]+$/)) {
error("SPACETIME_PORT 必须是数字端口,当前值: ${spacetimePort}")
}
if (spacetimePort.length() > 5) {
error("SPACETIME_PORT 必须在 1-65535 之间,当前值: ${spacetimePort}")
}
def parsedSpacetimePort = spacetimePort.toInteger()
if (parsedSpacetimePort < 1 || parsedSpacetimePort > 65535) {
error("SPACETIME_PORT 必须在 1-65535 之间,当前值: ${spacetimePort}")
}
env.EFFECTIVE_SPACETIME_PORT = spacetimePort
// 记录当前构建节点名,部署阶段必须回到同一节点读取本地 build 目录。
env.SOURCE_NODE_NAME = env.NODE_NAME
}
dir("${env.WORKSPACE_ROOT}") {
checkout scm
sh '''
bash -lc '
set -euo pipefail
# 构建前清理工作区内的 Git 变更和未跟踪文件,避免复用固定源码目录时受到上次构建残留影响。
# 这里不使用 -x避免删除 node_modules 等忽略目录后与 RUN_NPM_CI=false 的配置冲突。
git reset --hard HEAD
git clean -fd
rm -rf "build/${EFFECTIVE_BUILD_VERSION}"
'
'''
script {
// 是否重装依赖交给流水线参数决定,避免每次构建都重复执行 npm ci。
if (params.RUN_NPM_CI) {
sh 'bash -lc "npm ci"'
}
}
sh """
bash -lc '
set -euo pipefail
# 构建并部署流水线显式透传本地测试参数,避免发布包回退到默认库名或端口。
npm run deploy:rust:remote -- --skip-upload \
--name "${env.EFFECTIVE_BUILD_VERSION}" \
--database "${env.EFFECTIVE_DATABASE}" \
--api-port "${env.EFFECTIVE_API_PORT}" \
--web-port "${env.EFFECTIVE_WEB_PORT}" \
--spacetime-port "${env.EFFECTIVE_SPACETIME_PORT}"
test -d "build/${env.EFFECTIVE_BUILD_VERSION}"
'
"""
archiveArtifacts artifacts: "build/${env.EFFECTIVE_BUILD_VERSION}/**", fingerprint: true
}
}
}
stage('触发部署') {
steps {
// 本阶段没有声明 agent确保触发下游前已经释放构建节点避免单执行器死锁。
build job: params.DEPLOY_JOB_NAME,
wait: true,
propagate: true,
parameters: [
string(name: 'SOURCE_NODE_NAME', value: env.SOURCE_NODE_NAME),
string(name: 'SOURCE_WORKSPACE_ROOT', value: env.WORKSPACE_ROOT),
string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION),
string(name: 'DEPLOY_DIRECTORY', value: params.DEPLOY_DIRECTORY),
string(name: 'WEB_PORT', value: env.EFFECTIVE_WEB_PORT),
booleanParam(name: 'CLEAR_DATABASE', value: params.CLEAR_DATABASE),
booleanParam(name: 'MIGRATE_ON_CONFLICT', value: params.MIGRATE_ON_CONFLICT),
string(name: 'MIGRATION_DIRECTORY', value: params.MIGRATION_DIRECTORY),
booleanParam(name: 'RUN_DEPLOY_HOOKS_WITH_SUDO', value: params.RUN_DEPLOY_HOOKS_WITH_SUDO),
string(name: 'EXPECTED_UPSTREAM_JOB', value: env.JOB_NAME),
]
}
}
}
post {
success {
echo "构建并部署完成,版本号: ${env.EFFECTIVE_BUILD_VERSION}"
}
}
}