1#!/usr/bin/env groovy
2
3@Library('apm@current') _
4
5pipeline {
6  agent { label 'linux && immutable' }
7  environment {
8    REPO = 'apm-agent-go'
9    BASE_DIR = "src/go.elastic.co/apm"
10    NOTIFY_TO = credentials('notify-to')
11    JOB_GCS_BUCKET = credentials('gcs-bucket')
12    CODECOV_SECRET = 'secret/apm-team/ci/apm-agent-go-codecov'
13    GO111MODULE = 'on'
14    GOPATH = "${env.WORKSPACE}"
15    GOPROXY = 'https://proxy.golang.org'
16    HOME = "${env.WORKSPACE}"
17    GITHUB_CHECK_ITS_NAME = 'Integration Tests'
18    ITS_PIPELINE = 'apm-integration-tests-selector-mbp/master'
19    OPBEANS_REPO = 'opbeans-go'
20    SLACK_CHANNEL = '#apm-agent-go'
21  }
22  options {
23    timeout(time: 1, unit: 'HOURS')
24    buildDiscarder(logRotator(numToKeepStr: '20', artifactNumToKeepStr: '20', daysToKeepStr: '30'))
25    timestamps()
26    ansiColor('xterm')
27    disableResume()
28    durabilityHint('PERFORMANCE_OPTIMIZED')
29    rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true])
30    quietPeriod(10)
31  }
32  triggers {
33    issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?(?:benchmark\\W+)?tests(?:\\W+please)?.*')
34  }
35  parameters {
36    string(name: 'GO_VERSION', defaultValue: "1.15.10", description: "Go version to use.")
37    booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.')
38    booleanParam(name: 'test_ci', defaultValue: true, description: 'Enable test')
39    booleanParam(name: 'docker_test_ci', defaultValue: true, description: 'Enable run docker tests')
40    booleanParam(name: 'bench_ci', defaultValue: true, description: 'Enable benchmarks')
41  }
42  stages {
43    stage('Initializing'){
44      options { skipDefaultCheckout() }
45      environment {
46        GO_VERSION = "${params.GO_VERSION}"
47        PATH = "${env.PATH}:${env.WORKSPACE}/bin"
48      }
49      stages {
50        /**
51         Checkout the code and stash it, to use it on other stages.
52        */
53        stage('Checkout') {
54          options { skipDefaultCheckout() }
55          steps {
56            pipelineManager([ cancelPreviousRunningBuilds: [ when: 'PR' ] ])
57            deleteDir()
58            gitCheckout(basedir: "${BASE_DIR}", githubNotifyFirstTimeContributor: true, reference: '/var/lib/jenkins/.git-references/apm-agent-go.git')
59            stash allowEmpty: true, name: 'source', useDefaultExcludes: false
60            script {
61              dir("${BASE_DIR}"){
62                // Skip all the stages except docs for PR's with asciidoc and md changes only
63                env.ONLY_DOCS = isGitRegionMatch(patterns: [ '.*\\.(asciidoc|md)' ], shouldMatchAll: true)
64              }
65            }
66          }
67        }
68        /**
69        Execute unit tests.
70        */
71        stage('Tests') {
72          options { skipDefaultCheckout() }
73          when {
74            beforeAgent true
75            allOf {
76              expression { return env.ONLY_DOCS == "false" }
77              expression { return params.test_ci }
78            }
79          }
80          steps {
81            withGithubNotify(context: 'Tests', tab: 'tests') {
82              deleteDir()
83              unstash 'source'
84              dir("${BASE_DIR}"){
85                script {
86                  def go = readYaml(file: '.jenkins.yml')
87                  def parallelTasks = [:]
88                  go['GO_VERSION'].each{ version ->
89                    parallelTasks["Go-${version}"] = generateStep(version)
90                  }
91                  // For the cutting edge
92                  def edge = readYaml(file: '.jenkins-edge.yml')
93                  edge['GO_VERSION'].each{ version ->
94                    parallelTasks["Go-${version}"] = generateStepAndCatchError(version)
95                  }
96                  parallel(parallelTasks)
97                }
98              }
99            }
100          }
101        }
102        stage('Coverage') {
103          options { skipDefaultCheckout() }
104          when {
105            beforeAgent true
106            allOf {
107              expression { return env.ONLY_DOCS == "false" }
108              expression { return params.docker_test_ci }
109            }
110          }
111          steps {
112            withGithubNotify(context: 'Coverage') {
113              deleteDir()
114              unstash 'source'
115              dir("${BASE_DIR}"){
116                sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies'
117                sh script: './scripts/jenkins/docker-test.sh', label: 'Docker tests'
118              }
119            }
120          }
121          post {
122            always {
123              coverageReport("${BASE_DIR}/build/coverage")
124              codecov(repo: env.REPO, basedir: "${BASE_DIR}",
125                flags: "-f build/coverage/coverage.cov -X search",
126                secret: "${CODECOV_SECRET}")
127              junit(allowEmptyResults: true,
128                keepLongStdio: true,
129                testResults: "${BASE_DIR}/build/junit-*.xml")
130            }
131          }
132        }
133        stage('Benchmark') {
134          agent { label 'linux && immutable' }
135          options { skipDefaultCheckout() }
136          when {
137            beforeAgent true
138            allOf {
139              anyOf {
140                branch 'master'
141                tag pattern: 'v\\d+\\.\\d+\\.\\d+.*', comparator: 'REGEXP'
142                expression { return params.Run_As_Master_Branch }
143                expression { return env.GITHUB_COMMENT?.contains('benchmark tests') }
144              }
145              expression { return params.bench_ci }
146            }
147          }
148          steps {
149            withGithubNotify(context: 'Benchmark', tab: 'tests') {
150              deleteDir()
151              unstash 'source'
152              dir("${BASE_DIR}"){
153                sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies'
154                sh script: './scripts/jenkins/bench.sh', label: 'Benchmarking'
155                sendBenchmarks(file: 'build/bench.out', index: 'benchmark-go')
156              }
157            }
158          }
159        }
160      }
161    }
162    stage('More OS') {
163      when {
164        beforeAgent true
165        expression { return env.ONLY_DOCS == "false" }
166      }
167      parallel {
168        stage('Windows') {
169          agent { label 'windows-2019-immutable' }
170          options { skipDefaultCheckout() }
171          environment {
172            GO_VERSION = "${params.GO_VERSION}"
173          }
174          steps {
175            withGithubNotify(context: 'Build-Test - Windows') {
176              cleanDir("${WORKSPACE}/${BASE_DIR}")
177              unstash 'source'
178              withGoEnv(version: "${env.GO_VERSION}"){
179                dir("${BASE_DIR}"){
180                  bat script: 'scripts/jenkins/windows/build-test.bat', label: 'Build and test'
181                }
182              }
183            }
184          }
185          post {
186            always {
187              junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/build/junit-*.xml")
188            }
189          }
190        }
191        stage('OSX') {
192          agent { label 'macosx && x86_64' }
193          options { skipDefaultCheckout() }
194          environment {
195            GO_VERSION = "${params.GO_VERSION}"
196            PATH = "${env.PATH}:${env.WORKSPACE}/bin"
197          }
198          steps {
199            withGithubNotify(context: 'Build-Test - OSX') {
200              retry(3) {
201                deleteDir()
202                unstash 'source'
203                dir("${BASE_DIR}"){
204                  sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies'
205                }
206              }
207              retry(3) {
208                dir("${BASE_DIR}"){
209                  sh script: './scripts/jenkins/build.sh', label: 'Build'
210                }
211              }
212              dir("${BASE_DIR}"){
213                sh script: './scripts/jenkins/test.sh', label: 'Test'
214              }
215            }
216          }
217          post {
218            always {
219              junit(allowEmptyResults: true, keepLongStdio: true, testResults: "${BASE_DIR}/build/junit-*.xml")
220              deleteDir()
221            }
222          }
223        }
224      }
225    }
226    stage('Integration Tests') {
227      agent none
228      when {
229        beforeAgent true
230        allOf {
231          expression { return env.ONLY_DOCS == "false" }
232          anyOf {
233            changeRequest()
234            expression { return !params.Run_As_Master_Branch }
235          }
236        }
237      }
238      steps {
239        build(job: env.ITS_PIPELINE, propagate: false, wait: false,
240              parameters: [string(name: 'INTEGRATION_TEST', value: 'Go'),
241                           string(name: 'BUILD_OPTS', value: "--go-agent-version ${env.GIT_BASE_COMMIT} --opbeans-go-agent-branch ${env.GIT_BASE_COMMIT}"),
242                           string(name: 'GITHUB_CHECK_NAME', value: env.GITHUB_CHECK_ITS_NAME),
243                           string(name: 'GITHUB_CHECK_REPO', value: env.REPO),
244                           string(name: 'GITHUB_CHECK_SHA1', value: env.GIT_BASE_COMMIT)])
245        githubNotify(context: "${env.GITHUB_CHECK_ITS_NAME}", description: "${env.GITHUB_CHECK_ITS_NAME} ...", status: 'PENDING', targetUrl: "${env.JENKINS_URL}search/?q=${env.ITS_PIPELINE.replaceAll('/','+')}")
246      }
247    }
248    stage('Release') {
249      options { skipDefaultCheckout() }
250      when {
251        beforeAgent true
252        tag pattern: 'v\\d+\\.\\d+\\.\\d+', comparator: 'REGEXP'
253      }
254      stages {
255        stage('Opbeans') {
256          environment {
257            REPO_NAME = "${OPBEANS_REPO}"
258            GO_VERSION = "${params.GO_VERSION}"
259          }
260          steps {
261            deleteDir()
262            dir("${OPBEANS_REPO}"){
263              git credentialsId: 'f6c7695a-671e-4f4f-a331-acdce44ff9ba',
264                  url: "git@github.com:elastic/${OPBEANS_REPO}.git"
265              sh script: ".ci/bump-version.sh ${env.BRANCH_NAME}", label: 'Bump version'
266              // The opbeans-go pipeline will trigger a release for the master branch
267              gitPush()
268              // The opbeans-go pipeline will trigger a release for the release tag
269              gitCreateTag(tag: "${env.BRANCH_NAME}")
270            }
271          }
272        }
273        stage('Notify') {
274          steps {
275            notifyStatus(slackStatus: 'good', subject: "[${env.REPO}] Release *${env.BRANCH_NAME}* published", body: "Great news, the release has finished successfully. (<${env.RUN_DISPLAY_URL}|Open>).")
276          }
277        }
278      }
279    }
280  }
281  post {
282    cleanup {
283      notifyBuildResult()
284    }
285  }
286}
287
288def generateStep(version){
289  return {
290    node('linux && immutable'){
291      try {
292        echo "${version}"
293        withEnv(["GO_VERSION=${version}"]) {
294          // Another retry in case there are any environmental issues
295          // See https://issuetracker.google.com/issues/146072599 for more context
296          retry(3) {
297            deleteDir()
298            unstash 'source'
299            dir("${BASE_DIR}"){
300              sh script: './scripts/jenkins/before_install.sh', label: 'Install dependencies'
301            }
302          }
303          retry(3) {
304            dir("${BASE_DIR}"){
305              sh script: './scripts/jenkins/build.sh', label: 'Build'
306            }
307          }
308          dir("${BASE_DIR}"){
309            sh script: './scripts/jenkins/test.sh', label: 'Test'
310          }
311        }
312      } catch(e){
313        error(e.toString())
314      } finally {
315        junit(allowEmptyResults: true,
316          keepLongStdio: true,
317          testResults: "${BASE_DIR}/build/junit-*.xml")
318      }
319    }
320  }
321}
322
323def generateStepAndCatchError(version){
324  return {
325    catchError(buildResult: 'SUCCESS', message: 'Cutting Edge Tests', stageResult: 'UNSTABLE') {
326      generateStep(version)
327    }
328  }
329}
330
331def cleanDir(path){
332  powershell label: "Clean ${path}", script: "Remove-Item -Recurse -Force ${path}"
333}
334
335def notifyStatus(def args = [:]) {
336  releaseNotification(slackChannel: "${env.SLACK_CHANNEL}",
337                      slackColor: args.slackStatus,
338                      slackCredentialsId: 'jenkins-slack-integration-token',
339                      to: "${env.NOTIFY_TO}",
340                      subject: args.subject,
341                      body: args.body)
342}
343