Jenkinsfile 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #!/usr/bin/env groovy
  2. /*
  3. * This Jenkinsfile is intended to run on https://ci.jenkins.io and may fail anywhere else.
  4. * It makes assumptions about plugins being installed, labels mapping to nodes that can build what is needed, etc.
  5. */
  6. def failFast = false
  7. def numBuildToKeep = env.CHANGE_ID ? '10' : '50'
  8. properties([
  9. buildDiscarder(logRotator(numToKeepStr: numBuildToKeep, artifactNumToKeepStr: '3')),
  10. disableConcurrentBuilds(abortPrevious: true)
  11. ])
  12. def axes = [
  13. platforms: ['linux', 'windows'],
  14. jdks: [21, 25],
  15. ]
  16. stage('Record build') {
  17. retry(conditions: [kubernetesAgent(handleNonKubernetes: true), nonresumable()], count: 2) {
  18. node('maven-21-nonspot') {
  19. infra.checkoutSCM()
  20. /*
  21. * Record the primary build for this CI job.
  22. */
  23. withCredentials([string(credentialsId: 'launchable-jenkins-jenkins', variable: 'LAUNCHABLE_TOKEN')]) {
  24. /*
  25. * TODO Add the commits of the transitive closure of the Jenkins WAR under test to this build.
  26. */
  27. // Replace URL encoded characters with '-' because Launchable rejects '%2F' in build name
  28. def launchableName = env.BUILD_TAG.replaceAll('(%[0-9A-Fa-f]{2})+', '-')
  29. sh "launchable verify && launchable record build --name ${launchableName} --source jenkinsci/jenkins=."
  30. axes.values().combinations {
  31. def (platform, jdk) = it
  32. if (platform == 'windows' && jdk != axes.jdks.last()) {
  33. return // unnecessary use of hardware
  34. }
  35. def sessionFile = "launchable-session-${platform}-jdk${jdk}.txt"
  36. sh "launchable record session --build ${launchableName} --flavor platform=${platform} --flavor jdk=${jdk} >${sessionFile}"
  37. stash name: sessionFile, includes: sessionFile
  38. }
  39. }
  40. /*
  41. * Record commits for use in downstream CI jobs that may consume this artifact.
  42. */
  43. withCredentials([string(credentialsId: 'launchable-jenkins-acceptance-test-harness', variable: 'LAUNCHABLE_TOKEN')]) {
  44. sh 'launchable verify && launchable record commit'
  45. }
  46. withCredentials([string(credentialsId: 'launchable-jenkins-bom', variable: 'LAUNCHABLE_TOKEN')]) {
  47. sh 'launchable verify && launchable record commit'
  48. }
  49. }
  50. }
  51. }
  52. def builds = [:]
  53. axes.values().combinations {
  54. def (platform, jdk) = it
  55. if (platform == 'windows' && jdk != axes.jdks.last()) {
  56. return // unnecessary use of hardware
  57. }
  58. builds["${platform}-jdk${jdk}"] = {
  59. // see https://github.com/jenkins-infra/documentation/blob/master/ci.adoc#node-labels for information on what node types are available
  60. def agentContainerLabel = 'maven-' + jdk
  61. if (platform == 'windows') {
  62. agentContainerLabel += '-windows'
  63. }
  64. agentContainerLabel += '-nonspot'
  65. retry(conditions: [kubernetesAgent(handleNonKubernetes: true), nonresumable()], count: 2) {
  66. node(agentContainerLabel) {
  67. // First stage is actually checking out the source. Since we're using Multibranch
  68. // currently, we can use "checkout scm".
  69. stage("${platform.capitalize()} - JDK ${jdk} - Checkout") {
  70. infra.checkoutSCM()
  71. }
  72. def tmpDir = pwd(tmp: true)
  73. def changelistF = "${tmpDir}/changelist"
  74. def m2repo = "${tmpDir}/m2repo"
  75. def session
  76. // Now run the actual build.
  77. stage("${platform.capitalize()} - JDK ${jdk} - Build / Test") {
  78. timeout(time: 6, unit: 'HOURS') {
  79. dir(tmpDir) {
  80. def sessionFile = "launchable-session-${platform}-jdk${jdk}.txt"
  81. unstash sessionFile
  82. session = readFile(sessionFile).trim()
  83. }
  84. def mavenOptions = [
  85. '-Pdebug',
  86. '-Penable-jacoco',
  87. '--update-snapshots',
  88. "-Dmaven.repo.local=$m2repo",
  89. '-Dmaven.test.failure.ignore',
  90. '-DforkCount=2',
  91. '-Dspotbugs.failOnError=false',
  92. '-Dcheckstyle.failOnViolation=false',
  93. '-Dset.changelist',
  94. 'help:evaluate',
  95. '-Dexpression=changelist',
  96. "-Doutput=$changelistF",
  97. 'clean',
  98. 'install',
  99. ]
  100. if (env.CHANGE_ID && !pullRequest.labels.contains('full-test')) {
  101. def excludesFile
  102. def target = platform == 'windows' ? '30%' : '100%'
  103. withCredentials([string(credentialsId: 'launchable-jenkins-jenkins', variable: 'LAUNCHABLE_TOKEN')]) {
  104. if (isUnix()) {
  105. excludesFile = "${tmpDir}/excludes.txt"
  106. sh "launchable verify && launchable subset --session ${session} --target ${target} --get-tests-from-previous-sessions --output-exclusion-rules maven >${excludesFile}"
  107. } else {
  108. excludesFile = "${tmpDir}\\excludes.txt"
  109. bat "launchable verify && launchable subset --session ${session} --target ${target}% --get-tests-from-previous-sessions --output-exclusion-rules maven >${excludesFile}"
  110. }
  111. }
  112. mavenOptions.add(0, "-Dsurefire.excludesFile=${excludesFile}")
  113. }
  114. withChecks(name: 'Tests', includeStage: true) {
  115. realtimeJUnit(healthScaleFactor: 20.0, testResults: '*/target/surefire-reports/*.xml,target/vitest-reports/*.xml') {
  116. infra.runMaven(mavenOptions, jdk)
  117. if (isUnix()) {
  118. sh 'git add . && git diff --exit-code HEAD'
  119. }
  120. }
  121. }
  122. }
  123. }
  124. // Once we've built, archive the artifacts and the test results.
  125. stage("${platform.capitalize()} - JDK ${jdk} - Publish") {
  126. archiveArtifacts allowEmptyArchive: true, artifacts: '**/target/surefire-reports/*.dumpstream'
  127. // cli and war have been migrated to JUnit 5
  128. if (failFast && currentBuild.result == 'UNSTABLE') {
  129. error 'There were test failures; halting early'
  130. }
  131. if (platform == 'linux' && jdk == axes['jdks'][0]) {
  132. def folders = env.JOB_NAME.split('/')
  133. if (folders.length > 1) {
  134. discoverGitReferenceBuild(scm: folders[1])
  135. }
  136. recordCoverage(tools: [[parser: 'JACOCO', pattern: 'coverage/target/site/jacoco-aggregate/jacoco.xml']],
  137. sourceCodeRetention: 'MODIFIED', sourceDirectories: [[path: 'core/src/main/java']])
  138. echo "Recording static analysis results for '${platform.capitalize()}'"
  139. recordIssues(
  140. enabledForFailure: true,
  141. tools: [java()],
  142. filters: [excludeFile('.*Assert.java')],
  143. sourceCodeEncoding: 'UTF-8',
  144. skipBlames: true,
  145. trendChartType: 'TOOLS_ONLY'
  146. )
  147. recordIssues(
  148. enabledForFailure: true,
  149. tools: [javaDoc()],
  150. filters: [excludeFile('.*Assert.java')],
  151. sourceCodeEncoding: 'UTF-8',
  152. skipBlames: true,
  153. trendChartType: 'TOOLS_ONLY',
  154. qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]]
  155. )
  156. recordIssues([tool: spotBugs(pattern: '**/target/spotbugsXml.xml'),
  157. sourceCodeEncoding: 'UTF-8',
  158. skipBlames: true,
  159. trendChartType: 'TOOLS_ONLY',
  160. qualityGates: [[threshold: 1, type: 'NEW', unstable: true]]])
  161. recordIssues([tool: checkStyle(pattern: '**/target/checkstyle-result.xml'),
  162. sourceCodeEncoding: 'UTF-8',
  163. skipBlames: true,
  164. trendChartType: 'TOOLS_ONLY',
  165. qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]]])
  166. recordIssues([tool: esLint(pattern: '**/target/eslint-warnings.xml'),
  167. sourceCodeEncoding: 'UTF-8',
  168. skipBlames: true,
  169. trendChartType: 'TOOLS_ONLY',
  170. qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]]])
  171. recordIssues([tool: styleLint(pattern: '**/target/stylelint-warnings.xml'),
  172. sourceCodeEncoding: 'UTF-8',
  173. skipBlames: true,
  174. trendChartType: 'TOOLS_ONLY',
  175. qualityGates: [[threshold: 1, type: 'TOTAL', unstable: true]]])
  176. if (failFast && currentBuild.result == 'UNSTABLE') {
  177. error 'Static analysis quality gates not passed; halting early'
  178. }
  179. def changelist = readFile(changelistF)
  180. dir(m2repo) {
  181. archiveArtifacts(
  182. artifacts: "**/*$changelist/*$changelist*",
  183. excludes: '**/*.lastUpdated,**/jenkins-coverage*/,**/jenkins-test*/',
  184. allowEmptyArchive: true, // in case we forgot to reincrementalify
  185. fingerprint: true
  186. )
  187. }
  188. }
  189. withCredentials([string(credentialsId: 'launchable-jenkins-jenkins', variable: 'LAUNCHABLE_TOKEN')]) {
  190. if (isUnix()) {
  191. sh "launchable verify && launchable record tests --session ${session} --flavor platform=${platform} --flavor jdk=${jdk} maven './**/target/surefire-reports'"
  192. } else {
  193. bat "launchable verify && launchable record tests --session ${session} --flavor platform=${platform} --flavor jdk=${jdk} maven ./**/target/surefire-reports"
  194. }
  195. }
  196. }
  197. }
  198. }
  199. }
  200. }
  201. def athAxes = [
  202. platforms: ['linux'],
  203. jdks: [25],
  204. browsers: ['firefox'],
  205. ]
  206. athAxes.values().combinations {
  207. def (platform, jdk, browser) = it
  208. builds["ath-${platform}-jdk${jdk}-${browser}"] = {
  209. retry(conditions: [agent(), nonresumable()], count: 2) {
  210. node('docker-highmem') {
  211. // Just to be safe
  212. deleteDir()
  213. checkout scm
  214. withChecks(name: 'Tests', includeStage: true) {
  215. infra.withArtifactCachingProxy {
  216. sh "bash ath.sh ${jdk} ${browser}"
  217. }
  218. junit testResults: 'target/ath-reports/TEST-*.xml', testDataPublishers: [[$class: 'AttachmentPublisher']]
  219. }
  220. /*
  221. * Currently disabled, as the fact that this is a manually created subset will confuse Launchable,
  222. * which expects this to be a full build. When we implement subsetting, this can be re-enabled using
  223. * Launchable's subset rather than our own.
  224. */
  225. /*
  226. withCredentials([string(credentialsId: 'launchable-jenkins-acceptance-test-harness', variable: 'LAUNCHABLE_TOKEN')]) {
  227. sh "launchable verify && launchable record tests --no-build --flavor platform=${platform} --flavor jdk=${jdk} --flavor browser=${browser} maven './target/ath-reports'"
  228. }
  229. */
  230. }
  231. }
  232. }
  233. }
  234. builds.failFast = failFast
  235. parallel builds
  236. infra.maybePublishIncrementals()