pipeline { agent any environment { // 环境变量定义 PROJECT_NAME = 'timeline-frontend' REGISTRY = 'timeline-registry:5000' DOCKER_IMAGE = "${DOCKER_REGISTRY}/${PROJECT_NAME}" } parameters { // 构建参数 choice( name: 'DEPLOY_TARGET', choices: ['dev', 'staging', 'prod'], description: '选择部署环境' ) string( name: 'GIT_COMMIT', defaultValue: '', description: '指定 Git Commit SHA 进行构建(留空则使用最新提交)' ) } stages { stage('Checkout') { steps { script { if (params.GIT_COMMIT) { checkout scm sh "git reset --hard ${params.GIT_COMMIT}" } else { checkout scm } } echo "当前构建的 Git Commit: ${env.GIT_COMMIT}" } } stage('Build Docker Image') { steps { script { // 确保先执行前端构建命令 sh 'npm install' sh 'npm run build' // 这会生成 dist 目录 def imageTag = "${BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}" env.IMAGE_TAG = imageTag sh """ docker build -t ${DOCKER_IMAGE}:${imageTag} . docker tag ${DOCKER_IMAGE}:${imageTag} ${DOCKER_IMAGE}:latest """ } } } stage('Push Docker Image') { steps { script { withCredentials([usernamePassword(credentialsId: 'docker-registry-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) { sh 'echo $DOCKER_PASS | docker login $DOCKER_REGISTRY -u $DOCKER_USER --password-stdin' sh "docker push ${DOCKER_IMAGE}:${env.IMAGE_TAG}" sh "docker push ${DOCKER_IMAGE}:latest" } } } } stage('Deploy to Environment') { parallel { stage('Deploy to Dev') { when { expression { params.DEPLOY_TARGET == 'dev' } } steps { deployToEnvironment('dev') } } stage('Deploy to Staging') { when { anyOf { expression { params.DEPLOY_TARGET == 'staging' } expression { params.DEPLOY_TARGET == 'prod' } } } steps { deployToEnvironment('staging') } } stage('Deploy to Production') { when { expression { params.DEPLOY_TARGET == 'prod' } } steps { script { input message: "确定要部署到生产环境吗?", ok: "是", parameters: [ choice(name: 'CONFIRM_DEPLOY', choices: ['yes', 'no'], description: '确认部署') ] if (params.CONFIRM_DEPLOY == 'yes') { deployToEnvironment('prod') } else { error "部署被取消" } } } } } } } post { always { // 清理构建产物 cleanWs() } success { script { def slackMessage = """ *✅ 构建成功* 项目: ${PROJECT_NAME} 构建: ${BUILD_NUMBER} 分支: ${env.BRANCH_NAME} 提交: ${env.GIT_COMMIT} 镜像: ${DOCKER_IMAGE}:${env.IMAGE_TAG} """ // 发送成功通知(如果配置了 Slack 通知) // slackSend(channel: '#builds', color: 'good', message: slackMessage) } } failure { script { def slackMessage = """ *❌ 构建失败* 项目: ${PROJECT_NAME} 构建: ${BUILD_NUMBER} 分支: ${env.BRANCH_NAME} 提交: ${env.GIT_COMMIT} 错误: ${currentBuild.description ?: '构建失败'} """ // 发送失败通知(如果配置了 Slack 通知) // slackSend(channel: '#builds', color: 'danger', message: slackMessage) } } cleanup { // 清理 Docker 镜像 script { sh """ docker rmi -f ${DOCKER_IMAGE}:${env.IMAGE_TAG} || true docker rmi -f ${DOCKER_IMAGE}:latest || true """ } } } } // 公共函数定义 def deployToEnvironment(String env) { script { // 根据环境设置部署参数 def containerName = "${PROJECT_NAME}-${env}" def imageToDeploy = "${DOCKER_IMAGE}:${env.IMAGE_TAG}" // 停止现有容器 sh """ docker stop ${containerName} || true docker rm ${containerName} || true """ // 运行新容器 sh """ docker run -d \ --name ${containerName} \ --restart unless-stopped \ -p ${getPortForEnvironment(env)}:3000 \ ${imageToDeploy} """ // 验证部署 sh """ echo "等待容器启动..." sleep 10 docker ps | grep ${containerName} docker logs ${containerName} """ } } // 获取环境对应端口的辅助函数 def getPortForEnvironment(String env) { switch(env) { case 'dev': return '3001' case 'staging': return '3002' case 'prod': return '3000' default: return '3000' } }