diff --git a/.ci/Jenkinsfile b/.ci/Jenkinsfile index a04090eae..3c290dd30 100644 --- a/.ci/Jenkinsfile +++ b/.ci/Jenkinsfile @@ -3,12 +3,12 @@ @Library('apm@current') _ pipeline { - agent { label 'linux && immutable' } + agent { label 'linux && docker && ubuntu-18.04 && immutable' } environment { REPO = 'apm-agent-php' BASE_DIR = "src/go.elastic.co/apm/${env.REPO}" - NOTIFY_TO = credentials('notify-to') - JOB_GCS_BUCKET = credentials('gcs-bucket') + SLACK_CHANNEL = '#apm-agent-php' + NOTIFY_TO = 'build-apm+apm-agent-php@elastic.co' ONLY_DOCS = "false" } options { @@ -51,7 +51,7 @@ pipeline { } failFast false matrix { - agent { label 'linux && immutable' } + agent { label 'linux && docker && ubuntu-18.04 && immutable' } options { skipDefaultCheckout() } axes { axis { @@ -140,7 +140,8 @@ pipeline { unstash 'generate-for-package-7.4-Dockerfile.alpine' sh script: "make -C packaging package", label: 'package' sh script: "make -C packaging info", label: 'package info' - stash includes: 'build/packages/*', name: 'package' + // checksum files are regenerated by the signing component in the internal-ci instance. + stash(includes: 'build/packages/*', name: 'package', excludes: 'build/packages/**/*.sha512') } } } @@ -159,7 +160,7 @@ pipeline { } failFast false matrix { - agent { label 'linux && immutable' } + agent { label 'linux && docker && ubuntu-18.04 && immutable' } options { skipDefaultCheckout() } axes { axis { @@ -197,7 +198,7 @@ pipeline { } matrix { // TODO: This should be uncommented out when the implementation is in place - // agent { label 'linux && immutable' } + // agent { label 'linux && docker && ubuntu-18.04 && immutable' } options { skipDefaultCheckout() } axes { axis { @@ -245,6 +246,7 @@ pipeline { } } } + // This meta-stage happens in the internal-ci instance to be able to sign the artifacts correctly. stage('Release') { options { skipDefaultCheckout() @@ -252,20 +254,56 @@ pipeline { } when { beforeAgent true - tag pattern: 'v\\d+.*', comparator: 'REGEXP' + allOf { + tag pattern: 'v\\d+.*', comparator: 'REGEXP' + expression { isInternalCI() } + } + } + agent { label 'linux && docker && ubuntu-18.04 && immutable' } + environment { + BUCKET_NAME = 'internal-ci-artifacts' + BUCKET_SUBFOLDER = "${env.REPO}/${env.TAG_NAME}" + BUCKET_PATH = "gs://${env.BUCKET_NAME}/${env.BUCKET_SUBFOLDER}" + BUCKET_CREDENTIALS = 'internal-ci-gcs-plugin' + SIGNED_ARTIFACTS = 'signed-artifacts' + BUCKET_SUBFOLDER_SIGNED_ARTIFACTS = "${env.BUCKET_SUBFOLDER}/${env.SIGNED_ARTIFACTS}" + BUCKET_SIGNED_ARTIFACTS_PATH = "gs://${env.BUCKET_NAME}/${env.BUCKET_SUBFOLDER_SIGNED_ARTIFACTS}" } stages { stage('Notify') { options { skipDefaultCheckout() } steps { - emailext subject: "[${env.REPO}] Release ready to be pushed", to: "${NOTIFY_TO}", - body: "Please go to ${env.BUILD_URL}input to approve or reject within 12 hours.\n Changes: ${env.TAG_NAME}" - script { - def should_continue = input(message: "You are about to release version ${env.TAG_NAME}", - parameters: [ [$class: 'ChoiceParameterDefinition', - name: 'Do you wish to release it?', - choices: ['Yes', 'No']] ]) - env.RELEASE = should_continue.equals('Yes') + notifyStatus(slackStatus: 'warning', subject: "[${env.REPO}] Release ready to be pushed", + body: "Please (<${env.BUILD_URL}input|approve>) it or reject within 12 hours.\n Changes: ${env.TAG_NAME}") + setEnvVar('RELEASE', askAndWait("You are about to release version ${env.TAG_NAME}. Do you wish to release it?")) + } + } + stage('Signing CI') { + when { + beforeAgent true + expression { return env.RELEASE == 'true' } + } + options { skipDefaultCheckout() } + steps { + deleteDir() + unstash 'source' + dir("${BASE_DIR}") { + unstash 'package' + googleStorageUpload(bucket: env.BUCKET_PATH, + credentialsId: env.BUCKET_CREDENTIALS, + pathPrefix: 'build/packages/', + pattern: 'build/packages/**/*', + sharedPublicly: false, + showInline: true) + build(wait: true, propagate: true, job: 'elastic+unified-release+master+sign-artifacts-with-gpg', parameters: [string(name: 'gcs_input_path', value: "${env.BUCKET_PATH}")]) + dir("${SIGNED_ARTIFACTS}") { + googleStorageDownload(bucketUri: "${env.BUCKET_SIGNED_ARTIFACTS_PATH}/*", + credentialsId: env.BUCKET_CREDENTIALS, + localDirectory: 'build/packages/', + pathPrefix: "${env.BUCKET_SUBFOLDER_SIGNED_ARTIFACTS}") + stash allowEmpty: false, name: env.SIGNED_ARTIFACTS, useDefaultExcludes: false + } + archiveArtifacts(allowEmptyArchive: true, artifacts: "${SIGNED_ARTIFACTS}/**/*") } } } @@ -279,7 +317,7 @@ pipeline { deleteDir() unstash 'source' dir("${BASE_DIR}") { - unstash 'package' + unstash "${env.SIGNED_ARTIFACTS}" withCredentials([string(credentialsId: '2a9602aa-ab9f-4e52-baf3-b71ca88469c7', variable: 'GITHUB_TOKEN')]) { sh script: 'make -f .ci/Makefile release', label: 'release' } @@ -287,7 +325,7 @@ pipeline { } post { success { - emailext subject: "[${env.REPO}] Release published", to: "${env.NOTIFY_TO}", body: "Great news, the release has been done successfully." + notifyStatus(slackStatus: 'good', subject: "[${env.REPO}] Release *${env.TAG_NAME}* published", body: "(<${env.RUN_DISPLAY_URL}|Open>)") } always { script { @@ -297,11 +335,38 @@ pipeline { } } } + post { + failure { + notifyStatus(slackStatus: 'danger', subject: "[${env.REPO}] Release *${env.TAG_NAME}* failed", body: "(<${env.RUN_DISPLAY_URL}|Open>)") + } + } } } post { cleanup { - notifyBuildResult() + // Reporting disables in the `internal-ci` since credentials are not in place + // OTOH it avoids duplicated notifications + whenFalse(isInternalCI()){ + notifyBuildResult() + } } } } + +// TODO: create an input step to avoid this try/catch and return true/false +def askAndWait(message) { + try { + input(message: message, ok: 'Yes') + return true + } catch(err) { + return false + } +} + +def notifyStatus(def args = [:]) { + slackSend(channel: env.SLACK_CHANNEL, color: args.slackStatus, message: "${args.subject}. ${args.body}", + tokenCredentialId: 'jenkins-slack-integration-token') + // transform slack URL format '()' to 'URL'. + def bodyEmail = args.body.replaceAll('\\(<', '').replaceAll('\\|.*>\\)', '') + emailext(subject: args.subject, to: "${env.NOTIFY_TO}", body: bodyEmail) +}