Jenkinsfile 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. /**
  2. * UAT: Checkout -> Maven -> (optional) push images to Harbor -> deploy jar + docker restart.
  3. *
  4. * Jenkins Job: Pipeline script from SCM
  5. * Script Path: docs/jenkins/uat/Jenkinsfile
  6. *
  7. * Harbor (153.68): when PUSH_TO_HARBOR=true, push e.g.
  8. * 39.105.153.68/alien_cloud/gateway:uat-latest
  9. * Before push: existing uat-latest is archived as uat-build-<BUILD_NUMBER> via Harbor API
  10. * (same digest, no docker pull/tag/push). New image is pushed only as uat-latest.
  11. * Prod promote: SOURCE_TAG=uat-latest.
  12. */
  13. /** Normalize GIT_BRANCH: uat-20260202 (not origin/uat-20260202 or refs/heads/...) */
  14. def normalizeGitBranch(String raw) {
  15. def b = (raw ?: 'uat-20260202').trim()
  16. if (!b) {
  17. return 'uat-20260202'
  18. }
  19. while (b.startsWith('refs/heads/')) {
  20. b = b.substring('refs/heads/'.length())
  21. }
  22. while (b.startsWith('origin/')) {
  23. b = b.substring('origin/'.length())
  24. }
  25. return b ?: 'uat-20260202'
  26. }
  27. /** HARBOR_PUSH_SCOPE: all-java-services | <repo>-only */
  28. def filterHarborPushScope(List allServices, String scope) {
  29. def s = (scope ?: 'all-java-services').trim()
  30. if (s == 'all-java-services') {
  31. return allServices
  32. }
  33. if (s.endsWith('-only')) {
  34. def repo = s.substring(0, s.length() - '-only'.length())
  35. def picked = allServices.findAll { it.repo == repo }
  36. if (picked.isEmpty()) {
  37. error("Unknown HARBOR_PUSH_SCOPE: ${scope}")
  38. }
  39. return picked
  40. }
  41. error("Unknown HARBOR_PUSH_SCOPE: ${scope}")
  42. }
  43. /** Archive current uat-latest as uat-build-N via Harbor API (same digest, no layer re-upload). */
  44. def archiveHarborLatestViaApi(def script, String reg, String proj, String repo, String latestTag, String buildTag) {
  45. script.sh """
  46. set -e
  47. REG='${reg}'
  48. PROJ='${proj}'
  49. REPO='${repo}'
  50. LATEST='${latestTag}'
  51. BUILD_TAG='${buildTag}'
  52. if ! command -v jq >/dev/null 2>&1; then
  53. echo '>>> WARN: jq missing, skip Harbor API archive for '\${REPO}
  54. exit 0
  55. fi
  56. enc_repo=\$(printf '%s' "\${REPO}" | jq -sRr @uri)
  57. if ! curl -fsS -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
  58. "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts/\${LATEST}" >/dev/null 2>&1; then
  59. echo ">>> no prior \${LATEST} for \${REPO}, skip archive"
  60. exit 0
  61. fi
  62. echo ">>> archive previous \${LATEST} -> \${BUILD_TAG} (\${REPO}, Harbor API)"
  63. archive_code=\$(curl -sS -o /tmp/harbor_archive_\${REPO}.json -w '%{http_code}' \\
  64. -X POST -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
  65. "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts/\${LATEST}/tags" \\
  66. -H 'Content-Type: application/json' \\
  67. -d "{\\"name\\":\\"\${BUILD_TAG}\\"}")
  68. case "\${archive_code}" in
  69. 201|200)
  70. echo ">>> archived \${REPO}:\${BUILD_TAG} (same digest as prior \${LATEST})"
  71. ;;
  72. 409)
  73. echo ">>> WARN: \${REPO}:\${BUILD_TAG} already exists, continue"
  74. ;;
  75. *)
  76. echo ">>> ERROR: Harbor archive \${REPO} HTTP \${archive_code}"
  77. cat /tmp/harbor_archive_\${REPO}.json 2>/dev/null || true
  78. exit 1
  79. ;;
  80. esac
  81. """
  82. }
  83. def pushOneHarborImage(def script, Map svc, String reg, String proj, String latestTag, String buildTag,
  84. String baseImage, String dockerfile, String workspace) {
  85. def jarName = "${svc.module}-1.0.0.jar"
  86. def imageLatest = "${reg}/${proj}/${svc.repo}:${latestTag}"
  87. def withLibFlag = svc.withLib ? 'true' : 'false'
  88. script.sh """
  89. set -e
  90. test -f ${workspace}/${svc.module}/target/${jarName}
  91. cd ${workspace}/${svc.module}
  92. rm -rf .jenkins_docker_ctx && mkdir -p .jenkins_docker_ctx/lib
  93. cp -f target/${jarName} .jenkins_docker_ctx/${jarName}
  94. if [ "${withLibFlag}" = "true" ] && [ -d target/lib ]; then
  95. cp -rf target/lib/. .jenkins_docker_ctx/lib/
  96. else
  97. touch .jenkins_docker_ctx/lib/.keep
  98. fi
  99. """
  100. // Harbor API archive runs outside flock (HTTP only, safe to parallelize).
  101. archiveHarborLatestViaApi(script, reg, proj, svc.repo, latestTag, buildTag)
  102. // Parallel branches share one Docker daemon; serialize docker build/push to avoid containerd races.
  103. script.sh """
  104. set -e
  105. DOCKER_LOCK=/tmp/jenkins-alien-cloud-docker.lock
  106. flock "\${DOCKER_LOCK}" sh -c '
  107. set -e
  108. cd ${workspace}/${svc.module}/.jenkins_docker_ctx
  109. build_ok=0
  110. for attempt in 1 2 3; do
  111. if docker build -f ${workspace}/${dockerfile} \\
  112. --build-arg BASE_IMAGE=${baseImage} \\
  113. --build-arg JAR_FILE=${jarName} \\
  114. --build-arg SERVER_PORT=${svc.port} \\
  115. --build-arg WITH_LIB=${svc.withLib} \\
  116. -t ${imageLatest} .; then
  117. build_ok=1
  118. break
  119. fi
  120. echo ">>> WARN: docker build ${svc.repo} attempt \${attempt} failed, retrying..."
  121. sleep "\$((attempt * 5))"
  122. done
  123. if [ "\${build_ok}" -ne 1 ]; then
  124. echo ">>> ERROR: docker build ${svc.repo} failed after 3 attempts"
  125. exit 1
  126. fi
  127. docker push ${imageLatest}
  128. echo ">>> pushed ${imageLatest} (archived prior latest as ${buildTag} if any)"
  129. docker rmi ${imageLatest} 2>/dev/null || true
  130. '
  131. cd ${workspace}/${svc.module}
  132. rm -rf .jenkins_docker_ctx
  133. """
  134. }
  135. def deployOneUatService(def script, String moduleName, String dirName, String workspace) {
  136. def sourceJar = "${workspace}/${moduleName}/target/${moduleName}-1.0.0.jar"
  137. def sourceLib = "${workspace}/${moduleName}/target/lib"
  138. def targetDir = "/app_deploy_uat/${dirName}"
  139. script.sh """
  140. set -e
  141. echo ">>> Deploy module: ${moduleName}"
  142. if [ -f "${sourceJar}" ]; then
  143. mkdir -p "${targetDir}"
  144. if [ -d "${sourceLib}" ]; then
  145. rm -rf "${targetDir}/lib"
  146. cp -rf "${sourceLib}" "${targetDir}"
  147. fi
  148. cp -f "${sourceJar}" "${targetDir}/"
  149. if docker ps -a --format '{{.Names}}' | grep -wq "${dirName}"; then
  150. docker restart "${dirName}"
  151. echo ">>> [${dirName}] restarted"
  152. else
  153. echo ">>> [${dirName}] container missing, jar copied only"
  154. fi
  155. else
  156. echo ">>> [${dirName}] jar missing, skip"
  157. fi
  158. """
  159. }
  160. /** Delete oldest uat-build-* tags in Harbor, keep newest KEEP. Never deletes uat-latest or current build tag. */
  161. def pruneHarborUatTags(def script, String reg, String proj, List repoNames, int keepCount, String tagPrefix, String currentBuildTag, String latestTag) {
  162. if (repoNames == null || repoNames.isEmpty() || keepCount < 1) {
  163. return
  164. }
  165. def repos = repoNames.join(' ')
  166. // POSIX sh only (Jenkins sh step); no mapfile / process substitution
  167. script.sh """
  168. set -e
  169. REG='${reg}'
  170. PROJ='${proj}'
  171. KEEP=${keepCount}
  172. PREFIX='${tagPrefix}'
  173. CURRENT='${currentBuildTag}'
  174. LATEST='${latestTag}'
  175. if ! command -v jq >/dev/null 2>&1; then
  176. echo '>>> Harbor prune skipped: jq not installed on Jenkins agent'
  177. exit 0
  178. fi
  179. for repo in ${repos}; do
  180. enc_repo=\$(printf '%s' "\${repo}" | jq -sRr @uri)
  181. tags_file=\$(mktemp)
  182. curl -fsS -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
  183. "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts?page_size=100" \\
  184. | jq -r '.[] | .tags[]? | .name' | grep "^\${PREFIX}" | sort -t- -k3 -n > "\${tags_file}" || true
  185. count=\$(wc -l < "\${tags_file}" | tr -d ' ')
  186. echo ">>> prune \${repo}: \${count} tag(s) matching \${PREFIX}*"
  187. if [ "\${count}" -le "\${KEEP}" ]; then
  188. rm -f "\${tags_file}"
  189. continue
  190. fi
  191. del_count=\$((count - KEEP))
  192. deleted=0
  193. while IFS= read -r t; do
  194. if [ -z "\${t}" ]; then
  195. continue
  196. fi
  197. if [ "\${t}" = "\${CURRENT}" ] || [ "\${t}" = "\${LATEST}" ]; then
  198. continue
  199. fi
  200. if [ "\${deleted}" -ge "\${del_count}" ]; then
  201. break
  202. fi
  203. echo ">>> DELETE Harbor tag \${repo}:\${t}"
  204. if ! curl -fsS -X DELETE -u "\${HARBOR_USER}:\${HARBOR_PASS}" \\
  205. "http://\${REG}/api/v2.0/projects/\${PROJ}/repositories/\${enc_repo}/artifacts/\${t}/tags/\${t}"; then
  206. echo ">>> WARN: delete failed \${repo}:\${t} (check robot delete permission)"
  207. fi
  208. deleted=\$((deleted + 1))
  209. done < "\${tags_file}"
  210. rm -f "\${tags_file}"
  211. done
  212. """
  213. }
  214. pipeline {
  215. agent any
  216. options {
  217. buildDiscarder(logRotator(numToKeepStr: '5'))
  218. disableConcurrentBuilds()
  219. timestamps()
  220. timeout(time: 90, unit: 'MINUTES')
  221. }
  222. parameters {
  223. string(
  224. name: 'GIT_BRANCH',
  225. defaultValue: 'uat-20260202',
  226. trim: true,
  227. description: 'Git branch name only (e.g. uat-20260202). Do not prefix origin/'
  228. )
  229. booleanParam(name: 'FORCE_UPDATE', defaultValue: false, description: 'mvn -U (routine builds leave unchecked for speed)')
  230. booleanParam(name: 'ALLOW_SNAPSHOTS', defaultValue: true, description: 'allow SNAPSHOT deps')
  231. booleanParam(
  232. name: 'PUSH_TO_HARBOR',
  233. defaultValue: true,
  234. description: 'After Maven: docker build + push to Harbor (tags uat-latest and uat-build-<N>). Uncheck for jar-only UAT deploy.'
  235. )
  236. choice(
  237. name: 'HARBOR_PUSH_SCOPE',
  238. choices: [
  239. 'all-java-services',
  240. 'gateway-only',
  241. 'store-only',
  242. 'second-only',
  243. 'store-platform-only',
  244. 'lawyer-only',
  245. 'job-only',
  246. 'dining-only',
  247. ],
  248. description: 'Only when PUSH_TO_HARBOR=true; default=all seven; *-only=one service'
  249. )
  250. string(name: 'HARBOR_REGISTRY', defaultValue: '39.105.153.68', trim: true)
  251. string(name: 'HARBOR_PROJECT', defaultValue: 'alien_cloud', trim: true)
  252. booleanParam(
  253. name: 'HARBOR_PRUNE_OLD_TAGS',
  254. defaultValue: true,
  255. description: 'After push: delete old uat-build-* tags in Harbor, keep last N per repo (never deletes uat-latest)'
  256. )
  257. string(name: 'HARBOR_KEEP_TAG_COUNT', defaultValue: '10', trim: true,
  258. description: 'How many uat-build-* tags to keep per repository')
  259. booleanParam(
  260. name: 'HARBOR_PUSH_PARALLEL',
  261. defaultValue: true,
  262. description: 'Parallel per service (context prep + Harbor API archive); docker build/push serialized via flock'
  263. )
  264. }
  265. environment {
  266. MAVEN_HOME = tool '3.6.3'
  267. PATH = "${MAVEN_HOME}/bin:${env.PATH}"
  268. GIT_URL = 'http://8.152.195.41:3000/alien/alien_cloud'
  269. GIT_CREDENTIALS = 'zhanghaomimapingzheng'
  270. HARBOR_CREDENTIALS = 'harbor-robot-alien'
  271. UAT_HARBOR_LATEST_TAG = 'uat-latest'
  272. UAT_HARBOR_BUILD_TAG = "uat-build-${env.BUILD_NUMBER}"
  273. DOCKERFILE_JAVA = 'docs/jenkins/produ/docker/Dockerfile.java-service'
  274. MAVEN_LOCAL_REPO = '/var/jenkins_home/.m2/repository'
  275. }
  276. stages {
  277. stage('Checkout') {
  278. steps {
  279. script {
  280. // Do not use env.GIT_BRANCH — Jenkins SCM plugin injects origin/<branch> and overwrites it.
  281. def branch = normalizeGitBranch(params.GIT_BRANCH)
  282. if (!branch) {
  283. error('GIT_BRANCH is required')
  284. }
  285. env.UAT_GIT_BRANCH = branch
  286. if (params.GIT_BRANCH?.trim() != branch) {
  287. echo ">>> GIT_BRANCH normalized: '${params.GIT_BRANCH}' -> '${branch}'"
  288. }
  289. echo ">>> Checkout branch: ${branch}"
  290. sh """
  291. set -e
  292. git fetch origin
  293. git checkout -B ${branch} origin/${branch}
  294. git log -1 --oneline
  295. """
  296. }
  297. }
  298. }
  299. stage('Prepare Maven Settings') {
  300. steps {
  301. script {
  302. writeFile file: 'settings.xml', text: """<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
  303. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  304. xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
  305. <mirrors>
  306. <mirror>
  307. <id>aliyun-central</id>
  308. <mirrorOf>central</mirrorOf>
  309. <name>Aliyun Central</name>
  310. <url>https://maven.aliyun.com/repository/central</url>
  311. </mirror>
  312. </mirrors>
  313. <profiles>
  314. <profile>
  315. <id>repo-mix</id>
  316. <repositories>
  317. <repository>
  318. <id>aliyunmaven</id>
  319. <name>Aliyun Maven</name>
  320. <url>https://maven.aliyun.com/repository/public</url>
  321. <releases><enabled>true</enabled><updatePolicy>daily</updatePolicy></releases>
  322. <snapshots><enabled>false</enabled></snapshots>
  323. </repository>
  324. <repository>
  325. <id>central</id>
  326. <name>Maven Central</name>
  327. <url>https://repo.maven.apache.org/maven2</url>
  328. <releases><enabled>true</enabled><updatePolicy>daily</updatePolicy></releases>
  329. <snapshots><enabled>false</enabled></snapshots>
  330. </repository>
  331. <repository>
  332. <id>spring-milestones</id>
  333. <name>Spring Milestones</name>
  334. <url>https://repo.spring.io/milestone</url>
  335. <releases><enabled>true</enabled><updatePolicy>daily</updatePolicy></releases>
  336. <snapshots><enabled>false</enabled></snapshots>
  337. </repository>
  338. <repository>
  339. <id>spring-snapshots</id>
  340. <name>Spring Snapshots</name>
  341. <url>https://repo.spring.io/snapshot</url>
  342. <releases><enabled>false</enabled></releases>
  343. <snapshots><enabled>true</enabled><updatePolicy>daily</updatePolicy></snapshots>
  344. </repository>
  345. </repositories>
  346. <pluginRepositories>
  347. <pluginRepository>
  348. <id>aliyunmaven</id>
  349. <url>https://maven.aliyun.com/repository/public</url>
  350. <releases><enabled>true</enabled></releases>
  351. <snapshots><enabled>false</enabled></snapshots>
  352. </pluginRepository>
  353. <pluginRepository>
  354. <id>central</id>
  355. <url>https://repo.maven.apache.org/maven2</url>
  356. <releases><enabled>true</enabled></releases>
  357. <snapshots><enabled>false</enabled></snapshots>
  358. </pluginRepository>
  359. <pluginRepository>
  360. <id>spring-milestones</id>
  361. <url>https://repo.spring.io/milestone</url>
  362. <releases><enabled>true</enabled></releases>
  363. <snapshots><enabled>false</enabled></snapshots>
  364. </pluginRepository>
  365. </pluginRepositories>
  366. </profile>
  367. </profiles>
  368. <activeProfiles>
  369. <activeProfile>repo-mix</activeProfile>
  370. </activeProfiles>
  371. </settings>
  372. """
  373. }
  374. }
  375. }
  376. stage('Maven Build') {
  377. steps {
  378. script {
  379. def updateFlag = params.FORCE_UPDATE ? '-U' : ''
  380. retry(2) {
  381. sh """
  382. set -e
  383. mvn -version
  384. unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY ALL_PROXY all_proxy no_proxy NO_PROXY || true
  385. export MAVEN_OPTS="-Xms512m -Xmx2048m -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true -Dmaven.wagon.http.ssl.ignore.validity.dates=true"
  386. mkdir -p ${MAVEN_LOCAL_REPO}
  387. mvn clean package -DskipTests -s settings.xml ${updateFlag} -e \\
  388. -T 1C -Dmaven.artifact.threads=8 \\
  389. -Dmaven.repo.local=${MAVEN_LOCAL_REPO}
  390. """
  391. }
  392. }
  393. }
  394. }
  395. stage('Push images to Harbor') {
  396. when {
  397. expression { return params.PUSH_TO_HARBOR == true }
  398. }
  399. steps {
  400. script {
  401. def reg = params.HARBOR_REGISTRY.trim()
  402. def proj = params.HARBOR_PROJECT.trim()
  403. def latestTag = env.UAT_HARBOR_LATEST_TAG
  404. def buildTag = env.UAT_HARBOR_BUILD_TAG
  405. def baseImage = "${reg}/${proj}/base/openjdk8-ffmpeg:v1"
  406. def dockerfile = env.DOCKERFILE_JAVA
  407. def allHarborServices = [
  408. [module: 'alien-gateway', repo: 'gateway', port: '8000', withLib: false],
  409. [module: 'alien-store', repo: 'store', port: '50014', withLib: true],
  410. [module: 'alien-second', repo: 'second', port: '50015', withLib: false],
  411. [module: 'alien-store-platform', repo: 'store-platform', port: '50016', withLib: false],
  412. [module: 'alien-lawyer', repo: 'lawyer', port: '50017', withLib: true],
  413. [module: 'alien-job', repo: 'job', port: '50108', withLib: false],
  414. [module: 'alien-dining', repo: 'dining', port: '50019', withLib: false],
  415. ]
  416. def harborServices = filterHarborPushScope(allHarborServices, params.HARBOR_PUSH_SCOPE)
  417. echo ">>> HARBOR_PUSH_SCOPE=${params.HARBOR_PUSH_SCOPE} repos=${harborServices*.repo.join(',')}"
  418. withCredentials([usernamePassword(
  419. credentialsId: env.HARBOR_CREDENTIALS,
  420. usernameVariable: 'HARBOR_USER',
  421. passwordVariable: 'HARBOR_PASS',
  422. )]) {
  423. sh """
  424. set -e
  425. echo "\${HARBOR_PASS}" | docker login ${reg} -u "\${HARBOR_USER}" --password-stdin
  426. echo ">>> docker disk before Harbor push:"
  427. df -h /var/lib/docker 2>/dev/null || df -h / || true
  428. docker image prune -f 2>/dev/null || true
  429. """
  430. if (params.HARBOR_PUSH_PARALLEL) {
  431. def pushBranches = [:]
  432. harborServices.each { svc ->
  433. def s = svc
  434. pushBranches[s.repo] = {
  435. pushOneHarborImage(
  436. this, s, reg, proj, latestTag, buildTag,
  437. baseImage, dockerfile, env.WORKSPACE,
  438. )
  439. }
  440. }
  441. parallel pushBranches
  442. } else {
  443. harborServices.each { svc ->
  444. pushOneHarborImage(
  445. this, svc, reg, proj, latestTag, buildTag,
  446. baseImage, dockerfile, env.WORKSPACE,
  447. )
  448. }
  449. }
  450. sh """
  451. echo ">>> docker disk after Harbor push:"
  452. df -h /var/lib/docker 2>/dev/null || df -h / || true
  453. docker image prune -f 2>/dev/null || true
  454. """
  455. if (params.HARBOR_PRUNE_OLD_TAGS == true) {
  456. def keepN = (params.HARBOR_KEEP_TAG_COUNT ?: '10').trim() as int
  457. catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE') {
  458. pruneHarborUatTags(
  459. this, reg, proj, harborServices*.repo,
  460. keepN, 'uat-build-', buildTag, latestTag,
  461. )
  462. }
  463. }
  464. }
  465. echo ">>> Harbor latest: ${env.UAT_HARBOR_LATEST_TAG}; archived tag this run: ${env.UAT_HARBOR_BUILD_TAG}"
  466. echo ">>> Prod promote: SOURCE_TAG=${env.UAT_HARBOR_LATEST_TAG}"
  467. }
  468. }
  469. }
  470. stage('Deploy Services') {
  471. steps {
  472. script {
  473. def services = [
  474. 'alien-gateway:gateway-uat',
  475. 'alien-job:job-uat',
  476. 'alien-lawyer:lawyer-uat',
  477. 'alien-second:second-uat',
  478. 'alien-store:store-uat',
  479. 'alien-dining:dining-uat',
  480. 'alien-store-platform:store-platform-uat',
  481. ]
  482. def deployBranches = [:]
  483. services.each { item ->
  484. def parts = item.split(':')
  485. def moduleName = parts[0]
  486. def dirName = parts[1]
  487. deployBranches[dirName] = {
  488. deployOneUatService(this, moduleName, dirName, env.WORKSPACE)
  489. }
  490. }
  491. parallel deployBranches
  492. }
  493. }
  494. }
  495. }
  496. post {
  497. always {
  498. sh 'rm -f settings.xml || true'
  499. script {
  500. if (!params.PUSH_TO_HARBOR) {
  501. echo '>>> Harbor push SKIPPED: PUSH_TO_HARBOR is false. On "Build with Parameters" check PUSH_TO_HARBOR.'
  502. }
  503. }
  504. }
  505. success {
  506. script {
  507. if (params.PUSH_TO_HARBOR) {
  508. echo ">>> Harbor latest: ${env.UAT_HARBOR_LATEST_TAG}"
  509. echo ">>> Prod promote: SOURCE_TAG=${env.UAT_HARBOR_LATEST_TAG}"
  510. }
  511. }
  512. }
  513. }
  514. }