This commit is contained in:
69
jenkins/Jenkinsfile.build
Normal file
69
jenkins/Jenkinsfile.build
Normal file
@@ -0,0 +1,69 @@
|
||||
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')
|
||||
booleanParam(name: 'RUN_NPM_CI', defaultValue: false, description: '构建前是否执行 npm ci')
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
|
||||
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
|
||||
'
|
||||
'''
|
||||
|
||||
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}"
|
||||
test -d "build/${env.EFFECTIVE_BUILD_VERSION}"
|
||||
'
|
||||
"""
|
||||
|
||||
archiveArtifacts artifacts: "build/${env.EFFECTIVE_BUILD_VERSION}/**", fingerprint: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
echo "构建完成,版本号: ${env.EFFECTIVE_BUILD_VERSION}"
|
||||
}
|
||||
}
|
||||
}
|
||||
95
jenkins/Jenkinsfile.build-and-deploy
Normal file
95
jenkins/Jenkinsfile.build-and-deploy
Normal file
@@ -0,0 +1,95 @@
|
||||
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: 'WEB_PORT', defaultValue: '80', description: '发布包内静态网站端口,默认 80')
|
||||
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
|
||||
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()
|
||||
// 记录当前构建节点名,部署阶段必须回到同一节点读取本地 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
|
||||
'
|
||||
'''
|
||||
|
||||
script {
|
||||
// 是否重装依赖交给流水线参数决定,避免每次构建都重复执行 npm ci。
|
||||
if (params.RUN_NPM_CI) {
|
||||
sh 'bash -lc "npm ci"'
|
||||
}
|
||||
}
|
||||
|
||||
sh """
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
# 构建并部署流水线显式透传 Web 端口,确保部署包默认监听 80,同时允许 Jenkins 参数覆盖。
|
||||
npm run deploy:rust:remote -- --skip-upload --name "${env.EFFECTIVE_BUILD_VERSION}" --web-port "${params.WEB_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),
|
||||
booleanParam(name: 'CLEAR_DATABASE', value: params.CLEAR_DATABASE),
|
||||
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}"
|
||||
}
|
||||
}
|
||||
}
|
||||
109
jenkins/Jenkinsfile.deploy
Normal file
109
jenkins/Jenkinsfile.deploy
Normal file
@@ -0,0 +1,109 @@
|
||||
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: '固定部署目录')
|
||||
booleanParam(name: 'CLEAR_DATABASE', defaultValue: false, description: '部署时是否清空 SpacetimeDB 数据后再发布 wasm')
|
||||
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 不能为空。')
|
||||
}
|
||||
|
||||
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 {
|
||||
dir("${params.SOURCE_WORKSPACE_ROOT}") {
|
||||
sh """
|
||||
bash -lc '
|
||||
set -euo pipefail
|
||||
test -d "build/${params.BUILD_VERSION}"
|
||||
chmod +x scripts/jenkins-deploy-release.sh
|
||||
deploy_args=(
|
||||
--source-dir "build/${params.BUILD_VERSION}"
|
||||
--deploy-dir "${params.DEPLOY_DIRECTORY}"
|
||||
)
|
||||
if [[ "${params.CLEAR_DATABASE}" == "true" ]]; then
|
||||
deploy_args+=(--clear-database)
|
||||
fi
|
||||
if [[ "${params.RUN_DEPLOY_HOOKS_WITH_SUDO}" == "true" ]]; then
|
||||
deploy_args+=(--hook-with-sudo)
|
||||
fi
|
||||
# 只部署上游已构建好的版本目录,避免部署阶段再次构建产生漂移。
|
||||
./scripts/jenkins-deploy-release.sh "\${deploy_args[@]}"
|
||||
'
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
post {
|
||||
success {
|
||||
echo "部署完成,版本号: ${params.BUILD_VERSION},上游作业: ${env.UPSTREAM_JOB_NAME}"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user