blob: 86a81aa07e29bb3d0ff79c430413e8fecebcc6d8 [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()
46 TEST_IMAGE = 'overdrive3000/tox-osm:v1.6'
47 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 """
115 if (fileExists('coverage.xml')) { cobertura coberturaReportFile: 'coverage.xml' }
116 if (fileExists('nosetests.xml')) { junit 'nosetests.xml' }
117 }
118
119 stage('Changelog') {
120 sh 'mkdir -p changelog'
121 sh """
122 docker run --rm ${common} \
123 ${env.TEST_IMAGE} \
124 /bin/sh -lc 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
125 """
126 }
127 }
128 }
129 }
130
131 stage('Build & Push Image') {
132 when { expression { return params.DO_DOCKERPUSH } }
133 steps {
134 script {
135 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
136
137 if (!params.GERRIT_BRANCH) {
138 error 'GERRIT_BRANCH is required to tag the Docker image'
139 }
140 def sanitizedBranchName = params.GERRIT_BRANCH
141 .toLowerCase()
142 .replaceAll('[^a-z0-9._-]', '-')
143 def baseTagPrefix = "osm-${sanitizedBranchName}"
144 def buildNumber = env.BUILD_NUMBER ?: '0'
145 def isMergeJob = env.JOB_NAME?.contains('merge')
146 // Remove promotion logic from this stage
147 def moduleTags = []
148 if (isMergeJob) {
caviedesj272cc5c2026-01-22 14:26:27 +0100149 moduleTags << "${baseTagPrefix}-merge"
garciadeblas170d8922025-12-26 12:11:25 +0100150 } else {
151 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
152 }
153
154 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
155 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
156
157 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
158 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
159 sh """
160 docker build -f Dockerfile.production -t ${primaryLocalImage} .
161 """
162 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
163 // Push build-scope tag(s) only. Promotion happens after tests.
164 moduleTags.each { tag ->
165 def localImage = "${imageName}:${tag}"
166 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
167 sh "docker tag ${localImage} ${remoteImage}"
168 sh "docker push ${remoteImage}"
169 }
170 // Stash the built image id for later promotion without rebuild
171 env.BUILT_IMAGE = primaryLocalImage
172 env.BUILT_TAG = moduleTags[0]
173 }
174 }
175 }
176 }
177
178 stage('E2E Test (robot)') {
179 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
180 steps {
181 script {
182 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
183 build job: dowstreamJob,
184 parameters: [
185 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
186 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
187 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
188 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
189 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'MON'),
190 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
191 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
192 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
193 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
194 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
195 ]
196 }
197 }
198 }
199 stage('Image Promotion') {
200 when { expression { return env.JOB_NAME?.contains('merge') } }
201 steps {
202 script {
203 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
204 def sanitizedBranchName = params.GERRIT_BRANCH
205 .toLowerCase()
206 .replaceAll('[^a-z0-9._-]', '-')
207 def baseTagPrefix = "osm-${sanitizedBranchName}"
208 def targetTag = "${baseTagPrefix}-merge"
209 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
210
211 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
212 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
213 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
214 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
215 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
216 sh "docker push ${remoteImage}"
217 }
218 }
219 }
220 }
221
222 /*
223 Promotion should be done in a separate downstream job after E2E success to avoid
224 */
225 }
caviedesj31e83ba2026-01-08 18:33:54 +0100226
garciadeblas170d8922025-12-26 12:11:25 +0100227 post {
228 always {
caviedesj31e83ba2026-01-08 18:33:54 +0100229 // cleanWs()
230 deleteDir()
garciadeblas170d8922025-12-26 12:11:25 +0100231 }
232 }
garciadeblasd2332d02025-12-23 12:33:00 +0100233}
234