blob: 2633231a35373853cd838710d8497c5d6fffc558 [file] [log] [blame]
garciadeblasd2332d02025-12-23 12:33:00 +01001/*
2 Licensed under the Apache License, Version 2.0 (the "License");
3 you may not use this file except in compliance with the License.
4 You may obtain a copy of the License at
5
6 http://www.apache.org/licenses/LICENSE-2.0
7
8 Unless required by applicable law or agreed to in writing, software
9 distributed under the License is distributed on an "AS IS" BASIS,
10 WITHOUT WARRANTIES OR CONDITIONS OF ANY, either express or
11 implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14*/
15
garciadeblas170d8922025-12-26 12:11:25 +010016def DEFAULT_MODULE_NAME = 'mon'
17
garciadeblasd2332d02025-12-23 12:33:00 +010018pipeline {
garciadeblas170d8922025-12-26 12:11:25 +010019 agent { label 'pool' }
20 options { disableConcurrentBuilds() }
21 parameters {
22 // Core Gerrit / multibranch inputs
23 string(name: 'GERRIT_BRANCH', defaultValue: env.BRANCH_NAME ?: 'master', description: '')
24 string(name: 'GERRIT_PROJECT', defaultValue: 'osm/MON', description: '')
25 string(name: 'GERRIT_REFSPEC', defaultValue: env.GERRIT_REFSPEC ?: '', description: '')
26 string(name: 'GERRIT_PATCHSET_REVISION', defaultValue: env.GERRIT_PATCHSET_REVISION ?: '', description: '')
27 string(name: 'PROJECT_URL_PREFIX', defaultValue: 'https://osm.etsi.org/gerrit', description: '')
garciadeblas170d8922025-12-26 12:11:25 +010028 string(name: 'DOCKER_ARGS', defaultValue: '', description: 'Extra docker args for docker run')
29
caviedesjf1ff02f2026-02-12 11:14:06 +010030 // Build and test configuration
caviedesjfcb64912026-01-19 17:32:53 +010031 string(name: 'OPENSTACK_BASE_IMAGE', defaultValue: 'ubuntu24.04', description: '')
garciadeblas170d8922025-12-26 12:11:25 +010032 string(name: 'OPENSTACK_OSM_FLAVOR', defaultValue: 'osm.sanity', description: '')
caviedesjf1ff02f2026-02-12 11:14:06 +010033 string(name: 'MODULE_NAME', defaultValue: 'MON', description: 'Name of the module under test')
34 string(name: 'IMAGENAME', defaultValue: 'opensourcemano/mon', description: 'Image name for publish')
garciadeblas170d8922025-12-26 12:11:25 +010035
caviedesjf1ff02f2026-02-12 11:14:06 +010036 // Stage control flags
garciadeblas170d8922025-12-26 12:11:25 +010037 booleanParam(name: 'DO_INSTALL', defaultValue: true, description: '')
38 booleanParam(name: 'DO_DOCKERPUSH', defaultValue: true, description: '')
39 booleanParam(name: 'DO_ROBOT', defaultValue: true, description: '')
garciadeblas170d8922025-12-26 12:11:25 +010040 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', defaultValue: false, description: '')
41 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', defaultValue: false, description: '')
garciadeblas170d8922025-12-26 12:11:25 +010042 }
43 environment {
44 MDG = "${params.GERRIT_PROJECT?.contains('/') ? params.GERRIT_PROJECT.split('/')[1] : params.GERRIT_PROJECT}"
45 CONTAINER_NAME = "${params.GERRIT_PROJECT}-${params.GERRIT_BRANCH}".toLowerCase()
caviedesjcd184872026-02-12 19:03:53 +010046 TEST_IMAGE = 'opensourcemano/tox-osm:19.0'
garciadeblas170d8922025-12-26 12:11:25 +010047 DOCKER_REGISTRY = 'osm.etsi.org:5050/devops/cicd/'
48 }
49 stages {
50 stage('Prepare') { steps { sh 'env' } }
51
52 stage('Checkout') {
53 steps {
54 checkout scm
55 script {
56 sh "git fetch --tags"
57 if (params.GERRIT_REFSPEC?.trim()) { sh "git fetch origin ${params.GERRIT_REFSPEC}" }
58 if (params.GERRIT_PATCHSET_REVISION?.trim()) { sh "git checkout -f ${params.GERRIT_PATCHSET_REVISION}" }
59 sh "sudo git clean -dfx || git clean -dfx"
garciadeblasd2332d02025-12-23 12:33:00 +010060 }
garciadeblas170d8922025-12-26 12:11:25 +010061 }
garciadeblasd2332d02025-12-23 12:33:00 +010062 }
garciadeblas170d8922025-12-26 12:11:25 +010063
64 stage('Clone devops (central)') {
65 steps {
66 dir('devops') {
67 sh "git init ."
68 sh "git remote remove origin || true"
69 sh "git remote add origin ${params.PROJECT_URL_PREFIX}/osm/devops"
70 sh "git fetch --depth=1 origin ${params.GERRIT_BRANCH}"
71 sh "git checkout -f FETCH_HEAD"
72 }
73 }
74 }
75
76 stage('License Scan') {
77 steps {
78 script {
79 def isMergeJob = env.JOB_NAME?.contains('merge')
80 if (!isMergeJob) { sh 'devops/tools/license_scan.sh' } else { echo 'skip the scan for merge' }
81 }
82 }
83 }
84
85 stage('Prepare Test Image') {
86 steps {
87 script {
88 // Use shared test image from registry; no local build needed
89 sh "docker pull ${env.TEST_IMAGE} || true"
90 }
91 }
92 }
93
caviedesj31e83ba2026-01-08 18:33:54 +010094 stage('Tests') {
garciadeblas170d8922025-12-26 12:11:25 +010095 steps {
96 script {
garciadeblas170d8922025-12-26 12:11:25 +010097 def UID = sh(returnStdout: true, script: 'id -u').trim()
98 def GID = sh(returnStdout: true, script: 'id -g').trim()
99 def common = "-v ${env.WORKSPACE}:/tests -e UID=${UID} -e GID=${GID} " + (params.DOCKER_ARGS ?: '')
100
caviedesj31e83ba2026-01-08 18:33:54 +0100101 stage('Linting Tests') {
102 sh """
103 docker run --rm ${common} \
104 ${env.TEST_IMAGE} \
105 /tests/devops-stages/stage-lint.sh
106 """
107 }
108
109 stage('Unit Tests') {
garciadeblas170d8922025-12-26 12:11:25 +0100110 sh """
111 docker run --rm ${common} \
112 ${env.TEST_IMAGE} \
113 /tests/devops-stages/stage-test.sh
114 """
caviedesj114cec12026-02-26 12:50:30 +0100115 if (fileExists('coverage.xml')) {
116 recordCoverage tools: [[
117 parser: 'COBERTURA',
118 pattern: 'coverage.xml'
119 ]], sourceDirectories: []
120 }
121
122 if (fileExists('nosetests.xml')) {
123 junit 'nosetests.xml'
124 }
garciadeblas170d8922025-12-26 12:11:25 +0100125 }
126
127 stage('Changelog') {
128 sh 'mkdir -p changelog'
129 sh """
130 docker run --rm ${common} \
131 ${env.TEST_IMAGE} \
garciadeblasaf00b512026-02-14 11:08:47 +0100132 sh -c 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
garciadeblas170d8922025-12-26 12:11:25 +0100133 """
134 }
135 }
136 }
137 }
138
139 stage('Build & Push Image') {
140 when { expression { return params.DO_DOCKERPUSH } }
141 steps {
142 script {
143 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
144
145 if (!params.GERRIT_BRANCH) {
146 error 'GERRIT_BRANCH is required to tag the Docker image'
147 }
148 def sanitizedBranchName = params.GERRIT_BRANCH
149 .toLowerCase()
150 .replaceAll('[^a-z0-9._-]', '-')
151 def baseTagPrefix = "osm-${sanitizedBranchName}"
152 def buildNumber = env.BUILD_NUMBER ?: '0'
153 def isMergeJob = env.JOB_NAME?.contains('merge')
154 // Remove promotion logic from this stage
155 def moduleTags = []
156 if (isMergeJob) {
caviedesj272cc5c2026-01-22 14:26:27 +0100157 moduleTags << "${baseTagPrefix}-merge"
garciadeblas170d8922025-12-26 12:11:25 +0100158 } else {
159 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
160 }
161
162 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
163 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
164
165 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
166 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
167 sh """
168 docker build -f Dockerfile.production -t ${primaryLocalImage} .
169 """
170 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
171 // Push build-scope tag(s) only. Promotion happens after tests.
172 moduleTags.each { tag ->
173 def localImage = "${imageName}:${tag}"
174 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
175 sh "docker tag ${localImage} ${remoteImage}"
176 sh "docker push ${remoteImage}"
177 }
178 // Stash the built image id for later promotion without rebuild
179 env.BUILT_IMAGE = primaryLocalImage
180 env.BUILT_TAG = moduleTags[0]
181 }
182 }
183 }
184 }
185
186 stage('E2E Test (robot)') {
187 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
188 steps {
189 script {
190 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
191 build job: dowstreamJob,
192 parameters: [
193 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
194 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
195 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
196 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
197 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'MON'),
198 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
199 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
200 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
201 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
202 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
203 ]
204 }
205 }
206 }
207 stage('Image Promotion') {
208 when { expression { return env.JOB_NAME?.contains('merge') } }
209 steps {
210 script {
211 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
212 def sanitizedBranchName = params.GERRIT_BRANCH
213 .toLowerCase()
214 .replaceAll('[^a-z0-9._-]', '-')
215 def baseTagPrefix = "osm-${sanitizedBranchName}"
216 def targetTag = "${baseTagPrefix}-merge"
217 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
218
219 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
220 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
221 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
222 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
223 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
224 sh "docker push ${remoteImage}"
225 }
226 }
227 }
228 }
229
230 /*
231 Promotion should be done in a separate downstream job after E2E success to avoid
232 */
233 }
caviedesj31e83ba2026-01-08 18:33:54 +0100234
garciadeblas170d8922025-12-26 12:11:25 +0100235 post {
236 always {
caviedesj31e83ba2026-01-08 18:33:54 +0100237 // cleanWs()
238 deleteDir()
garciadeblas170d8922025-12-26 12:11:25 +0100239 }
240 }
garciadeblasd2332d02025-12-23 12:33:00 +0100241}
242