16ff10111f
Project CI / AI game creator shell Rust crates (push) Successful in 1m26s
Project CI / AI game creator shell Rust smoke (push) Successful in 1m53s
Project CI / Backend tests (push) Successful in 3m47s
Project CI / AI game creator shell Rust lane 2/2 (push) Has been cancelled
Project CI / Repository checks (push) Has been cancelled
Project CI / Frontend tests (push) Has been cancelled
Project CI / AI game creator shell web tests (push) Has been cancelled
Project CI / AI game creator shell Rust lane 1/2 (push) Has been cancelled
Project CI / Native shell tests (push) Has been cancelled
- production-api-deploy.sh 新增 --keep-releases(默认 2):发布成功后保留 current 目标与最近 1 个历史 release,只清理同时含 api-server 或 web 标记的旧目录,清理失败只告警、不改变发布结论 - production-api-deploy.sh 新增 prune_old_releases 与参数校验:非整数或小于 1 的 --keep-releases 在任何动作之前失败 - check-production-api-deploy 增加默认值、显式值和非法值三类夹具,并断言非法参数不会提升 release - check-production-ops 增加 release 保留策略与三个部署 Job 暂存清理的合同 - Api Deploy、Web Deploy、Stdb Publish 三个 Jenkinsfile 在部署或发布成功后只保留最近 2 个 build/<version> 暂存,失败时保留暂存便于诊断和重跑 - 内联清理片段避开 Groovy 反斜杠转义,并在 set -euo pipefail 下对空 build 目录安全(if [ -d build ] 守卫加兜底) - 开发运维文档补充 release 与 Jenkins 暂存的保留上限口径 - pitfalls 记录 dev 根盘写满的两处根因(发布与暂存无上限、JNLP 幽灵 agent 刷 syslog)以及 Jenkins 内联 shell 的两个坑
137 lines
6.1 KiB
Plaintext
137 lines
6.1 KiB
Plaintext
pipeline {
|
|
agent none
|
|
|
|
options {
|
|
disableConcurrentBuilds()
|
|
skipDefaultCheckout(true)
|
|
buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20'))
|
|
}
|
|
|
|
parameters {
|
|
choice(name: 'DEPLOY_TARGET', choices: ['development', 'release'], description: '逻辑部署目标;development 使用当前 Linux 开发/构建/开发部署 agent')
|
|
booleanParam(name: 'CONFIRM_RELEASE_DEPLOY_AGENT', defaultValue: false, description: '确认 release 目标已有独立 release 部署 agent;当前 Linux 开发/构建/开发部署 agent 不可冒充 release 部署机')
|
|
string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '上游构建源码分支')
|
|
string(name: 'COMMIT_HASH', defaultValue: '', description: '上游构建源码 commit')
|
|
string(name: 'NOTIFICATION_EMAILS', defaultValue: '', description: '本次运行追加通知邮箱;会与 Jenkins Secret Text 凭据 genarrative-notification-emails 合并发送')
|
|
string(name: 'BUILD_VERSION', defaultValue: '', description: '待发布版本号')
|
|
string(name: 'BUILD_JOB_NAME', defaultValue: 'Genarrative-Web-Build', description: 'Web 构建流水线作业名')
|
|
string(name: 'BUILD_NUMBER_TO_DEPLOY', defaultValue: '', description: '要复制归档产物的上游构建号')
|
|
string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: '生产 release 根目录')
|
|
string(name: 'CURRENT_LINK', defaultValue: '/opt/genarrative/current', description: '当前版本软链接')
|
|
string(name: 'WEB_LINK', defaultValue: '/srv/genarrative/web', description: 'Nginx 静态站点软链接')
|
|
}
|
|
|
|
stages {
|
|
stage('Prepare') {
|
|
agent {
|
|
label 'linux && genarrative-build'
|
|
}
|
|
steps {
|
|
script {
|
|
if (params.DEPLOY_TARGET == 'release' && !params.CONFIRM_RELEASE_DEPLOY_AGENT) {
|
|
error('release 部署需要先配置独立 release 部署 agent,并勾选 CONFIRM_RELEASE_DEPLOY_AGENT。当前 Linux 开发/构建/开发部署 agent 不能执行 release 部署。')
|
|
}
|
|
if (!params.BUILD_VERSION?.trim()) {
|
|
error('BUILD_VERSION 不能为空。')
|
|
}
|
|
if (!params.BUILD_JOB_NAME?.trim()) {
|
|
error('BUILD_JOB_NAME 不能为空。')
|
|
}
|
|
if (!params.BUILD_NUMBER_TO_DEPLOY?.trim()) {
|
|
error('BUILD_NUMBER_TO_DEPLOY 不能为空。')
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Fetch Artifact') {
|
|
agent {
|
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-dev-deploy' : 'linux && genarrative-release-deploy'}"
|
|
}
|
|
steps {
|
|
copyArtifacts(
|
|
projectName: params.BUILD_JOB_NAME,
|
|
selector: specific(params.BUILD_NUMBER_TO_DEPLOY),
|
|
filter: "build/${params.BUILD_VERSION}/web.tar.gz,build/${params.BUILD_VERSION}/web.tar.gz.sha256,build/${params.BUILD_VERSION}/release-manifest.json,scripts/deploy/production-web-deploy.sh",
|
|
target: '.',
|
|
fingerprintArtifacts: true
|
|
)
|
|
sh '''
|
|
bash -lc '
|
|
set -euo pipefail
|
|
|
|
test -f "build/${BUILD_VERSION}/web.tar.gz"
|
|
test -f "build/${BUILD_VERSION}/web.tar.gz.sha256"
|
|
test -f "build/${BUILD_VERSION}/release-manifest.json"
|
|
echo "[web-deploy] 已从 Jenkins 构建归档获取 Web 发布包: build/${BUILD_VERSION}/web.tar.gz"
|
|
'
|
|
'''
|
|
}
|
|
}
|
|
|
|
stage('Deploy Web') {
|
|
agent {
|
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-dev-deploy' : 'linux && genarrative-release-deploy'}"
|
|
}
|
|
steps {
|
|
sh '''
|
|
bash -lc '
|
|
set -euo pipefail
|
|
chmod +x scripts/deploy/production-web-deploy.sh
|
|
scripts/deploy/production-web-deploy.sh \
|
|
--source-dir "build/${BUILD_VERSION}" \
|
|
--version "${BUILD_VERSION}" \
|
|
--release-root "${RELEASE_ROOT}" \
|
|
--current-link "${CURRENT_LINK}" \
|
|
--web-link "${WEB_LINK}"
|
|
# 只保留最近 2 个 build/<version> 暂存:每次部署都用 copyArtifacts 重新落一份,
|
|
# 历史暂存不参与发布、只占目标机磁盘;部署失败时不清理,便于诊断和重跑。
|
|
if [ -d build ]; then
|
|
stale_list="$(ls -1dt build/*/ 2>/dev/null | tail -n +3 || true)"
|
|
while IFS= read -r stale_staging; do
|
|
[ -n "${stale_staging}" ] || continue
|
|
echo "[staging-cleanup] 清理历史构建暂存: ${stale_staging}"
|
|
rm -rf --one-file-system "${stale_staging}"
|
|
done <<< "${stale_list}"
|
|
fi
|
|
'
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
post {
|
|
always {
|
|
script {
|
|
def notificationParameters = [
|
|
string(name: 'SOURCE_JOB_NAME', value: env.JOB_NAME),
|
|
string(name: 'SOURCE_BUILD_NUMBER', value: env.BUILD_NUMBER),
|
|
string(name: 'SOURCE_BUILD_URL', value: env.BUILD_URL ?: ''),
|
|
string(name: 'SOURCE_RESULT', value: currentBuild.currentResult ?: 'UNKNOWN'),
|
|
string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH ?: ''),
|
|
string(name: 'SOURCE_COMMIT', value: env.SOURCE_COMMIT ?: (params.COMMIT_HASH ?: '')),
|
|
string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION ?: (params.BUILD_VERSION ?: '')),
|
|
string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET ?: ''),
|
|
string(name: 'DATABASE', value: params.DATABASE ?: ''),
|
|
string(name: 'SUMMARY', value: 'Web 发布流水线结束'),
|
|
]
|
|
def notificationRecipients = params.NOTIFICATION_EMAILS?.trim()
|
|
if (notificationRecipients) {
|
|
notificationParameters.add(string(name: 'EMAIL_RECIPIENTS', value: notificationRecipients))
|
|
}
|
|
try {
|
|
build job: 'Genarrative-Notify-Email',
|
|
wait: false,
|
|
propagate: false,
|
|
parameters: notificationParameters
|
|
} catch (error) {
|
|
echo "邮件通知触发失败: ${error.message}"
|
|
}
|
|
}
|
|
}
|
|
success {
|
|
echo "Web 发布完成: version=${params.BUILD_VERSION}"
|
|
}
|
|
}
|
|
}
|