blob: bd0abfc3ad02e6f6163acb40ad8818d1ac53e5d2 [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()
caviedesjd9443ef2026-02-12 17:29:06 +010050 TEST_IMAGE = "opensourcemano/tox-osm:19.0"
caviedesjc7013bf2026-01-09 10:48:20 +010051 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 """
caviedesj0be6e122026-02-26 12:52:10 +0100119 if (fileExists('coverage.xml')) {
120 recordCoverage tools: [[
121 parser: 'COBERTURA',
122 pattern: 'coverage.xml'
123 ]], sourceDirectories: []
124 }
125
126 if (fileExists('nosetests.xml')) {
127 junit 'nosetests.xml'
128 }
caviedesjc7013bf2026-01-09 10:48:20 +0100129 }
130
131 stage('Changelog') {
132 sh 'mkdir -p changelog'
133 sh """
134 docker run --rm ${common} \
135 ${env.TEST_IMAGE} \
garciadeblasac2becc2026-02-14 11:12:19 +0100136 sh -c 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html'
caviedesjc7013bf2026-01-09 10:48:20 +0100137 """
138 }
139 }
140 }
141 }
142
143 stage('Build & Push Image') {
144 when { expression { return params.DO_DOCKERPUSH } }
145 steps {
146 script {
147 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
148
149 if (!params.GERRIT_BRANCH) {
150 error 'GERRIT_BRANCH is required to tag the Docker image'
151 }
152 def sanitizedBranchName = params.GERRIT_BRANCH
153 .toLowerCase()
154 .replaceAll('[^a-z0-9._-]', '-')
155 def baseTagPrefix = "osm-${sanitizedBranchName}"
156 def buildNumber = env.BUILD_NUMBER ?: '0'
157 def isMergeJob = env.JOB_NAME?.contains('merge')
158 // Remove promotion logic from this stage
159 def moduleTags = []
160 if (isMergeJob) {
caviedesjc878b7c2026-01-22 14:22:17 +0100161 moduleTags << "${baseTagPrefix}-merge"
caviedesjc7013bf2026-01-09 10:48:20 +0100162 } else {
163 moduleTags << "${baseTagPrefix}-patchset-${buildNumber}"
164 }
165
166 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
167 def primaryLocalImage = "${imageName}:${moduleTags[0]}"
168
169 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
170 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
171 sh """
172 docker build -f airflow/Dockerfile.production -t ${primaryLocalImage} .
173 """
174 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
175 // Push build-scope tag(s) only. Promotion happens after tests.
176 moduleTags.each { tag ->
177 def localImage = "${imageName}:${tag}"
178 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}"
179 sh "docker tag ${localImage} ${remoteImage}"
180 sh "docker push ${remoteImage}"
181 }
182 // Stash the built image id for later promotion without rebuild
183 env.BUILT_IMAGE = primaryLocalImage
184 env.BUILT_TAG = moduleTags[0]
185 }
186 }
187 }
188 }
189
190 stage('E2E Test (robot)') {
191 when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } }
192 steps {
193 script {
194 def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}"
195 build job: dowstreamJob,
196 parameters: [
197 string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'),
198 string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''),
199 string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE),
200 string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR),
201 string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'NG-SA'),
202 string(name: 'CONTAINER_NAME', value: env.BUILT_TAG),
203 booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT),
204 booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL),
205 booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL),
206 booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS)
207 ]
208 }
209 }
210 }
211 stage('Image Promotion') {
212 when { expression { return env.JOB_NAME?.contains('merge') } }
213 steps {
214 script {
215 def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase()
216 def sanitizedBranchName = params.GERRIT_BRANCH
217 .toLowerCase()
218 .replaceAll('[^a-z0-9._-]', '-')
219 def baseTagPrefix = "osm-${sanitizedBranchName}"
220 def targetTag = "${baseTagPrefix}-merge"
221 def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}"
222
223 withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry',
224 usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) {
225 sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}"
226 def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}"
227 sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}"
228 sh "docker push ${remoteImage}"
229 }
230 }
231 }
232 }
233
234 /*
235 Promotion should be done in a separate downstream job after E2E success to avoid
236 */
237 }
238
239 post {
240 always {
241 // cleanWs()
242 deleteDir()
243 }
244 }
garciadeblasac2becc2026-02-14 11:12:19 +0100245}