blob: 94d085c17cfe7f5d2383dd89c7feec862151ea9e [file] [log] [blame]
garciadeblasd2b741e2025-10-31 11:12:37 +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
mesajbdf44322025-10-20 17:21:34 +020016def DEFAULT_MODULE_NAME = 'nbi'
garciadeblasd2b741e2025-10-31 11:12:37 +010017
mesajbdf44322025-10-20 17:21:34 +020018pipeline {
19 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/NBI', 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: '')
mesajbdf44322025-10-20 17:21:34 +020028 string(name: 'DOCKER_ARGS', defaultValue: '', description: 'Extra docker args for docker run')
29
caviedesj712762c2026-02-12 11:18:53 +010030 // OpenStack configuration
caviedesj1ff8d3b2026-01-19 17:30:49 +010031 string(name: 'OPENSTACK_BASE_IMAGE', defaultValue: 'ubuntu24.04', description: '')
mesajbdf44322025-10-20 17:21:34 +020032 string(name: 'OPENSTACK_OSM_FLAVOR', defaultValue: 'osm.sanity', description: '')
33
caviedesj712762c2026-02-12 11:18:53 +010034 // Stage toggles
mesajbdf44322025-10-20 17:21:34 +020035 booleanParam(name: 'DO_INSTALL', defaultValue: true, description: '')
36 booleanParam(name: 'DO_DOCKERPUSH', defaultValue: true, description: '')
37 booleanParam(name: 'DO_ROBOT', defaultValue: true, description: '')
caviedesj712762c2026-02-12 11:18:53 +010038
39 // Module configuration
mesajbdf44322025-10-20 17:21:34 +020040 string(name: 'MODULE_NAME', defaultValue: 'NBI', description: 'Name of the module under test')
41
caviedesj712762c2026-02-12 11:18:53 +010042 // Container management
mesajbdf44322025-10-20 17:21:34 +020043 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', defaultValue: false, description: '')
44 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', defaultValue: false, description: '')
mesajbdf44322025-10-20 17:21:34 +020045
caviedesj712762c2026-02-12 11:18:53 +010046 // Image configuration
47 string(name: 'IMAGENAME', defaultValue: 'opensourcemano/nbi', description: 'Image name for publish')
mesajbdf44322025-10-20 17:21:34 +020048 }
49 environment {
50 MDG = "${params.GERRIT_PROJECT?.contains('/') ? params.GERRIT_PROJECT.split('/')[1] : params.GERRIT_PROJECT}"
51 CONTAINER_NAME = "${params.GERRIT_PROJECT}-${params.GERRIT_BRANCH}".toLowerCase()
caviedesj9cb94d72026-02-12 18:39:17 +010052 TEST_IMAGE = 'opensourcemano/tox-osm:19.0'
mesajbdf44322025-10-20 17:21:34 +020053 DOCKER_REGISTRY = 'osm.etsi.org:5050/devops/cicd/'
54 }
55 stages {
56 stage('Prepare') { steps { sh 'env' } }
57
58 stage('Checkout') {
59 steps {
60 checkout scm
61 script {
62 sh "git fetch --tags"
63 if (params.GERRIT_REFSPEC?.trim()) { sh "git fetch origin ${params.GERRIT_REFSPEC}" }
64 if (params.GERRIT_PATCHSET_REVISION?.trim()) { sh "git checkout -f ${params.GERRIT_PATCHSET_REVISION}" }
65 sh "sudo git clean -dfx || git clean -dfx"
66 }
67 }
68 }
69
70 stage('Clone devops (central)') {
71 steps {
72 dir('devops') {
73 sh "git init ."
74 sh "git remote remove origin || true"
75 sh "git remote add origin ${params.PROJECT_URL_PREFIX}/osm/devops"
76 sh "git fetch --depth=1 origin ${params.GERRIT_BRANCH}"
77 sh "git checkout -f FETCH_HEAD"
78 }
79 }
80 }
81
82 stage('License Scan') {
83 steps {
84 script {
85 def isMergeJob = env.JOB_NAME?.contains('merge')
86 if (!isMergeJob) { sh 'devops/tools/license_scan.sh' } else { echo 'skip the scan for merge' }
87 }
88 }
89 }
90
91 stage('Prepare Test Image') {
92 steps {
93 script {
94 // Use shared test image from registry; no local build needed
95 sh "docker pull ${env.TEST_IMAGE} || true"
96 }
97 }
98 }
99
caviedesj26567a22026-01-13 16:50:22 +0100100 stage('Tests') {
mesajbdf44322025-10-20 17:21:34 +0200101 steps {
102 script {
mesajbdf44322025-10-20 17:21:34 +0200103 def UID = sh(returnStdout: true, script: 'id -u').trim()
104 def GID = sh(returnStdout: true, script: 'id -g').trim()
105 def common = "-v ${env.WORKSPACE}:/tests -e UID=${UID} -e GID=${GID} " + (params.DOCKER_ARGS ?: '')
106
caviedesj26567a22026-01-13 16:50:22 +0100107 stage('Linting Tests') {
108 sh """
109 docker run --rm ${common} \
110 ${env.TEST_IMAGE} \
111 /tests/devops-stages/stage-lint.sh
112 """
113 }
114
115 stage('Unit Tests') {
mesajbdf44322025-10-20 17:21:34 +0200116 sh """
117 docker run --rm ${common} \
118 ${env.TEST_IMAGE} \
119 /tests/devops-stages/stage-test.sh
120 """
caviedesj4236f572026-02-26 12:51:19 +0100121 if (fileExists('coverage.xml')) {
122 recordCoverage tools: [[
123 parser: 'COBERTURA',
124 pattern: 'coverage.xml'
125 ]], sourceDirectories: []
126 }
127
128 if (fileExists('nosetests.xml')) {
129 junit 'nosetests.xml'
130 }
mesajbdf44322025-10-20 17:21:34 +0200131 }
132
133 stage('Changelog') {
134 sh 'mkdir -p changelog'
135 sh """
136 docker run --rm ${common} \
137 ${env.TEST_IMAGE} \
garciadeblas78bcb9e2026-02-14 11:09:31 +0100138 sh -c 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
mesajbdf44322025-10-20 17:21:34 +0200139 """
140 }
141 }
142 }
143 }
144
145 stage('Build & Push Image') {
146 when { expression { return params.DO_DOCKERPUSH } }
147 steps {
148 script {
149 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
150
151 if (!params.GERRIT_BRANCH) {
152 error 'GERRIT_BRANCH is required to tag the Docker image'
153 }
154 def sanitizedBranchName = params.GERRIT_BRANCH
155 .toLowerCase()
156 .replaceAll('[^a-z0-9._-]', '-')
157 def baseTagPrefix = "osm-${sanitizedBranchName}"
158 def buildNumber = env.BUILD_NUMBER ?: '0'
159 def isMergeJob = env.JOB_NAME?.contains('merge')
160 // Remove promotion logic from this stage
161 def moduleTags = []
162 if (isMergeJob) {
caviedesj190e83f2026-01-22 14:23:29 +0100163 moduleTags << "${baseTagPrefix}-merge"
mesajbdf44322025-10-20 17:21:34 +0200164 } else {
165 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
166 }
167
168 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
169 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
170
171 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
172 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
173 sh """
174 docker build -f Dockerfile.production -t ${primaryLocalImage} .
175 """
176 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
177 // Push build-scope tag(s) only. Promotion happens after tests.
178 moduleTags.each { tag ->
179 def localImage = "${imageName}:${tag}"
180 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
181 sh "docker tag ${localImage} ${remoteImage}"
182 sh "docker push ${remoteImage}"
183 }
184 // Stash the built image id for later promotion without rebuild
185 env.BUILT_IMAGE = primaryLocalImage
186 env.BUILT_TAG = moduleTags[0]
187 }
188 }
189 }
190 }
191
192 stage('E2E Test (robot)') {
193 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
194 steps {
195 script {
196 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
197 build job: dowstreamJob,
198 parameters: [
199 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
200 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
201 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
202 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
203 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'NBI'),
204 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
205 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
206 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
207 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
208 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
209 ]
210 }
211 }
212 }
213 stage('Image Promotion') {
214 when { expression { return env.JOB_NAME?.contains('merge') } }
215 steps {
216 script {
217 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
218 def sanitizedBranchName = params.GERRIT_BRANCH
219 .toLowerCase()
220 .replaceAll('[^a-z0-9._-]', '-')
221 def baseTagPrefix = "osm-${sanitizedBranchName}"
222 def targetTag = "${baseTagPrefix}-merge"
223 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
224
225 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
226 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
227 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
228 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
229 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
230 sh "docker push ${remoteImage}"
231 }
232 }
233 }
234 }
235
236 /*
237 Promotion should be done in a separate downstream job after E2E success to avoid
238 */
239 }
caviedesj26567a22026-01-13 16:50:22 +0100240
mesajbdf44322025-10-20 17:21:34 +0200241 post {
242 always {
caviedesj26567a22026-01-13 16:50:22 +0100243 // cleanWs()
244 deleteDir()
mesajbdf44322025-10-20 17:21:34 +0200245 }
246 }
247}