pipeline { agent none options { disableConcurrentBuilds() skipDefaultCheckout(true) buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20')) } triggers { cron('0 4 * * *') } environment { GIT_REMOTE_URL = 'https://git.genarrative.world/GenarrativeAI/Genarrative.git' } parameters { string(name: 'SOURCE_BRANCH', defaultValue: 'master', description: '源码分支,默认 master 最新提交') string(name: 'COMMIT_HASH', defaultValue: '', description: '可选,指定属于 SOURCE_BRANCH 的 Git commit') string(name: 'BUILD_VERSION', defaultValue: '', description: '发布版本号,留空则使用 Jenkins BUILD_NUMBER') booleanParam(name: 'RUN_NPM_CI', defaultValue: true, description: 'Web 构建前是否执行 npm ci') string(name: 'NOTIFICATION_EMAILS', defaultValue: '', description: '本次运行追加通知邮箱;会与 Jenkins Secret Text 凭据 genarrative-notification-emails 合并发送') string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', defaultValue: '', description: '可选,透传给 Stdb module 构建的迁移 bootstrap secret 凭据 ID;留空则由 Stdb 构建自动生成') string(name: 'WEB_BUILD_JOB_NAME', defaultValue: 'Genarrative-Web-Build', description: 'Web 构建流水线作业名') string(name: 'API_BUILD_JOB_NAME', defaultValue: 'Genarrative-Api-Build', description: 'API 构建流水线作业名') string(name: 'STDB_BUILD_JOB_NAME', defaultValue: 'Genarrative-Stdb-Module-Build', description: 'Stdb 构建流水线作业名') string(name: 'WEB_DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Web-Deploy', description: 'Web 发布流水线作业名') string(name: 'API_DEPLOY_JOB_NAME', defaultValue: 'Genarrative-Api-Deploy', description: 'API 发布流水线作业名') string(name: 'STDB_PUBLISH_JOB_NAME', defaultValue: 'Genarrative-Stdb-Module-Publish', description: 'Stdb 发布流水线作业名') 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: 'DATABASE', defaultValue: 'genarrative-prod', description: '生产 SpacetimeDB database') string(name: 'SPACETIME_SERVER_URL', defaultValue: 'http://127.0.0.1:3101', description: 'Stdb 发布目标 URL;默认避开本机 Git/Web 使用的 3000 端口') string(name: 'SPACETIME_ROOT_DIR', defaultValue: '/stdb', description: 'Stdb 发布使用的 spacetime CLI root-dir') string(name: 'SPACETIME_RUN_AS_USER', defaultValue: 'spacetimedb', description: 'Stdb 发布使用的本机用户') } stages { stage('Resolve Source') { agent { label 'linux && genarrative-build' } steps { checkout([ $class: 'GitSCM', branches: [[name: "*/${params.SOURCE_BRANCH}"]], doGenerateSubmoduleConfigurations: false, extensions: [ [$class: 'CleanBeforeCheckout'], [$class: 'CloneOption', shallow: true, depth: 1, noTags: true, timeout: 30], ], userRemoteConfigs: [[url: "${GIT_REMOTE_URL}"]], ]) sh ''' bash -lc ' set -euo pipefail chmod +x scripts/jenkins-checkout-source.sh SOURCE_BRANCH="${SOURCE_BRANCH:-master}" \ COMMIT_HASH="${COMMIT_HASH:-}" \ GIT_REMOTE_URL="${GIT_REMOTE_URL}" \ SOURCE_COMMIT_FILE=".jenkins-source-commit" \ scripts/jenkins-checkout-source.sh ' ''' script { env.SOURCE_COMMIT = readFile('.jenkins-source-commit').trim() env.EFFECTIVE_BUILD_VERSION = params.BUILD_VERSION?.trim() ? params.BUILD_VERSION.trim() : env.BUILD_NUMBER if (params.DEPLOY_TARGET == 'release' && !params.CONFIRM_RELEASE_DEPLOY_AGENT) { error('release 部署需要先配置独立 release 部署 agent,并勾选 CONFIRM_RELEASE_DEPLOY_AGENT。当前 Linux 开发/构建/开发部署 agent 不能执行 release 部署。') } } } } stage('Build Components') { // 任一构建分支失败时立即中断其他并行分支,避免失败后继续消耗构建资源。 failFast true parallel { stage('Web') { steps { script { def webRun = build job: params.WEB_BUILD_JOB_NAME, wait: true, propagate: true, parameters: [ string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH), string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT), string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), booleanParam(name: 'RUN_NPM_CI', value: params.RUN_NPM_CI), ] env.WEB_BUILD_NUMBER = webRun.number.toString() } } } stage('Api') { steps { script { def apiRun = build job: params.API_BUILD_JOB_NAME, wait: true, propagate: true, parameters: [ string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH), string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT), string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), ] env.API_BUILD_NUMBER = apiRun.number.toString() } } } stage('Stdb') { steps { script { def stdbRun = build job: params.STDB_BUILD_JOB_NAME, wait: true, propagate: true, parameters: [ string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH), string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT), string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), string(name: 'MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID', value: params.MIGRATION_BOOTSTRAP_SECRET_CREDENTIAL_ID ?: ''), string(name: 'DATABASE', value: params.DATABASE), ] env.STDB_BUILD_NUMBER = stdbRun.number.toString() } } } } } stage('Deploy Stdb') { steps { build job: params.STDB_PUBLISH_JOB_NAME, wait: true, propagate: true, parameters: [ string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH), string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT), string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), string(name: 'DATABASE', value: params.DATABASE), string(name: 'SPACETIME_SERVER_URL', value: params.SPACETIME_SERVER_URL ?: ''), string(name: 'SPACETIME_ROOT_DIR', value: params.SPACETIME_ROOT_DIR ?: '/stdb'), string(name: 'SPACETIME_RUN_AS_USER', value: params.SPACETIME_RUN_AS_USER ?: 'spacetimedb'), string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET), booleanParam(name: 'CONFIRM_RELEASE_DEPLOY_AGENT', value: params.CONFIRM_RELEASE_DEPLOY_AGENT), string(name: 'BUILD_JOB_NAME', value: params.STDB_BUILD_JOB_NAME), string(name: 'BUILD_NUMBER_TO_DEPLOY', value: env.STDB_BUILD_NUMBER), ] } } stage('Deploy Api') { steps { build job: params.API_DEPLOY_JOB_NAME, wait: true, propagate: true, parameters: [ string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH), string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT), string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET), booleanParam(name: 'CONFIRM_RELEASE_DEPLOY_AGENT', value: params.CONFIRM_RELEASE_DEPLOY_AGENT), string(name: 'BUILD_JOB_NAME', value: params.API_BUILD_JOB_NAME), string(name: 'BUILD_NUMBER_TO_DEPLOY', value: env.API_BUILD_NUMBER), string(name: 'DATABASE', value: params.DATABASE), string(name: 'SPACETIME_SERVER_URL', value: params.SPACETIME_SERVER_URL ?: ''), ] } } stage('Deploy Web') { steps { build job: params.WEB_DEPLOY_JOB_NAME, wait: true, propagate: true, parameters: [ string(name: 'SOURCE_BRANCH', value: params.SOURCE_BRANCH), string(name: 'COMMIT_HASH', value: env.SOURCE_COMMIT), string(name: 'BUILD_VERSION', value: env.EFFECTIVE_BUILD_VERSION), string(name: 'NOTIFICATION_EMAILS', value: params.NOTIFICATION_EMAILS ?: ''), string(name: 'DEPLOY_TARGET', value: params.DEPLOY_TARGET), booleanParam(name: 'CONFIRM_RELEASE_DEPLOY_AGENT', value: params.CONFIRM_RELEASE_DEPLOY_AGENT), string(name: 'BUILD_JOB_NAME', value: params.WEB_BUILD_JOB_NAME), string(name: 'BUILD_NUMBER_TO_DEPLOY', value: env.WEB_BUILD_NUMBER), ] } } } 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: '全量构建发布编排结束'), ] 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 "Full Build-And-Deploy 完成: version=${env.EFFECTIVE_BUILD_VERSION}, commit=${env.SOURCE_COMMIT}" } } }