blob: 29406fabdb418cc02d54488888d582fcb2b7b62d [file] [log] [blame]
garciadeblas61f98692025-12-23 16:24:09 +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
caviedesj00417832026-01-09 11:34:42 +010016def DEFAULT_MODULE_NAME = 'osmclient'
17
garciadeblas61f98692025-12-23 16:24:09 +010018pipeline {
caviedesj00417832026-01-09 11:34:42 +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/osmclient', 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: '')
caviedesj00417832026-01-09 11:34:42 +010028 string(name: 'DOCKER_ARGS', defaultValue: '', description: 'Extra docker args for docker run')
29
caviedesj7c0b58c2026-02-12 11:25:00 +010030 // E2E test configuration
caviedesjd119d452026-01-19 17:29:47 +010031 string(name: 'OPENSTACK_BASE_IMAGE', defaultValue: 'ubuntu24.04', description: '')
caviedesj00417832026-01-09 11:34:42 +010032 string(name: 'OPENSTACK_OSM_FLAVOR', defaultValue: 'osm.sanity', description: '')
caviedesj7c0b58c2026-02-12 11:25:00 +010033 string(name: 'MODULE_NAME', defaultValue: 'osmclient', description: 'Name of the module under test')
caviedesj00417832026-01-09 11:34:42 +010034
caviedesj7c0b58c2026-02-12 11:25:00 +010035 // Pipeline control flags
caviedesj00417832026-01-09 11:34:42 +010036 booleanParam(name: 'DO_INSTALL', defaultValue: true, description: '')
37 booleanParam(name: 'DO_DOCKERPUSH', defaultValue: true, description: '')
38 booleanParam(name: 'DO_ROBOT', defaultValue: true, description: '')
caviedesj00417832026-01-09 11:34:42 +010039 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', defaultValue: false, description: '')
40 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', defaultValue: false, description: '')
caviedesj00417832026-01-09 11:34:42 +010041
caviedesj7c0b58c2026-02-12 11:25:00 +010042 // Docker image configuration
43 string(name: 'IMAGENAME', defaultValue: 'opensourcemano/osmclient', description: 'Image name for publish')
caviedesj00417832026-01-09 11:34:42 +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()
caviedesj7be06512026-02-12 18:36:01 +010048 TEST_IMAGE = 'opensourcemano/tox-osm:19.0'
caviedesj00417832026-01-09 11:34:42 +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"
garciadeblas61f98692025-12-23 16:24:09 +010062 }
caviedesj00417832026-01-09 11:34:42 +010063 }
garciadeblas61f98692025-12-23 16:24:09 +010064 }
caviedesj00417832026-01-09 11:34:42 +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 """
117 if (fileExists('coverage.xml')) { cobertura coberturaReportFile: 'coverage.xml' }
118 if (fileExists('nosetests.xml')) { junit 'nosetests.xml' }
119 }
120
121 stage('Changelog') {
122 sh 'mkdir -p changelog'
123 sh """
124 docker run --rm ${common} \
125 ${env.TEST_IMAGE} \
garciadeblas0b557202026-02-14 11:08:23 +0100126 sh -c 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
caviedesj00417832026-01-09 11:34:42 +0100127 """
128 }
129 }
130 }
131 }
132
133 stage('Build & Push Image') {
134 when { expression { return params.DO_DOCKERPUSH } }
135 steps {
136 script {
137 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
138
139 if (!params.GERRIT_BRANCH) {
140 error 'GERRIT_BRANCH is required to tag the Docker image'
141 }
142 def sanitizedBranchName = params.GERRIT_BRANCH
143 .toLowerCase()
144 .replaceAll('[^a-z0-9._-]', '-')
145 def baseTagPrefix = "osm-${sanitizedBranchName}"
146 def buildNumber = env.BUILD_NUMBER ?: '0'
147 def isMergeJob = env.JOB_NAME?.contains('merge')
148 // Remove promotion logic from this stage
149 def moduleTags = []
150 if (isMergeJob) {
caviedesj90b12042026-01-22 14:22:53 +0100151 moduleTags << "${baseTagPrefix}-merge"
caviedesj00417832026-01-09 11:34:42 +0100152 } else {
153 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
154 }
155
156 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
157 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
158
159 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
160 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
161 sh """
162 docker build -f Dockerfile.production -t ${primaryLocalImage} .
163 """
164 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
165 // Push build-scope tag(s) only. Promotion happens after tests.
166 moduleTags.each { tag ->
167 def localImage = "${imageName}:${tag}"
168 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
169 sh "docker tag ${localImage} ${remoteImage}"
170 sh "docker push ${remoteImage}"
171 }
172 // Stash the built image id for later promotion without rebuild
173 env.BUILT_IMAGE = primaryLocalImage
174 env.BUILT_TAG = moduleTags[0]
175 }
176 }
177 }
178 }
179
180 stage('E2E Test (robot)') {
181 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
182 steps {
183 script {
184 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
185 build job: dowstreamJob,
186 parameters: [
187 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
188 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
189 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
190 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
191 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'osmclient'),
192 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
193 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
194 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
195 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
196 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
197 ]
198 }
199 }
200 }
201 stage('Image Promotion') {
202 when { expression { return env.JOB_NAME?.contains('merge') } }
203 steps {
204 script {
205 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
206 def sanitizedBranchName = params.GERRIT_BRANCH
207 .toLowerCase()
208 .replaceAll('[^a-z0-9._-]', '-')
209 def baseTagPrefix = "osm-${sanitizedBranchName}"
210 def targetTag = "${baseTagPrefix}-merge"
211 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
212
213 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
214 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
215 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
216 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
217 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
218 sh "docker push ${remoteImage}"
219 }
220 }
221 }
222 }
223
224 /*
225 Promotion should be done in a separate downstream job after E2E success to avoid
226 */
227 }
228
229 post {
230 always {
231 // cleanWs()
232 deleteDir()
233 }
234 }
garciadeblas61f98692025-12-23 16:24:09 +0100235}
236