2 Copyright (c) 2022 Nordix Foundation
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
8 http://www.apache.org/licenses/LICENSE-2.0
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
17 import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
22 base_image = 'base-image'
23 in_progress = 'In Progress.'
24 completed = 'Completed.'
27 exception_message_exec = 'failed to execute the following command: '
28 exception_message_code_generation = 'Generated code verification failed'
30 node('nordix-nsm-build-ubuntu1804') {
31 build_number = env.BUILD_NUMBER
32 workspace = env.WORKSPACE
33 ws("${workspace}/${build_number}") {
34 def image_names = params.IMAGE_NAMES.split(' ')
35 def version = params.IMAGE_VERSION
36 def e2e_enabled = params.E2E_ENABLED
37 def git_project = params.GIT_PROJECT
38 def current_branch = params.CURRENT_BRANCH
39 def default_branch = params.DEFAULT_BRANCH
40 def build_steps = params.BUILD_STEPS
41 def image_registry = params.IMAGE_REGISTRY
42 def local_version = "${env.JOB_NAME}-${build_number}"
45 stage('Clone/Checkout') {
46 git branch: default_branch, url: git_project
49 branches: [[name: current_branch]],
52 refspec: '+refs/pull/*/head:refs/remotes/origin/pr/*',
61 stage('Docker login') {
62 withCredentials([usernamePassword(credentialsId: 'nordix-cicd-harbor-credentials', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
64 echo $HARBOR_PASSWORD | docker login --username $HARBOR_USERNAME --password-stdin $IMAGE_REGISTRY
69 BaseImage(version, build_steps, image_registry, local_version).call()
72 Images(image_names, version, build_steps, image_registry, local_version).call()
75 E2e(e2e_enabled).call()
84 // Static analysis: Runs the GeneratedCode function and then UnitTests and Linter in parallel
87 GeneratedCode().call() // cannot generate code and run the linter and tests at the same time
91 stages.put('Unit Tests', UnitTests())
92 stages.put('Linter', Linter())
93 // stages.put('Generated code verification', GeneratedCode())
98 // Runs the unit tests and set the github commit status
101 def context = 'Unit Tests'
102 stage('Unit Tests') {
103 def command = 'make test'
105 SetBuildStatus(in_progress, context, pending)
106 ExecSh(command).call()
107 SetBuildStatus(completed, context, success)
108 } catch (Exception e) {
109 SetBuildStatus(failed, context, failure)
110 Error(exception_message_exec + command).call()
116 // Runs the linter and set the github commit status
119 def context = 'Linter'
121 def command = 'make lint'
123 SetBuildStatus(in_progress, context, pending)
124 ExecSh(command).call()
125 SetBuildStatus(completed, context, success)
126 } catch (Exception e) {
127 SetBuildStatus(failed, context, failure)
128 Error(exception_message_exec + command).call()
134 // Check if code has been generated correctly and set the github commit status:
135 // go.mod: runs "go mod tidy"
136 // go generate ./...: Code should be generated using "make genrate" command
137 // proto: skipped due to version of protoc
138 // If files are generated correctly then GetModifiedFiles function should return an empty string
139 def GeneratedCode() {
141 def context = 'Generated code verification'
142 SetBuildStatus(in_progress, context, pending)
143 stage('go mod tidy') {
144 def command = 'go mod tidy'
146 ExecSh(command).call()
147 if (GetModifiedFiles() != '') {
148 throw new Exception(exception_message_code_generation)
150 } catch (Exception e) {
151 SetBuildStatus(failed, context, failure)
152 Error(exception_message_exec + command).call()
155 stage('go generate ./...') {
156 def command = 'make generate'
158 ExecSh(command).call()
159 if (GetModifiedFiles() != '') {
160 throw new Exception(exception_message_code_generation)
162 } catch (Exception e) {
163 SetBuildStatus(failed, context, failure)
164 Error(exception_message_exec + command).call()
168 // TODO: protoc version could be different
169 Utils.markStageSkippedForConditional('Proto')
172 // if (GetModifiedFiles() != '') {
173 // throw new Exception(exception_message)
175 // } catch (Exception e) {
176 // SetBuildStatus(failed, context, failure)
178 // sh 'git status -s'
182 SetBuildStatus(completed, context, success)
186 def BaseImage(version, build_steps, registry, local_version) {
188 Build(base_image, version, build_steps, registry, local_version).call()
192 // Call Build function for every images in parallel
193 def Images(images, version, build_steps, registry, local_version) {
197 stages.put(i, Build(i, version, build_steps, registry, local_version))
203 // Build set the github commit status
204 def Build(image, version, build_steps, registry, local_version) {
206 stage("${image} (${version}): ${build_steps}") {
207 def context = "Image: ${image}"
208 def in_progress_message = "${in_progress} (${build_steps})"
209 def completed_message = "${completed} (${build_steps})"
210 def failed_message = "${failed} (${build_steps})"
211 def command = "make ${image} VERSION=${version} BUILD_STEPS='${build_steps}' REGISTRY=${registry} LOCAL_VERSION=${local_version} BASE_IMAGE=${base_image}:${local_version}"
213 SetBuildStatus(in_progress_message, context, pending)
214 ExecSh(command).call()
215 SetBuildStatus(completed_message, context, success)
216 } catch (Exception e) {
217 SetBuildStatus(failed_message, context, failure)
218 Error(exception_message_exec + command).call()
226 def E2e(e2e_enabled) {
227 if (e2e_enabled == 'true') {
229 echo 'make e2e' // todo
233 Utils.markStageSkippedForConditional('E2E')
238 // Raise error in Jenkins job
254 def ExecSh(command) {
263 // Set the commit status on Github
264 // https://plugins.jenkins.io/github/#plugin-content-pipeline-examples
265 def SetBuildStatus(String message, String context, String state) {
267 $class: 'GitHubCommitStatusSetter',
268 reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'https://github.com/Nordix/Meridio'],
269 commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: GetCommitSha()],
270 contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context],
271 errorHandlers: [[$class: 'ShallowAnyErrorHandler']], // Prevent GitHubCommitStatusSetter to set the job status to unstable
272 statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ]
276 // Return the current commit sha
278 return sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
281 // Returns if any files has been modified/added/removed
282 def GetModifiedFiles() {
283 return sh(script: 'git status -s', returnStdout: true).trim()