2020-06-18 22:46:29 +00:00
#!groovy
/**
2020-09-18 06:46:08 +00:00
* Jenkins pipeline to build Corda OS release branches and tags.
* PLEASE NOTE: we DO want to run a build for each commit!!!
2020-06-18 22:46:29 +00:00
*/
/**
* Sense environment
*/
2022-03-15 07:58:58 +00:00
boolean isReleaseBranch = (env.BRANCH_NAME =~ /^release\/os\/.*/)
2020-06-20 10:08:52 +00:00
boolean isReleaseTag = (env.TAG_NAME =~ /^release-.*(?<!_JDK11)$/)
2020-07-16 15:46:10 +00:00
boolean isInternalRelease = (env.TAG_NAME =~ /^internal-release-.*$/)
2020-09-18 06:46:08 +00:00
boolean isReleaseCandidate = (env.TAG_NAME =~ /^(release-.*(RC|HC).*(?<!_JDK11))$/)
2020-06-26 09:48:47 +00:00
/*
** calculate the stage for NexusIQ evaluation
** * build for snapshots
** * stage-release: for release candidates and for health checks
2020-09-18 06:46:08 +00:00
** * release: for GA release
2020-06-26 09:48:47 +00:00
*/
2020-08-06 19:20:19 +00:00
def nexusDefaultIqStage = "build"
2020-06-26 09:48:47 +00:00
if (isReleaseTag) {
switch (env.TAG_NAME) {
2020-08-06 19:20:19 +00:00
case ~/.*-RC\d+(-.*)?/: nexusDefaultIqStage = "stage-release"; break;
case ~/.*-HC\d+(-.*)?/: nexusDefaultIqStage = "stage-release"; break;
default: nexusDefaultIqStage = "release"
2020-06-26 09:48:47 +00:00
}
}
2020-06-18 22:46:29 +00:00
2020-08-06 19:20:19 +00:00
/**
* make sure calculated default value of NexusIQ stage is first in the list
* thus making it default for the `choice` parameter
*/
def nexusIqStageChoices = [nexusDefaultIqStage].plus(
[
'develop',
'build',
'stage-release',
'release',
'operate'
].minus([nexusDefaultIqStage]))
2022-03-15 07:58:58 +00:00
/**
* define an empty teamsWebHookURL and if it is a Release Branch
* then set it for the Corda 4 Jenkins Connector
*/
def teamsWebHookURL = ""
if (isReleaseBranch || isReleaseTag){
withCredentials([string(credentialsId: 'ms-teams-webhook', variable: 'webhook_url')]) {
teamsWebHookURL = "$webhook_url"
}
}
2020-09-18 06:46:08 +00:00
/**
* Common Gradle arguments for all Gradle executions
*/
String COMMON_GRADLE_PARAMS = [
'--no-daemon',
'--stacktrace',
'--info',
'-Pcompilation.warningsAsErrors=false',
'-Ptests.failFast=true',
].join(' ')
2019-10-08 14:33:24 +00:00
pipeline {
2020-09-18 06:46:08 +00:00
agent { label 'standard' }
/*
* List options in alphabetical order
*/
2019-11-09 08:57:19 +00:00
options {
2020-07-15 09:17:58 +00:00
buildDiscarder(logRotator(daysToKeepStr: '14', artifactDaysToKeepStr: '14'))
2020-09-18 06:46:08 +00:00
parallelsAlwaysFailFast()
timeout(time: 6, unit: 'HOURS')
timestamps()
2022-03-15 07:58:58 +00:00
office365ConnectorWebhooks([[
name : "Corda 4 Jenkins Connector",
notifyBackToNormal : true,
startNotification : false,
notifyFailure : true,
notifySuccess : true,
notifyNotBuilt : false,
notifyAborted : false,
notifyRepeatedFailure: true,
notifyUnstable : true,
url : "${teamsWebHookURL}"
]])
2019-11-09 08:57:19 +00:00
}
2019-10-08 14:33:24 +00:00
2020-08-06 19:20:19 +00:00
parameters {
choice choices: nexusIqStageChoices, description: 'NexusIQ stage for code evaluation', name: 'nexusIqStage'
2022-01-11 11:44:42 +00:00
booleanParam defaultValue: true, description: 'Run tests during this build?', name: 'DO_TEST'
2020-08-06 19:20:19 +00:00
}
2020-09-18 06:46:08 +00:00
/*
* List environment variables in alphabetical order
*/
2019-10-08 14:33:24 +00:00
environment {
2020-09-18 06:46:08 +00:00
ARTIFACTORY_BUILD_NAME = "Corda :: Publish :: Publish Release to Artifactory :: ${env.BRANCH_NAME}"
2019-11-04 13:05:13 +00:00
ARTIFACTORY_CREDENTIALS = credentials('artifactory-credentials')
2020-07-17 08:39:45 +00:00
CORDA_ARTIFACTORY_PASSWORD = "${env.ARTIFACTORY_CREDENTIALS_PSW}"
2020-09-18 06:46:08 +00:00
CORDA_ARTIFACTORY_USERNAME = "${env.ARTIFACTORY_CREDENTIALS_USR}"
DOCKER_URL = "https://index.docker.io/v1/"
2019-10-08 14:33:24 +00:00
}
stages {
2020-09-18 06:46:08 +00:00
stage('Compile') {
steps {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
'clean',
'jar'
].join(' ')
2020-07-30 10:27:45 +00:00
}
2020-09-18 06:46:08 +00:00
}
stage('Stash') {
2022-01-11 11:44:42 +00:00
when {
expression { params.DO_TEST }
}
2020-09-18 06:46:08 +00:00
steps {
stash name: 'compiled', useDefaultExcludes: false
}
}
stage('Sonatype Check') {
2020-06-26 09:48:47 +00:00
steps {
script {
sh "./gradlew --no-daemon properties | grep -E '^(version|group):' >version-properties"
2020-07-22 11:41:22 +00:00
/* every build related to Corda X.Y (GA, RC, HC, patch or snapshot) uses the same NexusIQ application */
2020-11-16 07:18:22 +00:00
def version = sh (returnStdout: true, script: "grep ^version: version-properties | sed -e 's/^version: \\([0-9]\\+\\(\\.[0-9]\\+\\)\\+\\).*\$/\\1/'").trim()
2020-06-26 09:48:47 +00:00
def groupId = sh (returnStdout: true, script: "grep ^group: version-properties | sed -e 's/^group: //'").trim()
def artifactId = 'corda'
2020-08-06 19:17:15 +00:00
nexusAppId = "${groupId}-${artifactId}-${version}"
2020-06-26 09:48:47 +00:00
}
nexusPolicyEvaluation (
failBuildOnNetworkError: false,
2020-07-22 11:41:22 +00:00
iqApplication: selectedApplication(nexusAppId), // application *has* to exist before a build starts!
2020-06-26 09:48:47 +00:00
iqScanPatterns: [[scanPattern: 'node/capsule/build/libs/corda*.jar']],
2020-08-06 19:20:19 +00:00
iqStage: params.nexusIqStage
2020-06-26 09:48:47 +00:00
)
}
}
2020-09-18 06:46:08 +00:00
stage('All Tests') {
2022-01-11 11:44:42 +00:00
when {
expression { params.DO_TEST }
beforeAgent true
}
2020-03-03 11:16:38 +00:00
parallel {
2020-09-18 06:46:08 +00:00
stage('Another agent') {
agent {
label 'standard'
2020-08-21 10:18:54 +00:00
}
2020-09-18 06:46:08 +00:00
options {
skipDefaultCheckout true
2020-08-21 10:18:54 +00:00
}
2020-09-18 06:46:08 +00:00
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(' ')
}
}
2020-03-03 11:16:38 +00:00
}
}
2020-09-18 06:46:08 +00:00
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(' ')
}
}
2020-03-03 11:16:38 +00:00
}
}
2019-10-08 14:33:24 +00:00
}
}
2020-06-20 10:08:52 +00:00
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',
2020-07-15 20:33:49 +00:00
repo: 'corda-releases'
2020-06-20 10:08:52 +00:00
)
rtGradleRun(
usesPlugin: true,
useWrapper: true,
switches: '-s --info',
tasks: 'artifactoryPublish',
deployerId: 'deployer',
buildName: env.ARTIFACTORY_BUILD_NAME
)
rtPublishBuildInfo(
serverId: 'R3-Artifactory',
buildName: env.ARTIFACTORY_BUILD_NAME
)
}
}
2020-06-30 19:12:27 +00:00
2020-12-02 16:26:15 +00:00
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,
2020-12-03 21:42:57 +00:00
'-Pdocker.image.repository=entdocker.software.r3.com/corda',
2020-12-02 16:26:15 +00:00
'docker:pushDockerImage',
'--image OFFICIAL',
'--registry-url=entdocker.software.r3.com'
].join(' ')
}
}
}
2020-06-30 19:12:27 +00:00
stage('Publish Release to Docker Hub') {
when {
2020-12-02 16:26:15 +00:00
expression { isReleaseTag && !isInternalRelease && !isReleaseCandidate}
2020-06-30 19:12:27 +00:00
}
steps {
withCredentials([
usernamePassword(credentialsId: 'corda-publisher-docker-hub-credentials',
usernameVariable: 'DOCKER_USERNAME',
2020-09-18 06:46:08 +00:00
passwordVariable: 'DOCKER_PASSWORD')
]) {
sh script: [
'./gradlew',
COMMON_GRADLE_PARAMS,
2020-11-24 17:36:53 +00:00
'docker:pushDockerImage',
'-Pdocker.image.repository=corda/corda',
2020-11-30 22:30:29 +00:00
'--image OFFICIAL'
2020-09-18 06:46:08 +00:00
].join(' ')
2020-06-30 19:12:27 +00:00
}
}
}
2019-10-08 14:33:24 +00:00
}
post {
always {
2020-01-16 11:55:01 +00:00
script {
try {
2022-01-11 11:44:42 +00:00
if (params.DO_TEST) {
unstash 'allure-input'
allure includeProperties: false,
jdk: '',
results: [[path: '**/allure-input']]
}
2020-01-16 11:55:01 +00:00
} catch (err) {
echo("Allure report generation failed: $err")
if (currentBuild.resultIsBetterOrEqualTo('SUCCESS')) {
currentBuild.result = 'UNSTABLE'
}
}
}
2020-06-18 22:46:29 +00:00
2020-01-14 11:52:05 +00:00
script
{
2020-06-20 10:08:52 +00:00
if (!isReleaseTag) {
2020-06-18 22:46:29 +00:00
// 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(
2020-09-18 06:46:08 +00:00
currentBuild.previousBuild?.timeInMillis ?: 0).clearTime()
2020-06-18 22:46:29 +00:00
def currentBuildDate = new Date(
currentBuild.timeInMillis).clearTime()
2020-01-14 11:52:05 +00:00
2020-06-18 22:46:29 +00:00
if (prevBuildDate != currentBuildDate) {
def statusSymbol = '\u2753'
switch(currentBuild.result) {
case 'SUCCESS':
statusSymbol = '\u2705'
break;
case 'UNSTABLE':
2020-09-18 06:46:08 +00:00
statusSymbol = '\u26A0'
break;
2020-06-18 22:46:29 +00:00
case 'FAILURE':
statusSymbol = '\u274c'
break;
default:
break;
}
2020-01-14 11:52:05 +00:00
2020-06-18 22:46:29 +00:00
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')
}
2019-12-20 14:44:45 +00:00
}
}
2019-10-08 14:33:24 +00:00
}
cleanup {
deleteDir() /* clean up our workspace */
}
}
2020-06-05 06:56:37 +00:00
}