a2ee879fc8
Co-authored-by: kdletters <kdletters@qq.com> Reviewed-on: https://git.genarrative.world/git/GenarrativeAI/Genarrative/pulls/103 Co-authored-by: Linghong <ink29535@proton.me> Co-committed-by: Linghong <ink29535@proton.me>
168 lines
11 KiB
Plaintext
168 lines
11 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-Api-Build', description: 'API 构建流水线作业名')
|
|
string(name: 'BUILD_NUMBER_TO_DEPLOY', defaultValue: '', description: '要复制归档产物的上游构建号')
|
|
booleanParam(name: 'INCLUDE_PINGORA_GATEWAY', defaultValue: true, description: '上游构建是否包含 Pingora 影子网关产物;release 发布默认必须包含')
|
|
booleanParam(name: 'KEEP_MAINTENANCE_MODE', defaultValue: false, description: '发布成功且 readiness 通过后是否继续保持维护模式;默认退出维护')
|
|
string(name: 'RELEASE_ROOT', defaultValue: '/opt/genarrative/releases', description: '生产 release 根目录')
|
|
string(name: 'CURRENT_LINK', defaultValue: '/opt/genarrative/current', description: '当前版本软链接')
|
|
string(name: 'SERVICE_NAME', defaultValue: 'genarrative-api.service', description: 'systemd 服务名')
|
|
string(name: 'HEALTH_URL', defaultValue: 'http://127.0.0.1:8082/readyz', description: '本机 readiness 检查地址')
|
|
string(name: 'API_ENV_FILE', defaultValue: '/etc/genarrative/api-server.env', description: 'api-server 环境文件')
|
|
string(name: 'WORKER_ENV_FILE', defaultValue: '/etc/genarrative/external-generation-worker.env', description: '外部生成 worker 环境文件')
|
|
string(name: 'CONTROLLER_ENV_FILE', defaultValue: '/etc/genarrative/external-generation-controller.env', description: '外部生成 controller 环境文件')
|
|
string(name: 'BGFILTER_WORKER_ENV_FILE', defaultValue: '/etc/genarrative/bgfilter-worker.env', description: 'BgFilter worker 环境文件')
|
|
string(name: 'DATABASE', defaultValue: 'genarrative-prod', description: 'api-server 连接的 SpacetimeDB database')
|
|
string(name: 'SPACETIME_SERVER_URL', defaultValue: 'http://127.0.0.1:3101', description: 'api-server 连接的 SpacetimeDB server URL')
|
|
}
|
|
|
|
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 不能为空。')
|
|
}
|
|
if (!params.DATABASE?.trim()) {
|
|
error('DATABASE 不能为空。')
|
|
}
|
|
if (!(params.DATABASE.trim() ==~ /^[a-z0-9]+(-[a-z0-9]+)*$/)) {
|
|
error("DATABASE 必须匹配 ^[a-z0-9]+(-[a-z0-9]+)*\$: ${params.DATABASE}")
|
|
}
|
|
[API_ENV_FILE: params.API_ENV_FILE, WORKER_ENV_FILE: params.WORKER_ENV_FILE, CONTROLLER_ENV_FILE: params.CONTROLLER_ENV_FILE, BGFILTER_WORKER_ENV_FILE: params.BGFILTER_WORKER_ENV_FILE].each { name, value ->
|
|
def envFile = value?.trim()
|
|
if (!envFile || envFile == '/' || !(envFile ==~ /^\/(?!.*\.\.)[A-Za-z0-9._\/-]+$/)) {
|
|
error("${name} 必须是 Linux 绝对路径且不能包含 ..: ${envFile}")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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}/api-server,build/${params.BUILD_VERSION}/api-server.sha256,build/${params.BUILD_VERSION}/release-manifest.json,build/${params.BUILD_VERSION}/scripts/database-backup-to-oss.mjs,build/${params.BUILD_VERSION}/scripts/ops/production-health-patrol.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-current-release-audit.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-direct-rehearsal-status.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-cutover-status-snapshot.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-cutover-evidence-bundle.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-cutover-command-evidence.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-cutover-evidence-verify.mjs,build/${params.BUILD_VERSION}/scripts/ops/pingora-cutover-evidence-audit.mjs,build/${params.BUILD_VERSION}/scripts/check-production-health-patrol-env.mjs,build/${params.BUILD_VERSION}/scripts/check-pingora-release-readiness.mjs,build/${params.BUILD_VERSION}/scripts/check-pingora-direct-preflight.mjs,build/${params.BUILD_VERSION}/scripts/check-pingora-direct-live.mjs,build/${params.BUILD_VERSION}/scripts/check-pingora-canary-live.mjs,build/${params.BUILD_VERSION}/scripts/check-pingora-canary-access-log-parity.mjs,build/${params.BUILD_VERSION}/scripts/deploy/production-api-deploy.sh,build/${params.BUILD_VERSION}/scripts/deploy/maintenance-on.sh,build/${params.BUILD_VERSION}/scripts/deploy/maintenance-off.sh,build/${params.BUILD_VERSION}/scripts/deploy/pingora-direct-enable.sh,build/${params.BUILD_VERSION}/scripts/deploy/pingora-direct-rollback.sh,build/${params.BUILD_VERSION}/scripts/deploy/pingora-realpath-canary-enable.sh,build/${params.BUILD_VERSION}/scripts/deploy/pingora-realpath-canary-disable.sh,build/${params.BUILD_VERSION}/scripts/deploy/pingora-health-patrol-env-switch.mjs,build/${params.BUILD_VERSION}/scripts/deploy/pingora-gateway-env-shadow-switch.mjs,build/${params.BUILD_VERSION}/scripts/deploy/pingora-tls-cert-sync.mjs,build/${params.BUILD_VERSION}/deploy/systemd/**,build/${params.BUILD_VERSION}/deploy/nginx/**,build/${params.BUILD_VERSION}/deploy/env/**,build/${params.BUILD_VERSION}/deploy/pingora/**",
|
|
target: '.',
|
|
fingerprintArtifacts: true
|
|
)
|
|
script {
|
|
if (params.INCLUDE_PINGORA_GATEWAY) {
|
|
copyArtifacts(
|
|
projectName: params.BUILD_JOB_NAME,
|
|
selector: specific(params.BUILD_NUMBER_TO_DEPLOY),
|
|
filter: "build/${params.BUILD_VERSION}/pingora-gateway,build/${params.BUILD_VERSION}/pingora-gateway.sha256",
|
|
target: '.',
|
|
fingerprintArtifacts: true
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
stage('Deploy Api') {
|
|
agent {
|
|
label "${params.DEPLOY_TARGET == 'development' ? 'linux && genarrative-dev-deploy' : 'linux && genarrative-release-deploy'}"
|
|
}
|
|
steps {
|
|
sh '''
|
|
bash -lc '
|
|
set -euo pipefail
|
|
chmod +x "build/${BUILD_VERSION}/scripts/deploy/production-api-deploy.sh" "build/${BUILD_VERSION}/scripts/deploy/maintenance-on.sh" "build/${BUILD_VERSION}/scripts/deploy/maintenance-off.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-direct-enable.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-direct-rollback.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-realpath-canary-enable.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-realpath-canary-disable.sh" "build/${BUILD_VERSION}/scripts/deploy/pingora-health-patrol-env-switch.mjs" "build/${BUILD_VERSION}/scripts/deploy/pingora-gateway-env-shadow-switch.mjs" "build/${BUILD_VERSION}/scripts/deploy/pingora-tls-cert-sync.mjs"
|
|
pingora_deploy_args=()
|
|
maintenance_deploy_args=()
|
|
if [[ "${INCLUDE_PINGORA_GATEWAY:-false}" == "true" ]]; then
|
|
pingora_deploy_args+=(--require-pingora-gateway)
|
|
fi
|
|
if [[ "${KEEP_MAINTENANCE_MODE:-false}" == "true" ]]; then
|
|
maintenance_deploy_args+=(--keep-maintenance-mode)
|
|
fi
|
|
"build/${BUILD_VERSION}/scripts/deploy/production-api-deploy.sh" \
|
|
--source-dir "build/${BUILD_VERSION}" \
|
|
--version "${BUILD_VERSION}" \
|
|
--release-root "${RELEASE_ROOT}" \
|
|
--current-link "${CURRENT_LINK}" \
|
|
--service "${SERVICE_NAME}" \
|
|
"${pingora_deploy_args[@]}" \
|
|
"${maintenance_deploy_args[@]}" \
|
|
--health-url "${HEALTH_URL}" \
|
|
--api-env-file "${API_ENV_FILE:-/etc/genarrative/api-server.env}" \
|
|
--worker-env-file "${WORKER_ENV_FILE:-/etc/genarrative/external-generation-worker.env}" \
|
|
--controller-env-file "${CONTROLLER_ENV_FILE:-/etc/genarrative/external-generation-controller.env}" \
|
|
--bgfilter-worker-env-file "${BGFILTER_WORKER_ENV_FILE:-/etc/genarrative/bgfilter-worker.env}" \
|
|
--database "${DATABASE}" \
|
|
--spacetime-server-url "${SPACETIME_SERVER_URL:-http://127.0.0.1:3101}"
|
|
'
|
|
'''
|
|
}
|
|
}
|
|
}
|
|
|
|
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: 'INCLUDE_PINGORA_GATEWAY', value: String.valueOf(params.INCLUDE_PINGORA_GATEWAY)),
|
|
string(name: 'KEEP_MAINTENANCE_MODE', value: String.valueOf(params.KEEP_MAINTENANCE_MODE)),
|
|
string(name: 'SUMMARY', value: 'API 发布流水线结束'),
|
|
]
|
|
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 "API 发布完成: version=${params.BUILD_VERSION}"
|
|
}
|
|
}
|
|
}
|