| /* |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY, either express or |
| implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| */ |
| |
| def DEFAULT_MODULE_NAME = 'nbi' |
| |
| pipeline { |
| agent { label 'pool' } |
| options { disableConcurrentBuilds() } |
| parameters { |
| // Core Gerrit / multibranch inputs |
| string(name: 'GERRIT_BRANCH', defaultValue: env.BRANCH_NAME ?: 'master', description: '') |
| string(name: 'GERRIT_PROJECT', defaultValue: 'osm/NBI', description: '') |
| string(name: 'GERRIT_REFSPEC', defaultValue: env.GERRIT_REFSPEC ?: '', description: '') |
| string(name: 'GERRIT_PATCHSET_REVISION', defaultValue: env.GERRIT_PATCHSET_REVISION ?: '', description: '') |
| string(name: 'PROJECT_URL_PREFIX', defaultValue: 'https://osm.etsi.org/gerrit', description: '') |
| string(name: 'DOCKER_ARGS', defaultValue: '', description: 'Extra docker args for docker run') |
| |
| // OpenStack configuration |
| string(name: 'OPENSTACK_BASE_IMAGE', defaultValue: 'ubuntu24.04', description: '') |
| string(name: 'OPENSTACK_OSM_FLAVOR', defaultValue: 'osm.sanity', description: '') |
| |
| // Stage toggles |
| booleanParam(name: 'DO_INSTALL', defaultValue: true, description: '') |
| booleanParam(name: 'DO_DOCKERPUSH', defaultValue: true, description: '') |
| booleanParam(name: 'DO_ROBOT', defaultValue: true, description: '') |
| |
| // Module configuration |
| string(name: 'MODULE_NAME', defaultValue: 'NBI', description: 'Name of the module under test') |
| |
| // Container management |
| booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', defaultValue: false, description: '') |
| booleanParam(name: 'SAVE_CONTAINER_ON_PASS', defaultValue: false, description: '') |
| |
| // Image configuration |
| string(name: 'IMAGENAME', defaultValue: 'opensourcemano/nbi', description: 'Image name for publish') |
| } |
| environment { |
| MDG = "${params.GERRIT_PROJECT?.contains('/') ? params.GERRIT_PROJECT.split('/')[1] : params.GERRIT_PROJECT}" |
| CONTAINER_NAME = "${params.GERRIT_PROJECT}-${params.GERRIT_BRANCH}".toLowerCase() |
| TEST_IMAGE = 'opensourcemano/tox-osm:19.0' |
| DOCKER_REGISTRY = 'osm.etsi.org:5050/devops/cicd/' |
| } |
| stages { |
| stage('Prepare') { steps { sh 'env' } } |
| |
| stage('Checkout') { |
| steps { |
| checkout scm |
| script { |
| sh "git fetch --tags" |
| if (params.GERRIT_REFSPEC?.trim()) { sh "git fetch origin ${params.GERRIT_REFSPEC}" } |
| if (params.GERRIT_PATCHSET_REVISION?.trim()) { sh "git checkout -f ${params.GERRIT_PATCHSET_REVISION}" } |
| sh "sudo git clean -dfx || git clean -dfx" |
| } |
| } |
| } |
| |
| stage('Clone devops (central)') { |
| steps { |
| dir('devops') { |
| sh "git init ." |
| sh "git remote remove origin || true" |
| sh "git remote add origin ${params.PROJECT_URL_PREFIX}/osm/devops" |
| sh "git fetch --depth=1 origin ${params.GERRIT_BRANCH}" |
| sh "git checkout -f FETCH_HEAD" |
| } |
| } |
| } |
| |
| stage('License Scan') { |
| steps { |
| script { |
| def isMergeJob = env.JOB_NAME?.contains('merge') |
| if (!isMergeJob) { sh 'devops/tools/license_scan.sh' } else { echo 'skip the scan for merge' } |
| } |
| } |
| } |
| |
| stage('Prepare Test Image') { |
| steps { |
| script { |
| // Use shared test image from registry; no local build needed |
| sh "docker pull ${env.TEST_IMAGE} || true" |
| } |
| } |
| } |
| |
| stage('Tests') { |
| steps { |
| script { |
| def UID = sh(returnStdout: true, script: 'id -u').trim() |
| def GID = sh(returnStdout: true, script: 'id -g').trim() |
| def common = "-v ${env.WORKSPACE}:/tests -e UID=${UID} -e GID=${GID} " + (params.DOCKER_ARGS ?: '') |
| |
| stage('Linting Tests') { |
| sh """ |
| docker run --rm ${common} \ |
| ${env.TEST_IMAGE} \ |
| /tests/devops-stages/stage-lint.sh |
| """ |
| } |
| |
| stage('Unit Tests') { |
| sh """ |
| docker run --rm ${common} \ |
| ${env.TEST_IMAGE} \ |
| /tests/devops-stages/stage-test.sh |
| """ |
| if (fileExists('coverage.xml')) { |
| recordCoverage tools: [[ |
| parser: 'COBERTURA', |
| pattern: 'coverage.xml' |
| ]], sourceDirectories: [] |
| } |
| |
| if (fileExists('nosetests.xml')) { |
| junit 'nosetests.xml' |
| } |
| } |
| |
| stage('Changelog') { |
| sh 'mkdir -p changelog' |
| sh """ |
| docker run --rm ${common} \ |
| ${env.TEST_IMAGE} \ |
| sh -c 'devops/tools/generatechangelog-pipeline.sh > /tests/changelog/changelog-${MDG}.html' |
| """ |
| } |
| } |
| } |
| } |
| |
| stage('Build & Push Image') { |
| when { expression { return params.DO_DOCKERPUSH } } |
| steps { |
| script { |
| def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase() |
| |
| if (!params.GERRIT_BRANCH) { |
| error 'GERRIT_BRANCH is required to tag the Docker image' |
| } |
| def sanitizedBranchName = params.GERRIT_BRANCH |
| .toLowerCase() |
| .replaceAll('[^a-z0-9._-]', '-') |
| def baseTagPrefix = "osm-${sanitizedBranchName}" |
| def buildNumber = env.BUILD_NUMBER ?: '0' |
| def isMergeJob = env.JOB_NAME?.contains('merge') |
| // Remove promotion logic from this stage |
| def moduleTags = [] |
| if (isMergeJob) { |
| moduleTags << "${baseTagPrefix}-merge" |
| } else { |
| moduleTags << "${baseTagPrefix}-patchset-${buildNumber}" |
| } |
| |
| def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}" |
| def primaryLocalImage = "${imageName}:${moduleTags[0]}" |
| |
| withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry', |
| usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { |
| sh """ |
| docker build -f Dockerfile.production -t ${primaryLocalImage} . |
| """ |
| sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}" |
| // Push build-scope tag(s) only. Promotion happens after tests. |
| moduleTags.each { tag -> |
| def localImage = "${imageName}:${tag}" |
| def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${tag}" |
| sh "docker tag ${localImage} ${remoteImage}" |
| sh "docker push ${remoteImage}" |
| } |
| // Stash the built image id for later promotion without rebuild |
| env.BUILT_IMAGE = primaryLocalImage |
| env.BUILT_TAG = moduleTags[0] |
| } |
| } |
| } |
| } |
| |
| stage('E2E Test (robot)') { |
| when { expression { return params.DO_ROBOT && !env.JOB_NAME.contains('merge') } } |
| steps { |
| script { |
| def dowstreamJob = "osm-e2e/${params.GERRIT_BRANCH ?: 'master'}" |
| build job: dowstreamJob, |
| parameters: [ |
| string(name: 'GERRIT_BRANCH', value: params.GERRIT_BRANCH ?: 'master'), |
| string(name: 'GERRIT_REFSPEC', value: params.GERRIT_REFSPEC ?: ''), |
| string(name: 'OPENSTACK_BASE_IMAGE', value: params.OPENSTACK_BASE_IMAGE), |
| string(name: 'OPENSTACK_OSM_FLAVOR', value: params.OPENSTACK_OSM_FLAVOR), |
| string(name: 'MODULE_NAME', value: params.MODULE_NAME ?: 'NBI'), |
| string(name: 'CONTAINER_NAME', value: env.BUILT_TAG), |
| booleanParam(name: 'DO_ROBOT', value: params.DO_ROBOT), |
| booleanParam(name: 'DO_INSTALL', value: params.DO_INSTALL), |
| booleanParam(name: 'SAVE_CONTAINER_ON_FAIL', value: params.SAVE_CONTAINER_ON_FAIL), |
| booleanParam(name: 'SAVE_CONTAINER_ON_PASS', value: params.SAVE_CONTAINER_ON_PASS) |
| ] |
| } |
| } |
| } |
| stage('Image Promotion') { |
| when { expression { return env.JOB_NAME?.contains('merge') } } |
| steps { |
| script { |
| def moduleName = (env.MDG ?: DEFAULT_MODULE_NAME).toLowerCase() |
| def sanitizedBranchName = params.GERRIT_BRANCH |
| .toLowerCase() |
| .replaceAll('[^a-z0-9._-]', '-') |
| def baseTagPrefix = "osm-${sanitizedBranchName}" |
| def targetTag = "${baseTagPrefix}-merge" |
| def imageName = params.IMAGENAME ?: "opensourcemano/${moduleName}" |
| |
| withCredentials([[$class: 'UsernamePasswordMultiBinding', credentialsId: 'gitlab-registry', |
| usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD']]) { |
| sh "docker login ${env.DOCKER_REGISTRY.split('/')[0]} -u ${USERNAME} -p ${PASSWORD}" |
| def remoteImage = "${env.DOCKER_REGISTRY}${imageName}:${targetTag}" |
| sh "docker tag ${env.BUILT_IMAGE} ${remoteImage}" |
| sh "docker push ${remoteImage}" |
| } |
| } |
| } |
| } |
| |
| /* |
| Promotion should be done in a separate downstream job after E2E success to avoid |
| */ |
| } |
| |
| post { |
| always { |
| // cleanWs() |
| deleteDir() |
| } |
| } |
| } |