github.com/verrazzano/verrazzano@v1.7.0/ci/JenkinsfileBackendTests (about) 1 // Copyright (c) 2021, 2023, 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 = "verrazzano-builds" 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 OCI_OS_BUCKET="verrazzano-builds" 121 OCI_OS_COMMIT_BUCKET="verrazzano-builds-by-commit" 122 CLEAN_BRANCH_NAME = "${env.BRANCH_NAME.replace("/", "%2F")}" 123 SERVICE_KEY = credentials('PAGERDUTY_SERVICE_KEY') 124 125 STABLE_COMMIT_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-stable-commit.txt" 126 LAST_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}/last-backend-run-commit.txt" 127 CLEAN_BACKEND_OS_LOCATION = "${CLEAN_BRANCH_NAME}-last-clean-backend-test/verrazzano_backend-commit.txt" 128 129 STABLE_COMMIT_LOCATION = "${WORKSPACE}/last-stable-commit.txt" 130 LAST_BACKEND_LOCATION = "${WORKSPACE}/last-backend-run-commit.txt" 131 CLEAN_BACKEND_LOCATION = "${WORKSPACE}/last-clean-backend-commit.txt" 132 133 OCI_OS_REGION="us-phoenix-1" 134 135 PIPELINE_OWNERS = credentials('backendtests-owners') 136 } 137 138 // This job runs against the latest stable master commit. That is defined as the last clean master build and test run whose 139 // commit has been stored in object storage. This job will fetch that commit from master and run extended tests using that. 140 // This job is NOT currently setup to run extended tests from other branches, if you need to run those extended jobs you will 141 // need to run those against your branch individually. 142 143 stages { 144 stage('Check last clean backend') { 145 steps { 146 sh """ 147 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} 148 """ 149 150 script { 151 // Check if there is already a clean backend run at this commit already, and set the display name if 152 // it already is tested, or if doing a special run type (dry run, etc...) 153 preliminaryChecks() 154 } 155 } 156 } 157 stage('Clean workspace and checkout') { 158 when { 159 allOf { 160 expression { return runTests() } 161 } 162 } 163 steps { 164 script { 165 cleanWorkspaceAndCheckout() 166 } 167 } 168 } 169 170 stage ('Backend Test Suites') { 171 when { 172 allOf { 173 expression { return runTests() } 174 } 175 } 176 parallel { 177 stage('Quick Create Tests') { 178 steps { 179 script { 180 build job: "/verrazzano-clusterAPI-qc-kind-tests/${CLEAN_BRANCH_NAME}", 181 parameters: [ 182 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 183 ], wait: false, propagate: false 184 } 185 } 186 } 187 188 stage('Upgrade Path Minor Release Tests') { 189 steps { 190 script { 191 build job: "/verrazzano-push-triggered-upgrade-minor-release-tests/${CLEAN_BRANCH_NAME}", 192 parameters: [ 193 string(name: 'N_JOBS_FOR_EACH_BATCH', value: '6'), 194 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 195 string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS), 196 string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS), 197 string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS), 198 ], wait: true, propagate: true 199 } 200 } 201 } 202 203 stage('vz analyze tool') { 204 environment { 205 SEARCH_HTTP_ENDPOINT = credentials('search-gw-url') 206 SEARCH_PASSWORD = "${PROMETHEUS_CREDENTIALS_PSW}" 207 SEARCH_USERNAME = "${PROMETHEUS_CREDENTIALS_USR}" 208 } 209 steps { 210 retry(count: JOB_PROMOTION_RETRIES) { 211 script { 212 build job: "/verrazzano-analyze-tool-test/${CLEAN_BRANCH_NAME}", 213 parameters: [ 214 string(name: 'KUBERNETES_CLUSTER_VERSION', value: '1.27'), 215 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 216 ], wait: true 217 } 218 } 219 } 220 } 221 222 stage('A la carte tests') { 223 steps { 224 script { 225 build job: "/verrazzano-a-la-carte-triggered/${CLEAN_BRANCH_NAME}", 226 parameters: [ 227 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 228 ], wait: true, propagate: true 229 } 230 } 231 } 232 233 stage('Examples on OKE OCI DNS Tests') { 234 steps { 235 retry(count: JOB_PROMOTION_RETRIES) { 236 script { 237 build job: "verrazzano-new-oci-dns-examples-tests/${CLEAN_BRANCH_NAME}", 238 parameters: [ 239 string(name: 'GIT_COMMIT_TO_USE', value: env.GIT_COMMIT), 240 string(name: 'TEST_ENV', value: "ocidns_oke"), 241 string(name: 'TAGGED_TESTS', value: params.TAGGED_TESTS), 242 string(name: 'INCLUDED_TESTS', value: params.INCLUDED_TESTS), 243 string(name: 'EXCLUDED_TESTS', value: params.EXCLUDED_TESTS) 244 ], wait: true 245 } 246 } 247 } 248 post { 249 failure { 250 script { 251 TESTS_FAILED = true 252 } 253 } 254 } 255 } 256 } 257 } 258 stage('Update Last Clean Backend Test') { 259 when { 260 allOf { 261 expression { return runTests() } 262 expression { TESTS_FAILED == false } 263 } 264 } 265 environment { 266 GIT_COMMIT_USED = "${env.GIT_COMMIT}" 267 } 268 steps { 269 script { 270 sh """ 271 # Update the clean backend commit 272 echo "git-commit=${GIT_COMMIT_USED}" > commit-that-passed.txt 273 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 274 """ 275 } 276 } 277 } 278 } 279 post { 280 always { 281 script { 282 sh """ 283 # Update the last backend commit 284 echo "git-commit=${env.GIT_COMMIT}" > commit-used.txt 285 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 286 """ 287 } 288 } 289 failure { 290 script { 291 failedOrAborted() 292 } 293 } 294 aborted { 295 script { 296 failedOrAborted() 297 } 298 } 299 cleanup { 300 deleteDir() 301 } 302 } 303 } 304 305 def failedOrAborted() { 306 if (isAlertingEnabled()) { 307 if (isPagerDutyEnabled()) { 308 pagerduty(resolve: false, serviceKey: "$SERVICE_KEY", 309 incDescription: "Verrazzano Backend Tests: ${env.JOB_NAME} - Failed", 310 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}") 311 } 312 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}" ) 313 echo "done alerts" 314 } 315 } 316 317 // Preliminary job checks and display updates 318 def preliminaryChecks() { 319 // Get the last stable commit ID to pass the triggered tests 320 def stableCommitProps = readProperties file: "${STABLE_COMMIT_LOCATION}" 321 GIT_COMMIT_TO_USE = stableCommitProps['git-commit'] 322 echo "Last stable commit: ${GIT_COMMIT_TO_USE}" 323 324 LAST_CLEAN_BACKEND_COMMIT=getLastCleanBackendCommit() 325 echo "Last clean backend commit: ${LAST_CLEAN_BACKEND_COMMIT}" 326 327 if (LAST_CLEAN_BACKEND_COMMIT == GIT_COMMIT_TO_USE) { 328 backendTestsUpToDate = true 329 } else { 330 // 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) 331 LAST_BACKEND_RUN_COMMIT=getLastBackendRunCommit() 332 if (LAST_BACKEND_RUN_COMMIT != null && LAST_BACKEND_RUN_COMMIT == GIT_COMMIT_TO_USE) { 333 backendTestsUpToDateFailed = true 334 } 335 } 336 337 echo "Up to date: ${backendTestsUpToDate}" 338 echo "Dry run: ${params.DRY_RUN}" 339 echo "Force run: ${params.FORCE}" 340 echo "Execute tests: " + runTests() 341 342 // Indicate in title if run is up-to-date or dry-run 343 if (params.DRY_RUN) { 344 currentBuild.displayName = "${currentBuild.displayName} : DRY-RUN" 345 } 346 if (backendTestsUpToDate) { 347 currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE" 348 } 349 if (params.FORCE) { 350 currentBuild.displayName = "${currentBuild.displayName} : FORCE" 351 } else if (backendTestsUpToDateFailed) { 352 currentBuild.displayName = "${currentBuild.displayName} : UP-TO-DATE-FAILED" 353 currentBuild.result = 'FAILURE' 354 error('Failing the build since the current commit matches the commit of previously failing backend build') 355 } 356 357 if (runTests()) { 358 echo "Executing backend tests for commit ${GIT_COMMIT_TO_USE}" 359 } 360 } 361 362 def dockerLogins() { 363 try { 364 sh """ 365 echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin 366 """ 367 } catch(error) { 368 echo "docker login failed, retrying after sleep" 369 retry(4) { 370 sleep(30) 371 sh """ 372 echo "${DOCKER_SCAN_CREDS_PSW}" | docker login ${env.OCIR_SCAN_REGISTRY} -u ${DOCKER_SCAN_CREDS_USR} --password-stdin 373 """ 374 } 375 } 376 if (!(env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) { 377 try { 378 sh """ 379 echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin 380 """ 381 } catch(error) { 382 echo "docker login failed, retrying after sleep" 383 retry(4) { 384 sleep(30) 385 sh """ 386 echo "${DOCKER_CREDS_PSW}" | docker login ${env.DOCKER_REPO} -u ${DOCKER_CREDS_USR} --password-stdin 387 """ 388 } 389 } 390 } 391 } 392 393 def scmCheckout() { 394 echo "${NODE_LABELS}" 395 echo "SCM checkout of ${GIT_COMMIT_TO_USE}" 396 def scmInfo = checkout([ 397 $class: 'GitSCM', 398 branches: [[name: GIT_COMMIT_TO_USE]], 399 doGenerateSubmoduleConfigurations: false, 400 extensions: [], 401 submoduleCfg: [], 402 userRemoteConfigs: [[url: env.SCM_VERRAZZANO_GIT_URL]]]) 403 env.GIT_COMMIT = scmInfo.GIT_COMMIT 404 env.GIT_BRANCH = scmInfo.GIT_BRANCH 405 echo "SCM checkout of ${env.GIT_BRANCH} at ${env.GIT_COMMIT}" 406 // If the commit we were handed is not what the SCM says we are using, fail 407 if (!env.GIT_COMMIT.equals(GIT_COMMIT_TO_USE)) { 408 error( "SCM didn't checkout the commit we expected. Expected: ${GIT_COMMIT_TO_USE}, Found: ${scmInfo.GIT_COMMIT}") 409 } 410 411 if (LAST_CLEAN_BACKEND_COMMIT != null) { 412 COMPARISON_URL_ON_FAILURE = "https://github.com/verrazzano/verrazzano/compare/${LAST_CLEAN_BACKEND_COMMIT}...${GIT_COMMIT_TO_USE}" 413 def lastClean = "${LAST_CLEAN_BACKEND_COMMIT}" 414 def currentStable = "${GIT_COMMIT_TO_USE}" 415 def commitList = getCommitListFromGitLog(lastClean, currentStable) 416 withCredentials([file(credentialsId: 'jenkins-to-slack-users', variable: 'JENKINS_TO_SLACK_JSON')]) { 417 def userMappings = readJSON file: JENKINS_TO_SLACK_JSON 418 SUSPECT_LIST = getSuspectList(commitList, userMappings) 419 echo "Suspect list: ${SUSPECT_LIST}" 420 } 421 } 422 echo "URL if fails: ${COMPARISON_URL_ON_FAILURE}" 423 } 424 425 def cleanWorkspaceAndCheckout() { 426 scmCheckout() 427 dockerLogins() 428 TIMESTAMP = sh(returnStdout: true, script: "date +%Y%m%d%H%M%S").trim() 429 SHORT_COMMIT_HASH = sh(returnStdout: true, script: "git rev-parse --short=8 HEAD").trim() 430 // update the description with some meaningful info 431 currentBuild.description = SHORT_COMMIT_HASH + " : " + env.GIT_COMMIT + " : " + GIT_COMMIT_TO_USE 432 storeLocation="ephemeral/${env.BRANCH_NAME}/${SHORT_COMMIT_HASH}" 433 } 434 435 // Returns the last clean commit for the backends, or null if the commit file does not exist yet. 436 // - fails the pipeline if any error other than 404 is returned by the OCI CLI 437 def getLastCleanBackendCommit() { 438 lastBackendCommitCommandOutput = sh ( 439 label: "Get last clean backend commit ID", 440 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", 441 returnStdout: true 442 ).trim() 443 echo "command out: ${lastBackendCommitCommandOutput}" 444 if (lastBackendCommitCommandOutput.length() > 0) { 445 // We can get warning messages here as well even when the command succeeded, so be more precise on the checking 446 if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) { 447 // If we think we had a status: NNN, we ignore 404 and fail for others 448 assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}" 449 } else { 450 // 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 451 sh """ 452 if [ ! -f ${CLEAN_BACKEND_LOCATION} ]; then 453 echo "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}" 454 exit 1 455 fi 456 """ 457 } 458 } 459 // Get the commit ID for the last known clean pass of the Backend tests 460 def cleanBackendsCommitProps = readProperties file: "${CLEAN_BACKEND_LOCATION}" 461 return cleanBackendsCommitProps['git-commit'] 462 } 463 464 // Returns the last run commit for the Backend, or null if the commit file does not exist yet. 465 // - fails the pipeline if any error other than 404 is returned by the OCI CLI 466 def getLastBackendRunCommit() { 467 lastBackendCommitCommandOutput = sh ( 468 label: "Get last clean backend commit ID", 469 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", 470 returnStdout: true 471 ).trim() 472 echo "command out: ${lastBackendCommitCommandOutput}" 473 if (lastBackendCommitCommandOutput.length() > 0) { 474 // We can get warning messages here as well even when the command succeeded, so be more precise on the checking 475 if (lastBackendCommitCommandOutput =~ /(.*)status(.*)\d{1,4}(.*)/) { 476 // If we think we had a status: NNN, we ignore 404 and fail for others 477 assert lastBackendCommitCommandOutput =~ /(.*)status(.*)404(.*)/ : "An unexpected error occurred getting last backend commit from ObjectStore: ${lastBackendCommitCommandOutput}" 478 } else { 479 // 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 480 sh """ 481 if [ ! -f ${LAST_BACKEND_LOCATION} ]; then 482 echo "An unexpected error occurred getting last backend run commit from ObjectStore: ${lastBackendCommitCommandOutput}" 483 exit 1 484 fi 485 """ 486 } 487 } 488 // Get the commit ID for the last known clean pass of the Backend tests 489 def lastBackendCommitProps = readProperties file: "${LAST_BACKEND_LOCATION}" 490 return lastBackendCommitProps['git-commit'] 491 } 492 493 // Checks all the conditions gating test execution and collates the result 494 def runTests() { 495 return params.FORCE || ( ! backendTestsUpToDate && ! backendTestsUpToDateFailed && ! params.DRY_RUN ) 496 } 497 498 def isAlertingEnabled() { 499 // this controls whether any alerting happens for these tests 500 if (NOTIFY_BACKEND_FAILURES.equals("true") && (env.BRANCH_NAME.equals("master") || env.BRANCH_NAME.startsWith("release-"))) { 501 echo "Alert notifications enabled for ${env.BRANCH_NAME}" 502 return true 503 } 504 return false 505 } 506 507 def isPagerDutyEnabled() { 508 // this additionally controls whether PD alerts are enabled (note that you must also enable alerting in general as well if you want these) 509 if (NOTIFY_PAGERDUTY_BACKEND_FAILURES.equals("true")) { 510 echo "Pager-Duty notifications enabled via global override setting" 511 return true 512 } 513 return false 514 } 515 516 // def getCronSchedule() { 517 // if (env.BRANCH_NAME.equals("master")) { 518 // return "@weekly" 519 // } else if (env.BRANCH_NAME.startsWith("release-")) { 520 // return "@weekly" 521 // } 522 // return "" 523 // } 524 525 // Called in Stage Clean workspace and checkout steps 526 def getCommitListFromGitLog(lastClean, currentStable) { 527 echo "Checking for change sets" 528 def commitList = sh(returnStdout: true, script: "git log ${lastClean}...${currentStable} --oneline | cut -d \" \" -f 1").trim().split('\n') 529 for (int i = 0; i < commitList.size(); i++) { 530 echo "Found commit id: ${commitList[i]}" 531 } 532 return commitList 533 } 534 535 def trimIfGithubNoreplyUser(userIn) { 536 if (userIn == null) { 537 echo "Not a github noreply user, not trimming: ${userIn}" 538 return userIn 539 } 540 if (userIn.matches(".*\\+.*@users.noreply.github.com.*")) { 541 def userOut = userIn.substring(userIn.indexOf("+") + 1, userIn.indexOf("@")) 542 return userOut; 543 } 544 if (userIn.matches(".*<.*@users.noreply.github.com.*")) { 545 def userOut = userIn.substring(userIn.indexOf("<") + 1, userIn.indexOf("@")) 546 return userOut; 547 } 548 if (userIn.matches(".*@users.noreply.github.com")) { 549 def userOut = userIn.substring(0, userIn.indexOf("@")) 550 return userOut; 551 } 552 echo "Not a github noreply user, not trimming: ${userIn}" 553 return userIn 554 } 555 556 def getSuspectList(commitList, userMappings) { 557 def retValue = "" 558 def suspectList = [] 559 if (commitList == null || commitList.size() == 0) { 560 echo "No commits to form suspect list" 561 } else { 562 for (int i = 0; i < commitList.size(); i++) { 563 def id = commitList[i] 564 try { 565 def gitAuthor = sh( 566 script: "git log --format='%ae' '$id^!'", 567 returnStdout: true 568 ).trim() 569 if (gitAuthor != null) { 570 def author = trimIfGithubNoreplyUser(gitAuthor) 571 echo "DEBUG: author: ${gitAuthor}, ${author}, id: ${id}" 572 if (userMappings.containsKey(author)) { 573 def slackUser = userMappings.get(author) 574 if (!suspectList.contains(slackUser)) { 575 echo "Added ${slackUser} as suspect" 576 retValue += " ${slackUser}" 577 suspectList.add(slackUser) 578 } 579 } else { 580 // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated 581 if (!suspectList.contains(author)) { 582 echo "Added ${author} as suspect" 583 retValue += " ${author}" 584 suspectList.add(author) 585 } 586 } 587 } else { 588 echo "No author returned from git" 589 } 590 } catch (Exception e) { 591 echo "INFO: Problem processing commit ${id}, skipping commit: " + e.toString() 592 } 593 } 594 } 595 def startedByUser = ""; 596 def causes = currentBuild.getBuildCauses() 597 echo "causes: " + causes.toString() 598 for (cause in causes) { 599 def causeString = cause.toString() 600 echo "current cause: " + causeString 601 def causeInfo = readJSON text: causeString 602 if (causeInfo.userId != null) { 603 startedByUser = causeInfo.userId 604 } 605 } 606 607 if (startedByUser.length() > 0) { 608 echo "Build was started by a user, adding them to the suspect notification list: ${startedByUser}" 609 def author = trimIfGithubNoreplyUser(startedByUser) 610 echo "DEBUG: author: ${startedByUser}, ${author}" 611 if (userMappings.containsKey(author)) { 612 def slackUser = userMappings.get(author) 613 if (!suspectList.contains(slackUser)) { 614 echo "Added ${slackUser} as suspect" 615 retValue += " ${slackUser}" 616 suspectList.add(slackUser) 617 } 618 } else { 619 // If we don't have a name mapping use the commit.author, at least we can easily tell if the mapping gets dated 620 if (!suspectList.contains(author)) { 621 echo "Added ${author} as suspect" 622 retValue += " ${author}" 623 suspectList.add(author) 624 } 625 } 626 } else { 627 echo "Build not started by a user, not adding to notification list" 628 } 629 echo "returning suspect list: ${retValue}" 630 return retValue 631 } 632 633 @NonCPS 634 List extractReleaseTags(final String fileContent) { 635 List releases = [] 636 fileContent.eachLine { tag -> 637 releases << tag 638 } 639 return releases 640 } 641 642 def getLatestReleaseVersion() { 643 final String releaseTags = readFile(file: "${workspace}/tags.txt") 644 list gitTags = extractReleaseTags(releaseTags) 645 echo "gitTags = ${gitTags}" 646 return gitTags.pop() 647 }