pipeline { agent { label 'linux && genarrative-build' } options { disableConcurrentBuilds() skipDefaultCheckout(true) timeout(time: 120, unit: 'MINUTES') buildDiscarder(logRotator(numToKeepStr: '50', artifactNumToKeepStr: '50')) } environment { GIT_REMOTE_URL = 'ssh://git@127.0.0.1:2222/GenarrativeAI/Genarrative.git' GIT_REMOTE_CREDENTIAL_ID = 'genarrative-local-gitea-ssh' GENARRATIVE_PREVIEW_STATE_ROOT = '/data/jenkins/preview-deployments' GENARRATIVE_PREVIEW_SECRETS_FILE = '/data/jenkins/preview-secrets/.env.secrets.local' GENARRATIVE_PREVIEW_ENV_LOCAL_FILE = '/data/jenkins/preview-secrets/.env.local' GENARRATIVE_PREVIEW_WEB_HOST = '192.168.35.82' } parameters { choice(name: 'ACTION', choices: ['DEPLOY', 'STATUS', 'UNINSTALL'], description: '部署、查询状态或卸载分支预览容器') string(name: 'SOURCE_BRANCH', defaultValue: '', description: '源码分支;DEPLOY 必填,同一分支稳定映射到同一个预览实例') string(name: 'COMMIT_HASH', defaultValue: '', description: '可选;必须是 SOURCE_BRANCH 历史中的 commit') string(name: 'DEPLOYMENT_ID', defaultValue: '', description: '可选;留空时由 SOURCE_BRANCH 稳定生成,查询和卸载可直接传部署 ID') } stages { stage('Checkout Controller') { steps { checkout([ $class: 'GitSCM', branches: [[name: '*/master']], doGenerateSubmoduleConfigurations: false, extensions: [ [$class: 'CleanBeforeCheckout'], [$class: 'CloneOption', shallow: true, depth: 1, noTags: true, timeout: 30, honorRefspec: true], ], userRemoteConfigs: [[ url: env.GIT_REMOTE_URL, credentialsId: env.GIT_REMOTE_CREDENTIAL_ID, refspec: '+refs/heads/master:refs/remotes/origin/master', ]], ]) } } stage('Validate Request') { steps { sh ''' set -euo pipefail ACTION="${ACTION:-}" SOURCE_BRANCH="${SOURCE_BRANCH:-}" COMMIT_HASH="${COMMIT_HASH:-}" DEPLOYMENT_ID="${DEPLOYMENT_ID:-}" case "${ACTION}" in DEPLOY|STATUS|UNINSTALL) ;; *) exit 1 ;; esac if [ -n "${SOURCE_BRANCH}" ]; then git check-ref-format --branch "${SOURCE_BRANCH}" >/dev/null fi if [ -n "${COMMIT_HASH}" ] && ! printf '%s' "${COMMIT_HASH}" | grep -Eq '^[0-9a-fA-F]{7,40}$'; then echo 'COMMIT_HASH 只能填写 7 到 40 位十六进制 Git commit hash。' >&2 exit 1 fi if [ -n "${DEPLOYMENT_ID}" ] && ! printf '%s' "${DEPLOYMENT_ID}" | grep -Eq '^preview-[0-9a-f]{16}$'; then echo 'DEPLOYMENT_ID 格式非法。' >&2 exit 1 fi ''' } } stage('Checkout Requested Source') { when { expression { params.ACTION == 'DEPLOY' } } steps { dir('source') { script { def remoteConfig = [ url: env.GIT_REMOTE_URL, credentialsId: env.GIT_REMOTE_CREDENTIAL_ID, refspec: "+refs/heads/${params.SOURCE_BRANCH}:refs/remotes/origin/${params.SOURCE_BRANCH}", ] checkout([ $class: 'GitSCM', branches: [[name: "*/${params.SOURCE_BRANCH}"]], doGenerateSubmoduleConfigurations: false, extensions: [ [$class: 'CleanBeforeCheckout'], [$class: 'CloneOption', shallow: true, depth: 1, noTags: true, timeout: 30, honorRefspec: true], ], userRemoteConfigs: [remoteConfig], ]) } withCredentials([sshUserPrivateKey(credentialsId: env.GIT_REMOTE_CREDENTIAL_ID, keyFileVariable: 'GENARRATIVE_GIT_SSH_KEY')]) { sh ''' set -euo pipefail SOURCE_BRANCH="${SOURCE_BRANCH:-}" \ COMMIT_HASH="${COMMIT_HASH:-}" \ GIT_REMOTE_URL="${GIT_REMOTE_URL:-}" \ SOURCE_COMMIT_FILE=".jenkins-source-commit" \ GENARRATIVE_JENKINS_REUSE_EXISTING_CHECKOUT="true" \ GIT_SSH_COMMAND="ssh -i ${GENARRATIVE_GIT_SSH_KEY} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new" \ bash "${WORKSPACE}/scripts/jenkins-checkout-source.sh" ''' } } } } stage('Execute Preview Action') { steps { sh ''' set -euo pipefail chmod +x scripts/jenkins-preview-deployer.sh SOURCE_DIR="${WORKSPACE}/source" \ RESULT_FILE="${WORKSPACE}/preview-result.json" \ DESCRIPTION_FILE="${WORKSPACE}/.jenkins-preview-description" \ scripts/jenkins-preview-deployer.sh ''' script { if (fileExists('.jenkins-preview-description')) { currentBuild.description = readFile('.jenkins-preview-description').trim() } } } } } post { always { script { if (!fileExists('preview-result.json')) { writeFile file: 'preview-result.json', text: groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson([ schemaVersion: 1, action: params.ACTION ?: 'UNKNOWN', deploymentId: params.DEPLOYMENT_ID ?: null, projectName: params.DEPLOYMENT_ID ? "genarrative-${params.DEPLOYMENT_ID}" : null, branch: params.SOURCE_BRANCH ?: null, requestedCommit: params.COMMIT_HASH ?: null, resolvedCommit: null, status: 'failed', health: 'unknown', webUrl: null, message: 'Jenkins 在生成部署结果前失败,请查看构建日志。', updatedAt: new Date().format("yyyy-MM-dd'T'HH:mm:ssXXX"), ])) + '\n' } } archiveArtifacts artifacts: 'preview-result.json', allowEmptyArchive: false, fingerprint: true } } }