#!groovy /** * Jenkins pipeline to build Corda OS release branches and tags. * PLEASE NOTE: we DO want to run a build for each commit!!! */ /** * Sense environment */ boolean isReleaseBranch = (env.BRANCH_NAME =~ /^release\/os\/.*/) boolean isReleaseTag = (env.TAG_NAME =~ /^release-.*(?version-properties" /* every build related to Corda X.Y (GA, RC, HC, patch or snapshot) uses the same NexusIQ application */ def version = sh (returnStdout: true, script: "grep ^version: version-properties | sed -e 's/^version: \\([0-9]\\+\\(\\.[0-9]\\+\\)\\+\\).*\$/\\1/'").trim() def groupId = sh (returnStdout: true, script: "grep ^group: version-properties | sed -e 's/^group: //'").trim() def artifactId = 'corda' nexusAppId = "${groupId}-${artifactId}-${version}" } nexusPolicyEvaluation ( failBuildOnNetworkError: false, iqApplication: selectedApplication(nexusAppId), // application *has* to exist before a build starts! iqScanPatterns: [[scanPattern: 'node/capsule/build/libs/corda*.jar']], iqStage: params.nexusIqStage ) } } stage('All Tests') { when { expression { params.DO_TEST } beforeAgent true } parallel { stage('Another agent') { agent { label 'standard' } options { skipDefaultCheckout true } post { always { archiveArtifacts artifacts: '**/*.log', fingerprint: false junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true /* * Copy all JUnit results files into a single top level directory. * This is necessary to stop the allure plugin from hitting out * of memory errors due to being passed many directories with * long paths. * * File names are pre-pended with a prefix when * copied to avoid collisions between files where the same test * classes have run on multiple agents. */ fileOperations([fileCopyOperation( includes: '**/build/test-results/**/*.xml', targetLocation: 'allure-input', flattenFiles: true, renameFiles: true, sourceCaptureExpression: '.*/([^/]+)$', targetNameExpression: 'other-agent-$1')]) stash name: 'allure-input', includes: 'allure-input/**', useDefaultExcludes: false } cleanup { deleteDir() /* clean up our workspace */ } } stages { stage('Unstash') { steps { unstash 'compiled' } } stage('Recompile') { steps { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'jar' ].join(' ') } } stage('Unit Test') { steps { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'test' ].join(' ') } } stage('Smoke Test') { steps { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'smokeTest' ].join(' ') } } stage('Slow Integration Test') { steps { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'slowIntegrationTest' ].join(' ') } } } } stage('Same agent') { post { always { archiveArtifacts artifacts: '**/*.log', fingerprint: false junit testResults: '**/build/test-results/**/*.xml', keepLongStdio: true /* * Copy all JUnit results files into a single top level directory. * This is necessary to stop the allure plugin from hitting out * of memory errors due to being passed many directories with * long paths. * * File names are pre-pended with a prefix when * copied to avoid collisions between files where the same test * classes have run on multiple agents. */ fileOperations([fileCopyOperation( includes: '**/build/test-results/**/*.xml', targetLocation: 'allure-input', flattenFiles: true, renameFiles: true, sourceCaptureExpression: '.*/([^/]+)$', targetNameExpression: 'same-agent-$1')]) } } stages { stage('Integration Test') { steps { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'integrationTest' ].join(' ') } } stage('Deploy Node') { steps { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'deployNode' ].join(' ') } } } } } } stage('Publish to Artifactory') { when { expression { isReleaseTag } } steps { rtServer( id: 'R3-Artifactory', url: 'https://software.r3.com/artifactory', credentialsId: 'artifactory-credentials' ) rtGradleDeployer( id: 'deployer', serverId: 'R3-Artifactory', repo: 'corda-releases' ) rtGradleRun( usesPlugin: true, useWrapper: true, switches: '-s --info -DpublishApiDocs', tasks: 'artifactoryPublish', deployerId: 'deployer', buildName: env.ARTIFACTORY_BUILD_NAME ) rtPublishBuildInfo( serverId: 'R3-Artifactory', buildName: env.ARTIFACTORY_BUILD_NAME ) } } stage('Publish Release Candidate to Internal Repository') { when { expression { isReleaseCandidate } } steps { withCredentials([ usernamePassword(credentialsId: 'docker-image-pusher-os', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD') ]) { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, '-Pdocker.image.repository=entdocker.software.r3.com/corda', 'docker:pushDockerImage', '--image OFFICIAL', '--registry-url=entdocker.software.r3.com' ].join(' ') } } } stage('Publish Release to Docker Hub') { when { expression { isReleaseTag && !isInternalRelease && !isReleaseCandidate} } steps { withCredentials([ usernamePassword(credentialsId: 'corda-publisher-docker-hub-credentials', usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD') ]) { sh script: [ './gradlew', COMMON_GRADLE_PARAMS, 'docker:pushDockerImage', '-Pdocker.image.repository=corda/community', '--image OFFICIAL' ].join(' ') } } } } post { always { script { try { if (params.DO_TEST) { unstash 'allure-input' allure includeProperties: false, jdk: '', results: [[path: '**/allure-input']] } } catch (err) { echo("Allure report generation failed: $err") if (currentBuild.resultIsBetterOrEqualTo('SUCCESS')) { currentBuild.result = 'UNSTABLE' } } } script { if (!isReleaseTag) { // We want to send a summary email, but want to limit to once per day. // Comparing the dates of the previous and current builds achieves this, // i.e. we will only send an email for the first build on a given day. def prevBuildDate = new Date( currentBuild.previousBuild?.timeInMillis ?: 0).clearTime() def currentBuildDate = new Date( currentBuild.timeInMillis).clearTime() if (prevBuildDate != currentBuildDate) { def statusSymbol = '\u2753' switch(currentBuild.result) { case 'SUCCESS': statusSymbol = '\u2705' break; case 'UNSTABLE': statusSymbol = '\u26A0' break; case 'FAILURE': statusSymbol = '\u274c' break; default: break; } echo('First build for this date, sending summary email') emailext to: '$DEFAULT_RECIPIENTS', subject: "$statusSymbol" + '$BRANCH_NAME regression tests - $BUILD_STATUS', mimeType: 'text/html', body: '${SCRIPT, template="groovy-html.template"}' } else { echo('Already sent summary email today, suppressing') } } } } failure { script { def statusSymbol = '\u274c' if (isReleaseTag || isReleaseBranch || isReleaseCandidate) { emailext subject: "$statusSymbol " + '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', body: '${SCRIPT, template="groovy-html.template"}', recipientProviders: [ [$class: 'CulpritsRecipientProvider'], [$class: 'RequesterRecipientProvider'] ], mimeType: 'text/html', replyTo: '$DEFAULT_REPLYTO', to: "adel.el-beik@r3.com" } } } cleanup { deleteDir() /* clean up our workspace */ } } }