| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388 |
- /**
- * UAT: Checkout -> Maven -> (optional) push images to Harbor -> deploy jar + docker restart.
- *
- * Jenkins Job: Pipeline script from SCM
- * Script Path: docs/jenkins/Jenkinsfile-uat-build-deploy.groovy
- *
- * Harbor (153.68): when PUSH_TO_HARBOR=true, push e.g.
- * 39.105.153.68/alien_cloud/gateway:uat-latest
- * Before push: existing uat-latest is archived as uat-build-<BUILD_NUMBER> (same digest).
- * New image is pushed only as uat-latest. Prod promote: SOURCE_TAG=uat-latest.
- */
- /** HARBOR_PUSH_SCOPE: all-java-services | <repo>-only */
- def filterHarborPushScope(List allServices, String scope) {
- def s = (scope ?: 'all-java-services').trim()
- if (s == 'all-java-services') {
- return allServices
- }
- if (s.endsWith('-only')) {
- def repo = s.substring(0, s.length() - '-only'.length())
- def picked = allServices.findAll { it.repo == repo }
- if (picked.isEmpty()) {
- error("Unknown HARBOR_PUSH_SCOPE: ${scope}")
- }
- return picked
- }
- error("Unknown HARBOR_PUSH_SCOPE: ${scope}")
- }
- /** Delete oldest uat-build-* tags in Harbor, keep newest KEEP. Never deletes uat-latest or current build tag. */
- def pruneHarborUatTags(def script, String reg, String proj, List repoNames, int keepCount, String tagPrefix, String currentBuildTag, String latestTag) {
- if (repoNames == null || repoNames.isEmpty() || keepCount < 1) {
- return
- }
- def repos = repoNames.join(' ')
- script.sh """
- set -e
- REG='${reg}'
- PROJ='${proj}'
- KEEP=${keepCount}
- PREFIX='${tagPrefix}'
- CURRENT='${currentBuildTag}'
- LATEST='${latestTag}'
- if ! command -v jq >/dev/null 2>&1; then
- echo '>>> Harbor prune skipped: jq not installed on Jenkins agent'
- exit 0
- fi
- for repo in ${repos}; do
- enc_repo=\$(printf '%s' "\${repo}" | jq -sRr @uri)
- mapfile -t tags < <(curl -fsS -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
- "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts?page_size=100" \\
- | jq -r '.[] | .tags[]? | .name' | grep "^\${PREFIX}" | sort -t- -k3 -n || true)
- count=\${#tags[@]}
- echo ">>> prune \${repo}: \${count} tag(s) matching \${PREFIX}*"
- if [ "\${count}" -le "\${KEEP}" ]; then
- continue
- fi
- del_count=\$((count - KEEP))
- i=0
- while [ "\${i}" -lt "\${del_count}" ]; do
- t="\${tags[\$i]}"
- if [ "\${t}" = "\${CURRENT}" ] || [ "\${t}" = "\${LATEST}" ]; then
- i=\$((i + 1))
- continue
- fi
- echo ">>> DELETE Harbor tag \${repo}:\${t}"
- if ! curl -fsS -X DELETE -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
- "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts/\${t}/tags/\${t}"; then
- echo ">>> WARN: delete failed \${repo}:\${t} (check robot delete permission)"
- fi
- i=\$((i + 1))
- done
- done
- """
- }
- pipeline {
- agent any
- options {
- buildDiscarder(logRotator(numToKeepStr: '15'))
- timestamps()
- timeout(time: 90, unit: 'MINUTES')
- }
- parameters {
- string(
- name: 'GIT_BRANCH',
- defaultValue: 'uat-20260202',
- trim: true,
- description: 'Git branch, must match remote (e.g. uat-20260202)'
- )
- booleanParam(name: 'FORCE_UPDATE', defaultValue: true, description: 'mvn -U')
- booleanParam(name: 'ALLOW_SNAPSHOTS', defaultValue: true, description: 'allow SNAPSHOT deps')
- booleanParam(
- name: 'PUSH_TO_HARBOR',
- defaultValue: true,
- description: 'After Maven: docker build + push to Harbor (tags uat-latest and uat-build-<N>). Uncheck for jar-only UAT deploy.'
- )
- choice(
- name: 'HARBOR_PUSH_SCOPE',
- choices: [
- 'all-java-services',
- 'gateway-only',
- 'store-only',
- 'second-only',
- 'store-platform-only',
- 'lawyer-only',
- 'job-only',
- 'dining-only',
- ],
- description: 'Only when PUSH_TO_HARBOR=true; default=all seven; *-only=one service'
- )
- string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
- string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
- booleanParam(
- name: 'HARBOR_PRUNE_OLD_TAGS',
- defaultValue: true,
- description: 'After push: delete old uat-build-* tags in Harbor, keep last N per repo (never deletes uat-latest)'
- )
- string(name: 'HARBOR_KEEP_TAG_COUNT', defaultValue: '10', trim: true,
- description: 'How many uat-build-* tags to keep per repository')
- }
- environment {
- MAVEN_HOME = tool '3.6.3'
- PATH = "${MAVEN_HOME}/bin:${env.PATH}"
- GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
- GIT_CREDENTIALS = 'zhanghaomimapingzheng'
- HARBOR_CREDENTIALS = 'harbor-robot-alien'
- UAT_HARBOR_LATEST_TAG = 'uat-latest'
- UAT_HARBOR_BUILD_TAG = "uat-build-${env.BUILD_NUMBER}"
- DOCKERFILE_JAVA = 'docs/jenkins/produ/docker/Dockerfile.java-service'
- }
- stages {
- stage('Checkout') {
- steps {
- script {
- def branch = (params.GIT_BRANCH ?: 'uat-20260202').trim()
- if (!branch) {
- error('GIT_BRANCH is required')
- }
- env.GIT_BRANCH = branch
- echo ">>> Checkout branch: ${env.GIT_BRANCH}"
- git branch: "${env.GIT_BRANCH}",
- credentialsId: "${env.GIT_CREDENTIALS}",
- url: "${env.GIT_URL}"
- sh """
- set -e
- git fetch origin
- git reset --hard origin/${env.GIT_BRANCH}
- git log -1 --oneline
- """
- }
- }
- }
- stage('Prepare Maven Settings') {
- steps {
- script {
- writeFile file: 'settings.xml', text: """<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
- <profiles>
- <profile>
- <id>repo-mix</id>
- <repositories>
- <repository>
- <id>central</id>
- <name>Maven Central</name>
- <url>https://repo.maven.apache.org/maven2</url>
- <releases><enabled>true</enabled><updatePolicy>always</updatePolicy></releases>
- <snapshots><enabled>false</enabled></snapshots>
- </repository>
- <repository>
- <id>spring-milestones</id>
- <name>Spring Milestones</name>
- <url>https://repo.spring.io/milestone</url>
- <releases><enabled>true</enabled><updatePolicy>always</updatePolicy></releases>
- <snapshots><enabled>false</enabled></snapshots>
- </repository>
- <repository>
- <id>spring-snapshots</id>
- <name>Spring Snapshots</name>
- <url>https://repo.spring.io/snapshot</url>
- <releases><enabled>false</enabled></releases>
- <snapshots><enabled>true</enabled><updatePolicy>always</updatePolicy></snapshots>
- </repository>
- </repositories>
- <pluginRepositories>
- <pluginRepository>
- <id>central</id>
- <url>https://repo.maven.apache.org/maven2</url>
- <releases><enabled>true</enabled></releases>
- <snapshots><enabled>false</enabled></snapshots>
- </pluginRepository>
- <pluginRepository>
- <id>spring-milestones</id>
- <url>https://repo.spring.io/milestone</url>
- <releases><enabled>true</enabled></releases>
- <snapshots><enabled>false</enabled></snapshots>
- </pluginRepository>
- </pluginRepositories>
- </profile>
- </profiles>
- <activeProfiles>
- <activeProfile>repo-mix</activeProfile>
- </activeProfiles>
- </settings>
- """
- }
- }
- }
- stage('Maven Build') {
- steps {
- script {
- def updateFlag = params.FORCE_UPDATE ? '-U' : ''
- retry(2) {
- sh """
- set -e
- mvn -version
- unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY all_proxy no_proxy NO_PROXY || true
- export MAVEN_OPTS="-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true"
- rm -rf /root/.m2/repository/org/springframework/cloud/spring-cloud-dependencies/Hoxton.SR1 || true
- rm -rf /root/.m2/repository/org/springframework/boot/spring-boot-dependencies/2.3.2.RELEASE || true
- rm -rf ${WORKSPACE}/.m2/repository/org/springframework/cloud/spring-cloud-dependencies/Hoxton.SR1 || true
- rm -rf ${WORKSPACE}/.m2/repository/org/springframework/boot/spring-boot-dependencies/2.3.2.RELEASE || true
- mvn clean package -DskipTests -s settings.xml ${updateFlag} -e -Dmaven.repo.local=${WORKSPACE}/.m2/repository
- """
- }
- }
- }
- }
- stage('Push images to Harbor') {
- when {
- expression { return params.PUSH_TO_HARBOR == true }
- }
- steps {
- script {
- def reg = params.HARBOR_REGISTRY.trim()
- def proj = params.HARBOR_PROJECT.trim()
- def latestTag = env.UAT_HARBOR_LATEST_TAG
- def buildTag = env.UAT_HARBOR_BUILD_TAG
- def baseImage = "${reg}/${proj}/base/openjdk8-ffmpeg:v1"
- def dockerfile = env.DOCKERFILE_JAVA
- def allHarborServices = [
- [module: 'alien-gateway', repo: 'gateway', port: '8000', withLib: false],
- [module: 'alien-store', repo: 'store', port: '50014', withLib: true],
- [module: 'alien-second', repo: 'second', port: '50015', withLib: false],
- [module: 'alien-store-platform', repo: 'store-platform', port: '50016', withLib: false],
- [module: 'alien-lawyer', repo: 'lawyer', port: '50017', withLib: true],
- [module: 'alien-job', repo: 'job', port: '50108', withLib: false],
- [module: 'alien-dining', repo: 'dining', port: '50019', withLib: false],
- ]
- def harborServices = filterHarborPushScope(allHarborServices, params.HARBOR_PUSH_SCOPE)
- echo ">>> HARBOR_PUSH_SCOPE=${params.HARBOR_PUSH_SCOPE} repos=${harborServices*.repo.join(',')}"
- withCredentials([usernamePassword(
- credentialsId: env.HARBOR_CREDENTIALS,
- usernameVariable: 'HARBOR_USER',
- passwordVariable: 'HARBOR_PASS',
- )]) {
- sh """
- set -e
- echo "\${HARBOR_PASS}" | docker login ${reg} -u "\${HARBOR_USER}" --password-stdin
- echo ">>> docker disk before Harbor push:"
- df -h /var/lib/docker 2>/dev/null || df -h / || true
- docker system prune -f --filter until=48h 2>/dev/null || true
- """
- harborServices.each { svc ->
- def jarName = "${svc.module}-1.0.0.jar"
- def imageLatest = "${reg}/${proj}/${svc.repo}:${latestTag}"
- def imageBuild = "${reg}/${proj}/${svc.repo}:${buildTag}"
- def withLibFlag = svc.withLib ? 'true' : 'false'
- sh """
- set -e
- test -f ${WORKSPACE}/${svc.module}/target/${jarName}
- cd ${WORKSPACE}/${svc.module}
- rm -rf .jenkins_docker_ctx && mkdir -p .jenkins_docker_ctx/lib
- cp -f target/${jarName} .jenkins_docker_ctx/${jarName}
- if [ "${withLibFlag}" = "true" ] && [ -d target/lib ]; then
- cp -rf target/lib/. .jenkins_docker_ctx/lib/
- else
- touch .jenkins_docker_ctx/lib/.keep
- fi
- cd .jenkins_docker_ctx
- if docker pull ${imageLatest} 2>/dev/null; then
- echo ">>> archive previous ${latestTag} -> ${buildTag}"
- docker tag ${imageLatest} ${imageBuild}
- docker push ${imageBuild}
- fi
- docker build -f ${WORKSPACE}/${dockerfile} \\
- --build-arg BASE_IMAGE=${baseImage} \\
- --build-arg JAR_FILE=${jarName} \\
- --build-arg SERVER_PORT=${svc.port} \\
- --build-arg WITH_LIB=${svc.withLib} \\
- -t ${imageLatest} .
- docker push ${imageLatest}
- echo ">>> pushed ${imageLatest} (archived prior latest as ${buildTag} if any)"
- docker rmi ${imageLatest} 2>/dev/null || true
- cd ${WORKSPACE}/${svc.module}
- rm -rf .jenkins_docker_ctx
- """
- }
- if (params.HARBOR_PRUNE_OLD_TAGS == true) {
- def keepN = (params.HARBOR_KEEP_TAG_COUNT ?: '10').trim() as int
- pruneHarborUatTags(
- this, reg, proj, harborServices*.repo,
- keepN, 'uat-build-', buildTag, latestTag,
- )
- }
- }
- echo ">>> Harbor latest: ${env.UAT_HARBOR_LATEST_TAG}; archived tag this run: ${env.UAT_HARBOR_BUILD_TAG}"
- echo ">>> Prod promote: SOURCE_TAG=${env.UAT_HARBOR_LATEST_TAG}"
- }
- }
- }
- stage('Deploy Services') {
- steps {
- script {
- def services = [
- 'alien-gateway:gateway-uat',
- 'alien-job:job-uat',
- 'alien-lawyer:lawyer-uat',
- 'alien-second:second-uat',
- 'alien-store:store-uat',
- 'alien-dining:dining-uat',
- 'alien-store-platform:store-platform-uat',
- ]
- for (item in services) {
- def parts = item.split(':')
- def moduleName = parts[0]
- def dirName = parts[1]
- def sourceJar = "${env.WORKSPACE}/${moduleName}/target/${moduleName}-1.0.0.jar"
- def sourceLib = "${env.WORKSPACE}/${moduleName}/target/lib"
- def targetDir = "/app_deploy_uat/${dirName}"
- sh """
- set -e
- echo ">>> Deploy module: ${moduleName}"
- if [ -f "${sourceJar}" ]; then
- mkdir -p "${targetDir}"
- if [ -d "${sourceLib}" ]; then
- rm -rf "${targetDir}/lib"
- cp -rf "${sourceLib}" "${targetDir}"
- fi
- cp -f "${sourceJar}" "${targetDir}/"
- if docker ps -a --format '{{.Names}}' | grep -wq "${dirName}"; then
- docker restart "${dirName}"
- echo ">>> [${dirName}] restarted"
- else
- echo ">>> [${dirName}] container missing, jar copied only"
- fi
- else
- echo ">>> [${dirName}] jar missing, skip"
- fi
- """
- }
- }
- }
- }
- }
- post {
- always {
- sh 'rm -f settings.xml || true'
- script {
- if (!params.PUSH_TO_HARBOR) {
- echo '>>> Harbor push SKIPPED: PUSH_TO_HARBOR is false. On "Build with Parameters" check PUSH_TO_HARBOR.'
- }
- }
- }
- success {
- script {
- if (params.PUSH_TO_HARBOR) {
- echo ">>> Harbor latest: ${env.UAT_HARBOR_LATEST_TAG}"
- echo ">>> Prod promote: SOURCE_TAG=${env.UAT_HARBOR_LATEST_TAG}"
- }
- }
- }
- }
- }
|