blob: a902f5032e2644b2eb6458d65de0885f6c3af392 [file] [log] [blame]
Lionel Jouin85c872e2022-08-10 15:08:42 +02001/*
2Copyright (c) 2022 Nordix Foundation
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15*/
16
17import org.jenkinsci.plugins.pipeline.modeldefinition.Utils
18
19pending = 'PENDING'
20success = 'SUCCESS'
21failure = 'FAILURE'
22base_image = 'base-image'
23in_progress = 'In Progress.'
24completed = 'Completed.'
25failed = 'Failed'
26
Lionel Jouin26c167d2022-09-09 13:55:17 +020027node('nordix-nsm-build-ubuntu1804') {
Lionel Jouin85c872e2022-08-10 15:08:42 +020028 build_number = env.BUILD_NUMBER
29 workspace = env.WORKSPACE
30 ws("${workspace}/${build_number}") {
31 def image_names = params.IMAGE_NAMES.split(' ')
32 def version = params.IMAGE_VERSION
33 def e2e_enabled = params.E2E_ENABLED
34 def git_project = params.GIT_PROJECT
35 def current_branch = params.CURRENT_BRANCH
36 def default_branch = params.DEFAULT_BRANCH
37 def build_steps = params.BUILD_STEPS
38 def image_registry = params.IMAGE_REGISTRY
39 def local_version = "${env.JOB_NAME}-${build_number}"
40
Lionel Jouin26c167d2022-09-09 13:55:17 +020041 timeout(30) {
42 stage('Clone/Checkout') {
43 git branch: default_branch, url: git_project
44 checkout([
45 $class: 'GitSCM',
46 branches: [[name: current_branch]],
47 extensions: [],
48 userRemoteConfigs: [[
49 refspec: '+refs/pull/*/head:refs/remotes/origin/pr/*',
50 url: git_project
51 ]]
52 ])
53 sh 'git show'
Lionel Jouin85c872e2022-08-10 15:08:42 +020054 }
Lionel Jouin26c167d2022-09-09 13:55:17 +020055 stage('Verify') {
56 Verify().call()
57 }
58 stage('Docker login') {
59 withCredentials([usernamePassword(credentialsId: 'nordixinfra-harbor-creds-wrapper', passwordVariable: 'HARBOR_PASSWORD', usernameVariable: 'HARBOR_USERNAME')]) {
60 sh '''#!/bin/bash -eu
61 echo $HARBOR_PASSWORD | docker login --username $HARBOR_USERNAME --password-stdin $IMAGE_REGISTRY
62 '''
63 }
64 }
65 stage('Base Image') {
66 BaseImage(version, build_steps, image_registry, local_version).call()
67 }
68 stage('Images') {
69 Images(image_names, version, build_steps, image_registry, local_version).call()
70 }
71 stage('E2E') {
72 E2e(e2e_enabled).call()
73 }
Lionel Jouin85c872e2022-08-10 15:08:42 +020074 }
75 stage('Cleanup') {
76 Cleanup()
77 }
78 }
79}
80
81// Static analysis: Runs the GeneratedCode function and then UnitTests and Linter in parallel
82def Verify() {
83 return {
84 GeneratedCode().call() // cannot generate code and run the linter and tests at the same time
85 // Linter().call()
86 // UnitTests().call()
87 def stages = [:]
88 stages.put('Unit Tests', UnitTests())
89 stages.put('Linter', Linter())
90 // stages.put('Generated code verification', GeneratedCode())
91 parallel(stages)
92 }
93}
94
95// Runs the unit tests and set the github commit status
96def UnitTests() {
97 return {
98 def context = 'Unit Tests'
99 stage('Unit Tests') {
100 try {
101 SetBuildStatus(in_progress, context, pending)
robert.tomczyk0f893352022-09-08 18:26:30 +0100102 sh '''
103 . \${HOME}/.profile
104 make test
105 '''
Lionel Jouin85c872e2022-08-10 15:08:42 +0200106 SetBuildStatus(completed, context, success)
107 } catch (Exception e) {
108 SetBuildStatus(failed, context, failure)
109 Error(e).call()
110 }
111 }
112 }
113}
114
115// Runs the linter and set the github commit status
116def Linter() {
117 return {
118 def context = 'Linter'
119 stage('Linter') {
120 try {
121 SetBuildStatus(in_progress, context, pending)
robert.tomczyk0f893352022-09-08 18:26:30 +0100122 sh '''
123 . \${HOME}/.profile
124 make lint
125 '''
Lionel Jouin85c872e2022-08-10 15:08:42 +0200126 SetBuildStatus(completed, context, success)
127 } catch (Exception e) {
128 SetBuildStatus(failed, context, failure)
129 Error(e).call()
130 }
131 }
132 }
133}
134
135// Check if code has been generated correctly and set the github commit status:
136// go.mod: runs "go mod tidy"
137// go generate ./...: Code should be generated using "make genrate" command
138// proto: skipped due to version of protoc
139// If files are generated correctly then GetModifiedFiles function should return an empty string
140def GeneratedCode() {
141 return {
142 def context = 'Generated code verification'
143 def exception_message = 'Generated code verification failed'
144 SetBuildStatus(in_progress, context, pending)
145 stage('go mod tidy') {
146 try {
robert.tomczyk0f893352022-09-08 18:26:30 +0100147 sh '''
148 . \${HOME}/.profile
149 go mod tidy
150 '''
Lionel Jouin85c872e2022-08-10 15:08:42 +0200151 if (GetModifiedFiles() != '') {
152 throw new Exception(exception_message)
153 }
154 } catch (Exception e) {
155 SetBuildStatus(failed, context, failure)
156 sh 'git diff'
157 sh 'git status -s'
158 Error(e).call()
159 }
160 }
161 stage('go generate ./...') {
162 try {
robert.tomczyk0f893352022-09-08 18:26:30 +0100163 sh '''
164 . \${HOME}/.profile
165 make generate
166 '''
Lionel Jouin85c872e2022-08-10 15:08:42 +0200167 if (GetModifiedFiles() != '') {
168 throw new Exception(exception_message)
169 }
170 } catch (Exception e) {
171 SetBuildStatus(failed, context, failure)
172 sh 'git diff'
173 sh 'git status -s'
174 Error(e).call()
175 }
176 }
177 stage('Proto') {
178 // TODO: protoc version could be different
179 Utils.markStageSkippedForConditional('Proto')
180 // try {
181 // sh 'make proto'
182 // if (GetModifiedFiles() != '') {
183 // throw new Exception(exception_message)
184 // }
185 // } catch (Exception e) {
186 // SetBuildStatus(failed, context, failure)
187 // sh 'git diff'
188 // sh 'git status -s'
189 // Error(e).call()
190 // }
191 }
192 SetBuildStatus(completed, context, success)
193 }
194}
195
196def BaseImage(version, build_steps, registry, local_version) {
197 return {
198 Build(base_image, version, build_steps, registry, local_version).call()
199 }
200}
201
202// Call Build function for every images in parallel
203def Images(images, version, build_steps, registry, local_version) {
204 return {
205 def stages = [:]
206 for (i in images) {
207 stages.put(i, Build(i, version, build_steps, registry, local_version))
208 }
209 parallel(stages)
210 }
211}
212
213// Build set the github commit status
214def Build(image, version, build_steps, registry, local_version) {
215 return {
216 stage("${image} (${version}): ${build_steps}") {
217 def context = "Image: ${image}"
218 def in_progress_message = "${in_progress} (${build_steps})"
219 def completed_message = "${completed} (${build_steps})"
220 def failed_message = "${failed} (${build_steps})"
221 try {
222 SetBuildStatus(in_progress_message, context, pending)
223 sh "make ${image} VERSION=${version} BUILD_STEPS='${build_steps}' REGISTRY=${registry} LOCAL_VERSION=${local_version} BASE_IMAGE=${base_image}:${local_version}"
224 SetBuildStatus(completed_message, context, success)
225 } catch (Exception e) {
226 SetBuildStatus(failed_message, context, failure)
227 Error(e).call()
228 }
229 }
230 }
231}
232
233// Run the E2e Tests
234// Currently skipped
235def E2e(e2e_enabled) {
236 if (e2e_enabled == 'true') {
237 return {
238 echo 'make e2e' // todo
239 }
240 } else {
241 return {
242 Utils.markStageSkippedForConditional('E2E')
243 }
244 }
245}
246
247// Raise error in Jenkins job
248def Error(e) {
249 return {
250 Cleanup()
251 error e
252 }
253}
254
255// Cleanup directory
256def Cleanup() {
257 cleanWs()
258}
259
260// Set the commit status on Github
261// https://plugins.jenkins.io/github/#plugin-content-pipeline-examples
262def SetBuildStatus(String message, String context, String state) {
263 step([
264 $class: 'GitHubCommitStatusSetter',
265 reposSource: [$class: 'ManuallyEnteredRepositorySource', url: 'https://github.com/Nordix/Meridio'],
266 commitShaSource: [$class: 'ManuallyEnteredShaSource', sha: GetCommitSha()],
267 contextSource: [$class: 'ManuallyEnteredCommitContextSource', context: context],
268 errorHandlers: [[$class: 'ChangingBuildStatusErrorHandler', result: 'UNSTABLE']],
269 statusResultSource: [ $class: 'ConditionalStatusResultSource', results: [[$class: 'AnyBuildResult', message: message, state: state]] ]
270 ])
271}
272
273// Return the current commit sha
274def GetCommitSha() {
275 return sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
276}
277
278// Returns if any files has been modified/added/removed
279def GetModifiedFiles() {
280 return sh(script: 'git status -s', returnStdout: true).trim()
281}