github.com/verrazzano/verrazzano@v1.7.1/ci/JenkinsfileBackendTests (about) 1 // Copyright (c) 2021, 2024, Oracle and/or its affiliates. 2 // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl. 3 4 import groovy.transform.Field 5 6 @Field 7 def GIT_COMMIT_TO_USE = "" 8 @Field 9 def LAST_CLEAN_BACKEND_COMMIT = "" 10 @Field 11 def TESTS_FAILED = false 12 @Field 13 def storeLocation="" 14 @Field 15 def verrazzanoPrefix="verrazzano-" 16 @Field 17 def fullBundle="" 18 @Field 19 def liteBundle="" 20 @Field 21 def SUSPECT_LIST = "" 22 @Field 23 def COMPARISON_URL_ON_FAILURE = "" 24 25 @Field 26 def backendTestsUpToDate = false // If true, indicates that the backend tests already passed at the latest commit 27 @Field 28 def backendTestsUpToDateFailed = false // If true, indicates that the backend tests already ran and failed at the latest commit 29 30 // Non Fields 31 // def branchSpecificSchedule = getCronSchedule() 32 33 // File containing the links to download the Verrazzano distributions 34 @Field 35 def verrazzanoDistributionsFile = "verrazzano_distributions.html" 36 37 def ociOsRegion = "us-phoenix-1" 38 def ociOsBucket = "$OCI_OS_BUCKET" 39 def urlTriggerBranchName = env.BRANCH_NAME.replace('/', '%2F') 40 def lastStableCommitFile = "last-stable-commit.txt" 41 42 pipeline { 43 options { 44 timeout(time: 12, unit: 'HOURS') 45 skipDefaultCheckout true 46 disableConcurrentBuilds() 47 timestamps () 48 } 49 50 agent { 51 docker { 52 image "${RUNNER_DOCKER_IMAGE}" 53 args "${RUNNER_DOCKER_ARGS}" 54 registryUrl "${RUNNER_DOCKER_REGISTRY_URL}" 55 registryCredentialsId 'ocir-pull-and-push-account' 56 label "pipeline-job-large" 57 } 58 } 59 60 // triggers { 61 // cron(branchSpecificSchedule) 62 63 // There is an issue with URLTrigger for backend tests where the polling URL maintains a global state that does not work with specific jobs. 64 // Therefore, the below code is disabled until the global state issue is fixed. 65 // Ex: - Each job has an URL Trigger poll cron schedule 66 // - When a job’s cron triggers, it checks the state of whether the specific URL X has changed or not. 67 // - If it has changed, then it triggers only the job whose cron schedule fired off (no other jobs that look for that URL X will trigger) 68 // - Any other jobs that wake up and poll on URL X will not see it as changing unless it actually changed again since this time 69 70 // URLTrigger( 71 // cronTabSpec: branchSpecificSchedule, 72 // entries: [ 73 // URLTriggerEntry( 74 // url: "https://objectstorage.${ociOsRegion}.oraclecloud.com/n/${OS_NAMESPACE_URL_TRIGGER}/b/${ociOsBucket}/o/${urlTriggerBranchName}/${lastStableCommitFile}", 75 // checkETag: false, 76 // checkStatus: true, 77 // statusCode: 403, 78 // checkLastModificationDate: true, 79 // timeout: 200, 80 // requestHeaders: [ 81 // RequestHeader( headerName: "Accept" , headerValue: "application/json" ) 82 // ], 83 // contentTypes: [ 84 // MD5Sum() 85 // ] 86 // ) 87 // ] 88 // ) 89 // } 90 91 parameters { 92 string (name: 'TAGGED_TESTS', 93 defaultValue: '', 94 description: 'A comma separated list of build tags for tests that should be executed (e.g. unstable_test). Default:', 95 trim: true) 96 string (name: 'INCLUDED_TESTS', 97 defaultValue: '.*', 98 description: 'A regex matching any fully qualified test file that should be executed (e.g. examples/helidon/). Default: .*', 99 trim: true) 100 string (name: 'EXCLUDED_TESTS', 101 defaultValue: '_excluded_test', 102 description: 'A regex matching any fully qualified test file that should not be executed (e.g. multicluster/|_excluded_test). Default: _excluded_test', 103 trim: true) 104 booleanParam (description: 'Force execution of the tests even if up-to-date', name: 'FORCE', defaultValue: false) 105 booleanParam (description: 'Skip test execution (for debugging)', name: 'DRY_RUN', defaultValue: false) 106 } 107 108 environment { 109 IS_BACKEND_PIPELINE = "true" 110 OCIR_SCAN_COMPARTMENT = credentials('ocir-scan-compartment') 111 OCIR_SCAN_TARGET = credentials('ocir-scan-target') 112 OCIR_SCAN_REGISTRY = credentials('ocir-scan-registry') 113 OCIR_SCAN_REPOSITORY_PATH = credentials('ocir-scan-repository-path') 114 DOCKER_SCAN_CREDS = credentials('v8odev-ocir') 115 DOCKER_CREDS = credentials('github-packages-credentials-rw') 116 DOCKER_REPO = 'ghcr.io' 117 118 OCI_CLI_AUTH="instance_principal" 119 OCI_OS_NAMESPACE = credentials('oci-os-namespace') 120 CLEAN_BRANCH_NAME = "${env.BRANCH_NAME.replace("/", "%2F")}" 121 SERVICE_KEY = credentials('PAGERDUTY_SERVICE_KEY') 122 123 STABLE_COMMIT_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-stable-commit.txt" 124 LAST_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-backend-run-commit.txt" 125 CLEAN_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}-last-clean-backend-test/verrazzano_backend-commit.txt" 126 127 STABLE_COMMIT_LOCATION = "${WORKSPACE}/last-stable-commit.txt" 128 LAST_BACKEND_LOCATION = "${WORKSPACE}/last-backend-run-commit.txt" 129 CLEAN_BACKEND_LOCATION = "${WORKSPACE}/last-clean-backend-commit.txt" 130 131 OCI_OS_REGION="us-phoenix-1" 132 133 PIPELINE_OWNERS = credentials('backendtests-owners') 134 } 135 136 // This job runs against the latest stable master commit. That is defined as the last clean master build and test run whose 137 // commit has been stored in object storage. This job will fetch that commit from master and run extended tests using that. 138 // This job is NOT currently setup to run extended tests from other branches, if you need to run those extended jobs you will 139 // need to run those against your branch individually. 140 141 stages { 142 stage('Check last clean backend') { 143 steps { 144 sh """ 145 oci --region us-phoenix-1 os object get --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${STABLE_COMMIT_OS_LOCATION} --file ${STABLE_COMMIT_LOCATION} 146 """ 147 148 script { 149 // Check if there is already a clean backend run at this commit already, and set the display name if 150 // it already is tested, or if doing a special run type (dry run, etc...) 151 preliminaryChecks() 152 } 153 } 154 } 155 stage('Clean workspace and checkout') { 156 when { 157 allOf { 158 expression { return runTests() } 159 } 160 } 161 steps { 162 script { 163 cleanWorkspaceAndCheckout() 164 } 165 } 166 } 167 168 stage ('Backend Test Suites') { 169 when { 170 allOf { 171 expression { return runTests() } 172 } 173 } 174 parallel { 175 stage('Quick Create Tests') { 176 steps { 177 script { 178 build job: "/verrazzano-clusterAPI-qc-kind-tests/${CLEAN_BRANCH_NAME}", 179 parameters: [ 180 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 181 ], wait: false, propagate: false 182 } 183 } 184 } 185 186 stage('Upgrade Path Minor Release Tests') { 187 steps { 188 script { 189 build job: "/verrazzano-push-triggered-upgrade-minor-release-tests/${CLEAN_BRANCH_NAME}", 190 parameters: [ 191 string(name: 'N_JOBS_FOR_EACH_BATCH', value: '6'), 192 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 193 string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS), 194 string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS), 195 string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS), 196 ], wait: true, propagate: true 197 } 198 } 199 } 200 201 stage('vz analyze tool') { 202 steps { 203 retry(count: JOB_PROMOTION_RETRIES) { 204 script { 205 build job: "/verrazzano-analyze-tool-test/${CLEAN_BRANCH_NAME}", 206 parameters: [ 207 string(name: 'KUBERNETES_CLUSTER_VERSION', value: '1.27'), 208 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 209 ], wait: true 210 } 211 } 212 } 213 } 214 215 stage('A la carte tests') { 216 steps { 217 script { 218 build job: "/verrazzano-a-la-carte-triggered/${CLEAN_BRANCH_NAME}", 219 parameters: [ 220 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 221 ], wait: true, propagate: true 222 } 223 } 224 } 225 226 stage('Examples on OKE OCI DNS Tests') { 227 steps { 228 retry(count: JOB_PROMOTION_RETRIES) { 229 script { 230 build job: "verrazzano-new-oci-dns-examples-tests/${CLEAN_BRANCH_NAME}", 231 parameters: [ 232 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 233 string(name: 'TEST_ENV', value: "ocidns_oke"), 234 string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS), 235 string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS), 236 string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS) 237 ], wait: true 238 } 239 } 240 } 241 post { 242 failure { 243 script { 244 TESTS_FAILED = true 245 } 246 } 247 } 248 } 249 } 250 } 251 stage('Update Last Clean Backend Test') { 252 when { 253 allOf { 254 expression { return runTests() } 255 expression { TESTS_FAILED == false } 256 } 257 } 258 environment { 259 GIT_COMMIT_USED = "${env.GIT_COMMIT}" 260 } 261 steps { 262 script { 263 sh """ 264 # Update the clean backend commit 265 echo "git-commit=${GIT_COMMIT_USED}" > commit-that-passed.txt 266 oci --region ${OCI_OS_REGION} os object put --force --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${CLEAN_BRANCH_NAME}-last-clean-backend-test/verrazzano_backend-commit.txt --file commit-that-passed.txt 267 """ 268 } 269 } 270 } 271 } 272 post { 273 always { 274 script { 275 sh """ 276 # Update the last backend commit 277 echo "git-commit=${env.GIT_COMMIT}" > commit-used.txt 278 oci --region ${OCI_OS_REGION} os object put --force --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${LAST_BACKEND_OS_LOCATION} --file commit-used.txt 279 """ 280 } 281 } 282 failure { 283 script { 284 failedOrAborted() 285 } 286 } 287 aborted { 288 script { 289 failedOrAborted() 290 } 291 } 292 cleanup { 293 deleteDir() 294 } 295 } 296 } 297 298 def failedOrAborted() { 299 if (isAlertingEnabled()) { 300 if (isPagerDutyEnabled()) { 301 pagerduty(resolve: false, serviceKey: "$SERVICE_KEY", 302 incDescription: "Verrazzano Backend Tests: ${env.JOB_NAME} - Failed", 303 incDetails: "Job Failed - \"${env.JOB_NAME}\" build: ${env.BUILD_NUMBER}\n\nView the log at:\n ${env.BUILD_URL}\n\nBlue Ocean:\n${env.RUN_DISPLAY_URL}") 304 } 305 slackSend ( channel: "$SLACK_ALERT_CHANNEL", message: "Job Failed - \"${env.JOB_NAME}\" build: ${env.BUILD_NUMBER}\n\nView the log at:\n ${env.BUILD_URL}\n\nBlue Ocean:\n${env.RUN_DISPLAY_URL}\n\nSuspects:\n${SUSPECT_LIST} ${PIPELINE_OWNERS}\n\nChange comparison: ${COMPARISON_URL_ON_FAILURE}" ) 306 echo "done alerts" 307 } 308 } 309 310 // Preliminary job checks and display updates 311 def preliminaryChecks() { 312 // Get the last stable commit ID to pass the triggered tests 313 def stableCommitProps = readProperties file: "${STABLE_COMMIT_LOCATION}" 314 GIT_COMMIT_TO_USE = stableCommitProps['git-commit'] 315 echo "Last stable commit: ${GIT_COMMIT_TO_USE}" 316 317 LAST_CLEAN_BACKEND_COMMIT=getLastCleanBackendCommit() 318 echo "Last clean backend commit: ${LAST_CLEAN_BACKEND_COMMIT}" 319 320 if (LAST_CLEAN_BACKEND_COMMIT == GIT_COMMIT_TO_USE) { 321 backendTestsUpToDate = true 322 } else { 323 // Check if we are still at the same commit previously run (if so we know it wasn't clean and it failed in some way) 324 LAST_BACKEND_RUN_COMMIT=getLastBackendRunCommit() 325 if (LAST_BACKEND_RUN_COMMIT != null && LAST_BACKEND_RUN_COMMIT == GIT_COMMIT_TO_USE) { 326 backendTestsUpToDateFailed = true 327 } 328 } 329 330 echo "Up to date: ${backendTestsUpToDate}" 331 echo "Dry run: ${params.DRY_RUN}" 332 echo "Force run: ${params.FORCE}" 333 echo "Execute tests: " + runTests() 334 335 // Indicate in title if run is up-to-date or dry-run 336 if (params.DRY_RUN) { 337 currentBuild.displayName = "${currentBuild.displayName} : DRY-RUN" 338 } 339 if (backendTestsUpToDate) { 340 currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE" 341 } 342 if (params.FORCE) { 343 currentBuild.displayName = "${currentBuild.displayName} : FORCE" 344 } else if (backendTestsUpToDateFailed) { 345 currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE-FAILED" 346 currentBuild.result = 'FAILURE' 347 error('Failing the build since the current commit matches the commit of previously failing backend build') 348 } 349 350 if (runTests()) { 351 echo "Executing backend tests for commit ${GIT_COMMIT_TO_USE}" 352 } 353 } 354 355 def dockerLogins() { 356 try { 357 sh """ 358 echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin 359 """ 360 } catch(error) { 361 echo "docker login failed, retrying after sleep" 362 retry(4) { 363 sleep(30) 364 sh """ 365 echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin 366 """ 367 } 368 } 369 if (!(env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) { 370 try { 371 sh """ 372 echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin 373 """ 374 } catch(error) { 375 echo "docker login failed, retrying after sleep" 376 retry(4) { 377 sleep(30) 378 sh """ 379 echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin 380 """ 381 } 382 } 383 } 384 } 385 386 def scmCheckout() { 387 echo "${NODE_LABELS}" 388 echo "SCM checkout of ${GIT_COMMIT_TO_USE}" 389 def scmInfo = checkout([ 390 $class: 'GitSCM', 391 branches: [[name: GIT_COMMIT_TO_USE]], 392 doGenerateSubmoduleConfigurations: false, 393 extensions: [], 394 submoduleCfg: [], 395 userRemoteConfigs: [[url: env.SCM_VERRAZZANO_GIT_URL]]]) 396 env.GIT_COMMIT = scmInfo.GIT_COMMIT 397 env.GIT_BRANCH = scmInfo.GIT_BRANCH 398 echo "SCM checkout of ${env.GIT_BRANCH} at ${env.GIT_COMMIT}" 399 // If the commit we were handed is not what the SCM says we are using, fail 400 if (!env.GIT_COMMIT.equals(GIT_COMMIT_TO_USE)) { 401 error( "SCM didn't checkout the commit we expected. Expected: ${GIT_COMMIT_TO_USE}, Found: ${scmInfo.GIT_COMMIT}") 402 } 403 404 if (LAST_CLEAN_BACKEND_COMMIT != null) { 405 COMPARISON_URL_ON_FAILURE = "https://github.com/verrazzano/verrazzano/compare/${LAST_CLEAN_BACKEND_COMMIT}...${GIT_COMMIT_TO_USE}" 406 def lastClean = "${LAST_CLEAN_BACKEND_COMMIT}" 407 def currentStable = "${GIT_COMMIT_TO_USE}" 408 def commitList = getCommitListFromGitLog(lastClean, currentStable) 409 withCredentials([file(credentialsId: 'jenkins-to-slack-users', variable: 'JENKINS_TO_SLACK_JSON')]) { 410 def userMappings = readJSON file: JENKINS_TO_SLACK_JSON 411 SUSPECT_LIST = getSuspectList(commitList, userMappings) 412 echo "Suspect list: ${SUSPECT_LIST}" 413 } 414 } 415 echo "URL if fails: ${COMPARISON_URL_ON_FAILURE}" 416 } 417 418 def cleanWorkspaceAndCheckout() { 419 scmCheckout() 420 dockerLogins() 421 TIMESTAMP = sh(returnStdout: true, script: "date +%Y%m%d%H%M%S").trim() 422 SHORT_COMMIT_HASH = sh(returnStdout: true, script: "git rev-parse --short=8 HEAD").trim() 423 // update the description with some meaningful info 424 currentBuild.description = SHORT_COMMIT_HASH + " : " + env.GIT_COMMIT + " : " + GIT_COMMIT_TO_USE 425 storeLocation="ephemeral/${env.BRANCH_NAME}/${SHORT_COMMIT_HASH}" 426 } 427 428 // Returns the last clean commit for the backends, or null if the commit file does not exist yet. 429 // - fails the pipeline if any error other than 404 is returned by the OCI CLI 430 def getLastCleanBackendCommit() { 431 lastBackendCommitCommandOutput = sh ( 432 label: "Get last clean backend commit ID", 433 script: "oci --region us-phoenix-1 os object get --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${CLEAN_BACKEND_OS_LOCATION} --file ${CLEAN_BACKEND_LOCATION} 2>&1 || true", 434 returnStdout: true 435 ).trim() 436 echo "command out: ${lastBackendCommitCommandOutput}" 437 if (lastBackendCommitCommandOutput.length() > 0) { 438 // We can get warning messages here as well even when the command succeeded, so be more precise on the checking 439 if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) { 440 // If we think we had a status: NNN, we ignore 404 and fail for others 441 assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}" 442 } else { 443 // If we got here, we have some message that may or may not be an error. If we don't see the file, we assume it was an error 444 sh """ 445 if [ ! -f ${CLEAN_BACKEND_LOCATION} ]; then 446 echo "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}" 447 exit 1 448 fi 449 """ 450 } 451 } 452 // Get the commit ID for the last known clean pass of the Backend tests 453 def cleanBackendsCommitProps = readProperties file: "${CLEAN_BACKEND_LOCATION}" 454 return cleanBackendsCommitProps['git-commit'] 455 } 456 457 // Returns the last run commit for the Backend, or null if the commit file does not exist yet. 458 // - fails the pipeline if any error other than 404 is returned by the OCI CLI 459 def getLastBackendRunCommit() { 460 lastBackendCommitCommandOutput = sh ( 461 label: "Get last clean backend commit ID", 462 script: "oci --region us-phoenix-1 os object get --namespace ${OCI_OS_NAMESPACE} -bn ${OCI_OS_BUCKET} --name ${LAST_BACKEND_OS_LOCATION} --file ${LAST_BACKEND_LOCATION} 2>&1 || true", 463 returnStdout: true 464 ).trim() 465 echo "command out: ${lastBackendCommitCommandOutput}" 466 if (lastBackendCommitCommandOutput.length() > 0) { 467 // We can get warning messages here as well even when the command succeeded, so be more precise on the checking 468 if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) { 469 // If we think we had a status: NNN, we ignore 404 and fail for others 470 assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}" 471 } else { 472 // If we got here, we have some message that may or may not be an error. If we don't see the file, we assume it was an error 473 sh """ 474 if [ ! -f ${LAST_BACKEND_LOCATION} ]; then 475 echo "An unexpected error occurred getting last backend run commit from ObjectStore: ${lastBackendCommitCommandOutput}" 476 exit 1 477 fi 478 """ 479 } 480 } 481 // Get the commit ID for the last known clean pass of the Backend tests 482 def lastBackendCommitProps = readProperties file: "${LAST_BACKEND_LOCATION}" 483 return lastBackendCommitProps['git-commit'] 484 } 485 486 // Checks all the conditions gating test execution and collates the result 487 def runTests() { 488 return params.FORCE || ( ! backendTestsUpToDate && ! backendTestsUpToDateFailed && ! params.DRY_RUN ) 489 } 490 491 def isAlertingEnabled() { 492 // this controls whether any alerting happens for these tests 493 if (NOTIFY_BACKEND_FAILURES.equals("true") && (env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) { 494 echo "Alert notifications enabled for ${env.BRANCH_NAME}" 495 return true 496 } 497 return false 498 } 499 500 def isPagerDutyEnabled() { 501 // this additionally controls whether PD alerts are enabled (note that you must also enable alerting in general as well if you want these) 502 if (NOTIFY_PAGERDUTY_BACKEND_FAILURES.equals("true")) { 503 echo "Pager-Duty notifications enabled via global override setting" 504 return true 505 } 506 return false 507 } 508 509 // def getCronSchedule() { 510 // if (env.BRANCH_NAME.equals("master")) { 511 // return "@weekly" 512 // } else if (env.BRANCH_NAME.startsWith("release-")) { 513 // return "@weekly" 514 // } 515 // return "" 516 // } 517 518 // Called in Stage Clean workspace and checkout steps 519 def getCommitListFromGitLog(lastClean, currentStable) { 520 echo "Checking for change sets" 521 def commitList = sh(returnStdout: true, script: "git log ${lastClean}...${currentStable} --oneline | cut -d \" \" -f 1").trim().split('\n') 522 for (int i = 0; i < commitList.size(); i++) { 523 echo "Found commit id: ${commitList[i]}" 524 } 525 return commitList 526 } 527 528 def trimIfGithubNoreplyUser(userIn) { 529 if (userIn == null) { 530 echo "Not a github noreply user, not trimming: ${userIn}" 531 return userIn 532 } 533 if (userIn.matches(".*\\+.*@users.noreply.github.com.*")) { 534 def userOut = userIn.substring(userIn.indexOf("+") + 1, userIn.indexOf("@")) 535 return userOut; 536 } 537 if (userIn.matches(".*<.*@users.noreply.github.com.*")) { 538 def userOut = userIn.substring(userIn.indexOf("<") + 1, userIn.indexOf("@")) 539 return userOut; 540 } 541 if (userIn.matches(".*@users.noreply.github.com")) { 542 def userOut = userIn.substring(0, userIn.indexOf("@")) 543 return userOut; 544 } 545 echo "Not a github noreply user, not trimming: ${userIn}" 546 return userIn 547 } 548 549 def getSuspectList(commitList, userMappings) { 550 def retValue = "" 551 def suspectList = [] 552 if (commitList == null || commitList.size() == 0) { 553 echo "No commits to form suspect list" 554 } else { 555 for (int i = 0; i < commitList.size(); i++) { 556 def id = commitList[i] 557 try { 558 def gitAuthor = sh( 559 script: "git log --format='%ae' '$id^!'", 560 returnStdout: true 561 ).trim() 562 if (gitAuthor != null) { 563 def author = trimIfGithubNoreplyUser(gitAuthor) 564 echo "DEBUG: author: ${gitAuthor}, ${author}, id: ${id}" 565 if (userMappings.containsKey(author)) { 566 def slackUser = userMappings.get(author) 567 if (!suspectList.contains(slackUser)) { 568 echo "Added ${slackUser} as suspect" 569 retValue += " ${slackUser}" 570 suspectList.add(slackUser) 571 } 572 } else { 573 // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated 574 if (!suspectList.contains(author)) { 575 echo "Added ${author} as suspect" 576 retValue += " ${author}" 577 suspectList.add(author) 578 } 579 } 580 } else { 581 echo "No author returned from git" 582 } 583 } catch (Exception e) { 584 echo "INFO: Problem processing commit ${id}, skipping commit: " + e.toString() 585 } 586 } 587 } 588 def startedByUser = ""; 589 def causes = currentBuild.getBuildCauses() 590 echo "causes: " + causes.toString() 591 for (cause in causes) { 592 def causeString = cause.toString() 593 echo "current cause: " + causeString 594 def causeInfo = readJSON text: causeString 595 if (causeInfo.userId != null) { 596 startedByUser = causeInfo.userId 597 } 598 } 599 600 if (startedByUser.length() > 0) { 601 echo "Build was started by a user, adding them to the suspect notification list: ${startedByUser}" 602 def author = trimIfGithubNoreplyUser(startedByUser) 603 echo "DEBUG: author: ${startedByUser}, ${author}" 604 if (userMappings.containsKey(author)) { 605 def slackUser = userMappings.get(author) 606 if (!suspectList.contains(slackUser)) { 607 echo "Added ${slackUser} as suspect" 608 retValue += " ${slackUser}" 609 suspectList.add(slackUser) 610 } 611 } else { 612 // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated 613 if (!suspectList.contains(author)) { 614 echo "Added ${author} as suspect" 615 retValue += " ${author}" 616 suspectList.add(author) 617 } 618 } 619 } else { 620 echo "Build not started by a user, not adding to notification list" 621 } 622 echo "returning suspect list: ${retValue}" 623 return retValue 624 } 625 626 @NonCPS 627 List extractReleaseTags(final String fileContent) { 628 List releases = [] 629 fileContent.eachLine { tag -> 630 releases << tag 631 } 632 return releases 633 } 634 635 def getLatestReleaseVersion() { 636 final String releaseTags = readFile(file: "${workspace}/tags.txt") 637 list gitTags = extractReleaseTags(releaseTags) 638 echo "gitTags = ${gitTags}" 639 return gitTags.pop() 640 }