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