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-ubuntu2204') {
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/*',
60 if (currentBuild.result == 'FAILURE') {
61 Error('Failed at verification stage').call()
64 stage('Docker login') {
65 if (env.DRY_RUN != 'true') {
66 withCredentials([usernamePassword(credentialsId: 'nordix-cicd-harbor-credentials', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
68 echo $HARBOR_PASSWORD | docker login --username $HARBOR_USERNAME --password-stdin $IMAGE_REGISTRY
72 Utils.markStageSkippedForConditional('Docker login')
76 BaseImage(version, build_steps, image_registry, local_version).call()
79 Images(image_names, version, build_steps, image_registry, local_version).call()
80 if (currentBuild.result == 'FAILURE') {
81 Error('Failed to build image(s)').call()
85 if (e2e_enabled == 'true' && env.DRY_RUN != 'true') {
86 E2e(e2e_enabled).call()
88 Utils.markStageSkippedForConditional('E2E')
98 // Static analysis: Runs the GeneratedCode function and then UnitTests and Linter in parallel
101 GeneratedCode().call() // cannot generate code and run the linter and tests at the same time
103 // UnitTests().call()
105 stages.put('Unit Tests', UnitTests())
106 stages.put('Linter', Linter())
107 // stages.put('Generated code verification', GeneratedCode())
112 // Runs the unit tests and set the github commit status
115 def context = 'Unit Tests'
116 stage('Unit Tests') {
117 def command = 'make test'
119 SetBuildStatus(in_progress, context, pending)
120 ExecSh(command).call()
121 SetBuildStatus(completed, context, success)
122 } catch (Exception e) {
123 SetBuildStatus(failed, context, failure)
124 unstable "${exception_message_exec} ${command}"
125 currentBuild.result = 'FAILURE'
131 // Runs the linter and set the github commit status
134 def context = 'Linter'
136 def command = 'make lint'
138 SetBuildStatus(in_progress, context, pending)
139 ExecSh(command).call()
140 SetBuildStatus(completed, context, success)
141 } catch (Exception e) {
142 SetBuildStatus(failed, context, failure)
143 unstable "${exception_message_exec} ${command}"
144 currentBuild.result = 'FAILURE'
150 // Check if code has been generated correctly and set the github commit status:
151 // go.mod: runs "go mod tidy"
152 // go generate ./...: Code should be generated using "make genrate" command
153 // proto: skipped due to version of protoc
154 // If files are generated correctly then GetModifiedFiles function should return an empty string
155 def GeneratedCode() {
157 def context = 'Generated code verification'
158 SetBuildStatus(in_progress, context, pending)
159 stage('go mod tidy') {
160 def command = 'go mod tidy'
162 ExecSh(command).call()
163 if (GetModifiedFiles() != '') {
164 throw new Exception(exception_message_code_generation)
166 } catch (Exception e) {
167 SetBuildStatus(failed, context, failure)
168 Error(exception_message_exec + command).call()
171 stage('go generate ./...') {
172 def command = 'make generate'
174 ExecSh(command).call()
175 if (GetModifiedFiles() != '') {
176 throw new Exception(exception_message_code_generation)
178 } catch (Exception e) {
179 SetBuildStatus(failed, context, failure)
180 Error(exception_message_exec + command).call()
184 // TODO: protoc version could be different
185 Utils.markStageSkippedForConditional('Proto')
187 SetBuildStatus(completed, context, success)
191 def BaseImage(version, build_steps, registry, local_version) {
193 Build(base_image, version, build_steps, registry, local_version).call()
197 // Call Build function for every images in parallel
198 def Images(images, version, build_steps, registry, local_version) {
202 stages.put(i, Build(i, version, build_steps, registry, local_version))
208 // Build set the github commit status
209 def Build(image, version, build_steps, registry, local_version) {
211 stage("${image} (${version}): ${build_steps}") {
212 def context = "Image: ${image}"
213 def in_progress_message = "${in_progress} (${build_steps})"
214 def completed_message = "${completed} (${build_steps})"
215 def failed_message = "${failed} (${build_steps})"
216 def command = "make ${image} VERSION=${version} BUILD_STEPS='${build_steps}' REGISTRY=${registry} LOCAL_VERSION=${local_version} BASE_IMAGE=${base_image}:${local_version}"
218 SetBuildStatus(in_progress_message, context, pending)
219 ExecSh(command).call()
220 SetBuildStatus(completed_message, context, success)
221 } catch (Exception e) {
222 SetBuildStatus(failed_message, context, failure)
223 unstable "${exception_message_exec} ${command}"
224 currentBuild.result = 'FAILURE'
232 def E2e(e2e_enabled) {
234 echo 'make e2e' // todo
238 // Raise error in Jenkins job
254 def ExecSh(command) {
256 if (env.DRY_RUN != 'true') {
267 // Set the commit status on Github
268 // https://plugins.jenkins.io/github/#plugin-content-pipeline-examples
269 def SetBuildStatus(String message, String context, String state) {
270 if (env.DRY_RUN != 'true') {
272 $class: 'GitHubCommitStatusSetter',
273 reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'https://github.com/Nordix/Meridio'],
274 commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: GetCommitSha()],
275 contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context],
276 errorHandlers: [[$class: 'ShallowAnyErrorHandler']], // Prevent GitHubCommitStatusSetter to set the job status to unstable
277 statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ]
282 // Return the current commit sha
284 return sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
287 // Returns if any files has been modified/added/removed
288 def GetModifiedFiles() {
289 return sh(script: 'git status -s', returnStdout: true).trim()