blob: 3b51ddec9e5503f56a04006d31dbb3f4a7be2f8f [file] [log] [blame]
garciadeblas57c12492025-12-23 09:48:52 +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
garciadeblasafa3db92025-12-26 10:39:20 +010016def DEFAULT_MODULE_NAME = 'lcm'
17
garciadeblas57c12492025-12-23 09:48:52 +010018pipeline {
garciadeblasafa3db92025-12-26 10:39:20 +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/LCM', 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: '')
garciadeblasafa3db92025-12-26 10:39:20 +010028 string(name: 'DOCKER_ARGS', defaultValue: '', description: 'Extra docker args for docker run')
29
caviedesj7aa1b0d2026-02-12 11:04:56 +010030 // E2E test parameters
caviedesj3bfae902026-01-19 17:36:56 +010031 string(name: 'OPENSTACK_BASE_IMAGE', defaultValue: 'ubuntu24.04', description: '')
garciadeblasafa3db92025-12-26 10:39:20 +010032 string(name: 'OPENSTACK_OSM_FLAVOR', defaultValue: 'osm.sanity', description: '')
caviedesj7aa1b0d2026-02-12 11:04:56 +010033 string(name: 'MODULE_NAME', defaultValue: 'LCM', description: 'Name of the module under test')
garciadeblasafa3db92025-12-26 10:39:20 +010034
caviedesj7aa1b0d2026-02-12 11:04:56 +010035 // Flow control flags
garciadeblasafa3db92025-12-26 10:39:20 +010036 booleanParam(name: 'DO_INSTALL', defaultValue: true, description: '')
37 booleanParam(name: 'DO_DOCKERPUSH', defaultValue: true, description: '')
38 booleanParam(name: 'DO_ROBOT', defaultValue: true, description: '')
garciadeblasafa3db92025-12-26 10:39:20 +010039 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', defaultValue: false, description: '')
40 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', defaultValue: false, description: '')
garciadeblasafa3db92025-12-26 10:39:20 +010041
caviedesj7aa1b0d2026-02-12 11:04:56 +010042 // Image naming
43 string(name: 'IMAGENAME', defaultValue: 'opensourcemano/lcm', description: 'Image name for publish')
garciadeblasafa3db92025-12-26 10:39:20 +010044 }
45 environment {
46 MDG = "${params.GERRIT_PROJECT?.contains('/') ? params.GERRIT_PROJECT.split('/')[1] : params.GERRIT_PROJECT}"
47 CONTAINER_NAME = "${params.GERRIT_PROJECT}-${params.GERRIT_BRANCH}".toLowerCase()
caviedesj880dceb2026-02-12 19:06:24 +010048 TEST_IMAGE = 'opensourcemano/tox-osm:19.0'
garciadeblasafa3db92025-12-26 10:39:20 +010049 DOCKER_REGISTRY = 'osm.etsi.org:5050/devops/cicd/'
50 }
51 stages {
52 stage('Prepare') { steps { sh 'env' } }
53
54 stage('Checkout') {
55 steps {
56 checkout scm
57 script {
58 sh "git fetch --tags"
59 if (params.GERRIT_REFSPEC?.trim()) { sh "git fetch origin ${params.GERRIT_REFSPEC}" }
60 if (params.GERRIT_PATCHSET_REVISION?.trim()) { sh "git checkout -f ${params.GERRIT_PATCHSET_REVISION}" }
61 sh "sudo git clean -dfx || git clean -dfx"
garciadeblas57c12492025-12-23 09:48:52 +010062 }
garciadeblasafa3db92025-12-26 10:39:20 +010063 }
garciadeblas57c12492025-12-23 09:48:52 +010064 }
garciadeblasafa3db92025-12-26 10:39:20 +010065
66 stage('Clone devops (central)') {
67 steps {
68 dir('devops') {
69 sh "git init ."
70 sh "git remote remove origin || true"
71 sh "git remote add origin ${params.PROJECT_URL_PREFIX}/osm/devops"
72 sh "git fetch --depth=1 origin ${params.GERRIT_BRANCH}"
73 sh "git checkout -f FETCH_HEAD"
74 }
75 }
76 }
77
78 stage('License Scan') {
79 steps {
80 script {
81 def isMergeJob = env.JOB_NAME?.contains('merge')
82 if (!isMergeJob) { sh 'devops/tools/license_scan.sh' } else { echo 'skip the scan for merge' }
83 }
84 }
85 }
86
87 stage('Prepare Test Image') {
88 steps {
89 script {
90 // Use shared test image from registry; no local build needed
91 sh "docker pull ${env.TEST_IMAGE} || true"
92 }
93 }
94 }
95
96 stage('Tests') {
97 steps {
98 script {
99 def UID = sh(returnStdout: true, script: 'id -u').trim()
100 def GID = sh(returnStdout: true, script: 'id -g').trim()
101 def common = "-v ${env.WORKSPACE}:/tests -e UID=${UID} -e GID=${GID} " + (params.DOCKER_ARGS ?: '')
102
103 stage('Linting Tests') {
104 sh """
105 docker run --rm ${common} \
106 ${env.TEST_IMAGE} \
107 /tests/devops-stages/stage-lint.sh
108 """
109 }
110
111 stage('Unit Tests') {
112 sh """
113 docker run --rm ${common} \
114 ${env.TEST_IMAGE} \
115 /tests/devops-stages/stage-test.sh
116 """
caviedesj0c6e85c2026-02-26 12:48:56 +0100117 if (fileExists('coverage.xml')) {
118 recordCoverage tools: [[
119 parser: 'COBERTURA',
120 pattern: 'coverage.xml'
121 ]], sourceDirectories: []
122 }
123
124 if (fileExists('nosetests.xml')) {
125 junit 'nosetests.xml'
126 }
garciadeblasafa3db92025-12-26 10:39:20 +0100127 }
128
129 stage('Changelog') {
130 sh 'mkdir -p changelog'
131 sh """
132 docker run --rm ${common} \
133 ${env.TEST_IMAGE} \
caviedesj72b38802026-02-13 16:11:34 +0100134 sh -c 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
garciadeblasafa3db92025-12-26 10:39:20 +0100135 """
136 }
137 }
138 }
139 }
140
141 stage('Build & Push Image') {
142 when { expression { return params.DO_DOCKERPUSH } }
143 steps {
144 script {
145 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
146
147 if (!params.GERRIT_BRANCH) {
148 error 'GERRIT_BRANCH is required to tag the Docker image'
149 }
150 def sanitizedBranchName = params.GERRIT_BRANCH
151 .toLowerCase()
152 .replaceAll('[^a-z0-9._-]', '-')
153 def baseTagPrefix = "osm-${sanitizedBranchName}"
154 def buildNumber = env.BUILD_NUMBER ?: '0'
155 def isMergeJob = env.JOB_NAME?.contains('merge')
156 // Remove promotion logic from this stage
157 def moduleTags = []
158 if (isMergeJob) {
caviedesj862f4f22026-01-22 14:27:41 +0100159 moduleTags << "${baseTagPrefix}-merge"
garciadeblasafa3db92025-12-26 10:39:20 +0100160 } else {
161 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
162 }
163
164 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
165 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
166
167 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
168 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
169 sh """
170 docker build -f Dockerfile.production -t ${primaryLocalImage} .
171 """
172 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
173 // Push build-scope tag(s) only. Promotion happens after tests.
174 moduleTags.each { tag ->
175 def localImage = "${imageName}:${tag}"
176 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
177 sh "docker tag ${localImage} ${remoteImage}"
178 sh "docker push ${remoteImage}"
179 }
180 // Stash the built image id for later promotion without rebuild
181 env.BUILT_IMAGE = primaryLocalImage
182 env.BUILT_TAG = moduleTags[0]
183 }
184 }
185 }
186 }
187
188 stage('E2E Test (robot)') {
189 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
190 steps {
191 script {
192 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
193 build job: dowstreamJob,
194 parameters: [
195 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
196 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
197 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
198 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
199 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'LCM'),
200 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
201 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
202 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
203 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
204 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
205 ]
206 }
207 }
208 }
209 stage('Image Promotion') {
210 when { expression { return env.JOB_NAME?.contains('merge') } }
211 steps {
212 script {
213 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
214 def sanitizedBranchName = params.GERRIT_BRANCH
215 .toLowerCase()
216 .replaceAll('[^a-z0-9._-]', '-')
217 def baseTagPrefix = "osm-${sanitizedBranchName}"
218 def targetTag = "${baseTagPrefix}-merge"
219 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
220
221 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
222 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
223 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
224 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
225 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
226 sh "docker push ${remoteImage}"
227 }
228 }
229 }
230 }
231
232 /*
233 Promotion should be done in a separate downstream job after E2E success to avoid
234 */
235 }
caviedesj14fff7c2026-01-09 12:53:52 +0100236
garciadeblasafa3db92025-12-26 10:39:20 +0100237 post {
238 always {
caviedesj14fff7c2026-01-09 12:53:52 +0100239 // cleanWs()
240 deleteDir()
garciadeblasafa3db92025-12-26 10:39:20 +0100241 }
242 }
garciadeblas57c12492025-12-23 09:48:52 +0100243}
244