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