#!/bin/groovy
/*
 * SPDX-License-Identifier: LicenseRef-CSSL-1.0
 */

import groovy.transform.Field

@Field def branch = ''
@Field def gitLocalBranch = ''
@Field def downstreamResults = [:]

// Location of the executor node
def nodeExecutor = params.nodeExecutor

// Tags to shorten pipeline duration
def doBuild = true
def do4Gtest = false
def do5Gtest = false
def do5GUeTest = false

// list of failing stages
def failingStages = ""
def mrValidationWarning = ""

// Globally scoped status result variables for downstream jobs.
def ubuntuBuildStatus = ''
def ubuntuArmBuildStatus = ''
def ubuntuJetsonBuildStatus = ''
def rhelBuildStatus = ''
def armBuildStatus = ''

def physim5GStatus = ''
def physimGH5GStatus = ''
def channelSimStatus = ''
def vrtSim5GStatus = ''
def rfSim5GStatus = ''
def flexricRAN5GStatus = ''

def physim4GStatus = ''
def rfSim4GStatus = ''
def l2Sim4GStatus = ''
def lteTDDB200Status = ''
def lteFDDB200OAIUEStatus = ''
def lteFDDB200Status = ''
def lteTDD2x2N3xxStatus = ''

def nsaTDDB200Status = ''
def saTDDB200Status = ''
def phytestLDPCoffloadStatus = ''
def saAW2SStatus = ''
def cn5gCOTSUESanityCheck = ''
def saAERIALStatus = ''
def saMultiAntennaStatus = ''
def saFHI72Status = ''
def saFHI72MplaneStatus = ''
def saHandoverStatus = ''
def saAERIALOAIUEStatus = ''
def saOAIUEStatus = ''

pipeline {
  agent {
    label nodeExecutor
  }
  options {
    timestamps()
    ansiColor('xterm')
  }

  stages {
    stage ("Get Parameters") {
      steps {
        script {
          echo '\u2705 \u001B[32mGet Parameters\u001B[0m'
          // GIT_BRANCH is prefixed with "origin/" by Jenkins - stripped version for use in tags and params in case of PUSH event
          gitLocalBranch = env.GIT_BRANCH.replace('origin/', '')
          if (env.GITHUB_PR_NUMBER) {
            echo "PR number           :   ${env.GITHUB_PR_NUMBER}"
            echo "Source Repository   :   ${env.GITHUB_PR_URL}"
            echo "Source Branch       :   ${env.GITHUB_PR_SOURCE_BRANCH}"
            echo "Commit ID           :   ${env.GITHUB_PR_HEAD_SHA}"
            echo "Target Repo         :   ${env.GIT_URL}"
            echo "Target Branch       :   ${env.GITHUB_PR_TARGET_BRANCH}"
            echo "Label               :   ${env.GITHUB_PR_LABELS}"
            branch = "${env.GITHUB_PR_SOURCE_BRANCH.replace('/', '-').replace('+', '-')}-${env.GITHUB_PR_HEAD_SHA}"
          } else {
            echo "Branch              :   ${gitLocalBranch}"
            echo "Commit ID           :   ${env.GIT_COMMIT}"
            branch = "${gitLocalBranch}-${env.GIT_COMMIT}"
          }
          env.JOB_TIMESTAMP = sh(returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"').trim()
        }
      }
    }
    stage ("Verify Labels") {
      steps {
        echo '\u2705 \u001B[32mVerify Labels\u001B[0m'
        script {
          if (env.GITHUB_PR_NUMBER) {
            if (!(env.GITHUB_PR_LABELS)) {
              def gitBaseUrl = env.GIT_URL.trim().replace('.git', '')
              def message = "**CI Build:** [#${env.BUILD_NUMBER}](${BUILD_URL}) | Not performing CI due to the absence of one of the following mandatory labels:\n"
              message += "- ${gitBaseUrl}/labels/documentation (don't perform any stages)\n"
              message += "- ${gitBaseUrl}/labels/BUILD-ONLY (execute only build stages)\n"
              message += "- ${gitBaseUrl}/labels/4G-LTE (perform 4G tests)\n"
              message += "- ${gitBaseUrl}/labels/5G-NR (perform 5G tests)\n"
              message += "- ${gitBaseUrl}/labels/nrUE (perform only 5G-UE related tests including physims excluding LDPC tests)\n"
              message += "- ${gitBaseUrl}/labels/CI (perform both 4G and 5G tests)\n"
              githubPRComment comment: githubPRMessage(message)
              error('Not performing CI due to lack of mandatory labels')
            }
            if (env.GITHUB_PR_LABELS.contains('CI')) {
              do4Gtest = true
              do5Gtest = true
              do5GUeTest = true
            }
            if (env.GITHUB_PR_LABELS.contains('4G-LTE')) {
              do4Gtest = true
            }
            if (env.GITHUB_PR_LABELS.contains('5G-NR')) {
              do5Gtest = true
            }
            if (env.GITHUB_PR_LABELS.contains('nrUE')) {
              do5GUeTest = true
            }
            if (env.GITHUB_PR_LABELS.contains('documentation')) {
              doBuild = false
            }
          } else {
            // PUSH event
            do4Gtest = true
            do5Gtest = true
          }
          echo """
            CI decision summary:
            doBuild     = ${doBuild}
            do4Gtest    = ${do4Gtest}
            do5Gtest    = ${do5Gtest}
            do5GUeTest  = ${do5GUeTest}
          """
        }
      }
    }
    stage ("Verify Guidelines") {
      steps {
        echo "Git URL          is ${GIT_URL}"
        echo "Job Name         is ${JOB_NAME}"
        echo "Build Id         is ${BUILD_ID}"
        script {
          if (env.GITHUB_PR_NUMBER) {
            // Validate MR commits: checks for missing Signed-off-by and merge commits
            def mrValidationLog = "signedCommit_${env.BUILD_NUMBER}.log"
            def mrValidationExitCode = sh(
                script: "bash ./ci-scripts/pre-ci-check.sh -s ${env.GITHUB_PR_HEAD_SHA} -t origin/${env.GITHUB_PR_TARGET_BRANCH} > ${mrValidationLog} 2>&1",
                returnStatus: true
            )
            def mrValidationMessage = readFile(mrValidationLog).trim()
            sh "rm -f ${mrValidationLog}"
            if (mrValidationExitCode >= 1) {
              mrValidationWarning = mrValidationMessage
            }
            if (mrValidationExitCode >= 2) {
              error("${mrValidationMessage}")
            }
          } else {
            echo "Git Branch    is ${GIT_BRANCH}"
            echo "Git Commit    is ${GIT_COMMIT}"
          }
        }
      }
    }
    stage ("Internal-Repo-Push") {
      steps {
        script {
          triggerDownstreamJob ('RAN-Internal-Repo-Push')
        }
      }
      post {
        failure {
          script {
            currentBuild.result = 'FAILURE'
            failingStages += '\n * Internal-Repo-Push - merge validation failed (merge conflict, git operation failure, or internal CI error)'
          }
        }
      }
    }
    // Build Stages are Mandatory
    stage ("Image Building Processes") {
      when { expression {doBuild} }
      parallel {
        stage ("Ubuntu-Image-Builder") {
          steps {
            script {
              triggerDownstreamJob ('RAN-Ubuntu-Image-Builder')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                ubuntuBuildStatus = finalizeDownstreamJob('RAN-Ubuntu-Image-Builder')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += ubuntuBuildStatus
              }
            }
          }
        }
        stage ("Ubuntu-ARM-Image-Builder") {
          steps {
            script {
              triggerDownstreamJob ('RAN-Ubuntu-ARM-Image-Builder')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                ubuntuArmBuildStatus = finalizeDownstreamJob('RAN-Ubuntu-ARM-Image-Builder')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += ubuntuArmBuildStatus
              }
            }
          }
        }
        stage ("Ubuntu-Jetson-Image-Builder") {
          steps {
            script {
              triggerDownstreamJob ('RAN-Ubuntu-Jetson-Image-Builder')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                ubuntuJetsonBuildStatus = finalizeDownstreamJob('RAN-Ubuntu-Jetson-Image-Builder')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += ubuntuJetsonBuildStatus
              }
            }
          }
        }
        stage ("RHEL-Cluster-Image-Builder") {
          steps {
            script {
              triggerDownstreamJob ('RAN-RHEL-Cluster-Image-Builder')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                rhelBuildStatus = finalizeDownstreamJob('RAN-RHEL-Cluster-Image-Builder')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += rhelBuildStatus
              }
            }
          }
        }
        stage ("ARM-Cross-Compile") {
          steps {
            script {
              triggerDownstreamJob ('RAN-ARM-Cross-Compile-Builder')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                armBuildStatus = finalizeDownstreamJob('RAN-ARM-Cross-Compile-Builder')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += armBuildStatus
              }
            }
          }
        }
      }
    }
    stage ("DockerHub-Push") {
      when { expression {doBuild && !(env.GITHUB_PR_NUMBER)} }
      steps {
        script {
          catchError(buildResult: 'FAILURE', stageResult: 'FAILURE') {
            triggerDownstreamJob ('RAN-DockerHub-Push')
          }
        }
      }
      post {
        failure {
          script {
            echo "Push to Docker-Hub KO"
            failingStages += '\n * RAN-DockerHub-Push'
          }
        }
      }
    }
    stage ("Image Test Processes") {
      when { expression {doBuild} }
      parallel {
        stage ("PhySim-Cluster-5G") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-PhySim-Cluster-5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                physim5GStatus = finalizeDownstreamJob('RAN-PhySim-Cluster-5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += physim5GStatus
              }
            }
          }
        }
        stage ("PhySim-GraceHopper-5G") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-PhySim-GraceHopper-5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                physimGH5GStatus = finalizeDownstreamJob('RAN-PhySim-GraceHopper-5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += physimGH5GStatus
              }
            }
          }
        }
        stage ("Channel-Simulation") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-Channel-Simulation')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                channelSimStatus = finalizeDownstreamJob('RAN-Channel-Simulation')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += channelSimStatus
              }
            }
          }
        }
        stage ("PhySim-Cluster-4G") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-PhySim-Cluster-4G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                physim4GStatus = finalizeDownstreamJob('RAN-PhySim-Cluster-4G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += physim4GStatus
              }
            }
          }
        }
        stage ("RF-Sim-Test-4G") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-RF-Sim-Test-4G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                rfSim4GStatus = finalizeDownstreamJob('RAN-RF-Sim-Test-4G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += rfSim4GStatus
              }
            }
          }
        }
        stage ("VRT-Sim-Test-5G") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-VRT-Sim-Test-5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                vrtSim5GStatus = finalizeDownstreamJob('RAN-VRT-Sim-Test-5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += vrtSim5GStatus
              }
            }
          }
        }
        stage ("RF-Sim-Test-5G") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-RF-Sim-Test-5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                rfSim5GStatus = finalizeDownstreamJob('RAN-RF-Sim-Test-5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += rfSim5GStatus
              }
            }
          }
        }
        stage ("OAI-FLEXRIC-RAN-Integration-Test") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('OAI-FLEXRIC-RAN-Integration-Test')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                flexricRAN5GStatus = finalizeDownstreamJob('OAI-FLEXRIC-RAN-Integration-Test')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += flexricRAN5GStatus
              }
            }
          }
        }
        stage ("L2-Sim-Test-4G") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-L2-Sim-Test-4G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                l2Sim4GStatus = finalizeDownstreamJob('RAN-L2-Sim-Test-4G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += l2Sim4GStatus
              }
            }
          }
        }
        stage ("LTE-B200-FDD-LTEBOX-Container") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-LTE-FDD-LTEBOX-Container')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                lteFDDB200Status = finalizeDownstreamJob('RAN-LTE-FDD-LTEBOX-Container')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += lteFDDB200Status
              }
            }
          }
        }
        // Pipeline to test OAI LTE-UE
        stage ("LTE-B200-FDD-OAIUE-OAICN4G-Container") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-LTE-FDD-OAIUE-OAICN4G-Container')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                lteFDDB200OAIUEStatus = finalizeDownstreamJob('RAN-LTE-FDD-OAIUE-OAICN4G-Container')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += lteFDDB200OAIUEStatus
              }
            }
          }
        }
        stage ("LTE-B200-TDD-LTEBOX-Container") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-LTE-TDD-LTEBOX-Container')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                lteTDDB200Status = finalizeDownstreamJob('RAN-LTE-TDD-LTEBOX-Container')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += lteTDDB200Status
              }
            }
          }
        }
        stage ("NSA-B200-Module-LTEBOX-Container") {
          when { expression {do4Gtest || do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-NSA-B200-Module-LTEBOX-Container')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                nsaTDDB200Status = finalizeDownstreamJob('RAN-NSA-B200-Module-LTEBOX-Container')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += nsaTDDB200Status
              }
            }
          }
        }
        stage ("SA-B200-Module-SABOX-Container") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-B200-Module-SABOX-Container')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saTDDB200Status = finalizeDownstreamJob('RAN-SA-B200-Module-SABOX-Container')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saTDDB200Status
              }
            }
          }
        }
        stage ("gNB-N300-Timing-Phytest-LDPC") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-gNB-N300-Timing-Phytest-LDPC')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                phytestLDPCoffloadStatus = finalizeDownstreamJob('RAN-gNB-N300-Timing-Phytest-LDPC')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += phytestLDPCoffloadStatus
              }
            }
          }
        }
        stage ("LTE-TDD-2x2-Container") {
          when { expression {do4Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-LTE-TDD-2x2-Container')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                lteTDD2x2N3xxStatus = finalizeDownstreamJob('RAN-LTE-TDD-2x2-Container')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += lteTDD2x2N3xxStatus
              }
            }
          }
        }
        stage ("SA-AW2S-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-AW2S-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saAW2SStatus = finalizeDownstreamJob('RAN-SA-AW2S-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saAW2SStatus
              }
            }
          }
        }
        stage ("Sanity-Check OAI-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerCN5GDownstreamJob ('OAI-CN5G-COTS-UE-Test')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                cn5gCOTSUESanityCheck = finalizeDownstreamJob('OAI-CN5G-COTS-UE-Test')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += cn5gCOTSUESanityCheck
              }
            }
          }
        }
        stage ("SA-AERIAL-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-AERIAL-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saAERIALStatus = finalizeDownstreamJob('RAN-SA-AERIAL-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saAERIALStatus
              }
            }
          }
        }
        stage ("SA-Multi-Antenna-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-Multi-Antenna-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saMultiAntennaStatus = finalizeDownstreamJob('RAN-SA-Multi-Antenna-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saMultiAntennaStatus
              }
            }
          }
        }
        stage ("SA-FHI72-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-FHI72-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saFHI72Status = finalizeDownstreamJob('RAN-SA-FHI72-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saFHI72Status
              }
            }
          }
        }
        stage ("SA-FHI72-MPLANE-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-FHI72-MPLANE-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saFHI72MplaneStatus = finalizeDownstreamJob('RAN-SA-FHI72-MPLANE-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saFHI72MplaneStatus
              }
            }
          }
        }
        stage ("SA-Handover-CN5G") {
          when { expression {do5Gtest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-Handover-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saHandoverStatus = finalizeDownstreamJob('RAN-SA-Handover-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saHandoverStatus
              }
            }
          }
        }
        stage ("SA-AERIAL-OAIUE-CN5G") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-AERIAL-OAIUE-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saAERIALOAIUEStatus = finalizeDownstreamJob('RAN-SA-AERIAL-OAIUE-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saAERIALOAIUEStatus
              }
            }
          }
        }
        stage ("SA-OAIUE-CN5G") {
          when { expression {do5Gtest || do5GUeTest} }
          steps {
            script {
              triggerDownstreamJob ('RAN-SA-OAIUE-CN5G')
            }
          }
          post {
            always {
              script {
                // Using a unique variable name for each test stage to avoid overwriting on a global variable
                // due to parallel-time concurrency
                saOAIUEStatus = finalizeDownstreamJob('RAN-SA-OAIUE-CN5G')
              }
            }
            failure {
              script {
                currentBuild.result = 'FAILURE'
                failingStages += saOAIUEStatus
              }
            }
          }
        }
      }
    }
  }
  post {
    success {
      script {
        if (env.GITHUB_PR_NUMBER) {
          if (mrValidationWarning) {
            def message = "**CI Build:** [#${env.BUILD_NUMBER}](${BUILD_URL}) | **Passed**"
            message +=  "\n\n**Validation:** " + mrValidationWarning
            githubPRComment comment: githubPRMessage(message)
          }
          githubNotify account: params.gitAccount,
                       repo: params.gitRepo,
                       credentialsId: 'github-jenkins-duranta',
                       sha: env.GITHUB_PR_HEAD_SHA,
                       status: 'SUCCESS',
                       description: 'Build success',
                       targetUrl: BUILD_URL,
                       context: 'Jenkins Duranta CI'
        }
        echo "Pipeline is SUCCESSFUL"
      }
    }
    failure {
      script {
        if (env.GITHUB_PR_NUMBER) {
          def message = "**CI Build:** [#${env.BUILD_NUMBER}](${BUILD_URL}) | **Failed** on the following stages:"
          if (failingStages) {
            message += failingStages
          } else {
            message += "\nThe pipeline may have failed due to CI, validation, or scripting error. See the build for more details."
          }
          if (mrValidationWarning) {
            message += '\n\n**Validation:** ' + mrValidationWarning
          }
          if (env.GITHUB_PR_LABELS) {
            githubPRComment comment: githubPRMessage(message)
          }
          githubNotify account: params.gitAccount,
                       repo: params.gitRepo,
                       credentialsId: 'github-jenkins-duranta',
                       sha: env.GITHUB_PR_HEAD_SHA,
                       status: 'FAILURE',
                       description: 'Build failed',
                       targetUrl: BUILD_URL,
                       context: 'Jenkins Duranta CI'
        }
        echo "Pipeline FAILED"
      }
    }
  }
}

// ----  Slave Job functions

def triggerDownstreamJob (jobName) {
  // Workaround for the "cancelled" pipeline notification
  // The downstream job is triggered with the propagate false so the following commands are executed
  // Its status is now PASS/SUCCESS from a stage pipeline point of view
  // localStatus variable MUST be analyzed to properly assess the status
  def localStatus = build job: jobName,
    parameters: [
      string(name: 'targetRepo', value: String.valueOf(env.GIT_URL)),
      string(name: 'sourceRepo', value: String.valueOf(env.GITHUB_PR_URL ?: env.GIT_URL)),
      string(name: 'sourceBranch', value: String.valueOf(env.GITHUB_PR_SOURCE_BRANCH ?: gitLocalBranch)),
      string(name: 'sourceCommit', value: String.valueOf(env.GITHUB_PR_HEAD_SHA ?: env.GIT_COMMIT)),
      string(name: 'requestNumber', value: String.valueOf(env.GITHUB_PR_NUMBER ?: gitLocalBranch)),
      booleanParam(name: 'mergeWithTarget', value: env.GITHUB_PR_NUMBER != null),
      string(name: 'targetBranch', value: (env.GITHUB_PR_TARGET_BRANCH ?: gitLocalBranch)),
      string(name: 'repository', value: String.valueOf(params.ciRepositoryURL)),
      string(name: 'branch', value: String.valueOf(branch))
    ], propagate: false
  def localResult = localStatus.getResult()
  echo "${jobName} Slave Job status is ${localResult}"
  downstreamResults[jobName] = localStatus.resultIsBetterOrEqualTo('SUCCESS') ? 'SUCCESS' : 'FAILURE'
  if (localStatus.resultIsBetterOrEqualTo('SUCCESS')) {
    echo "${jobName} Slave Job is OK"
  } else {
    error "${jobName} Slave Job is KO"
  }
}

def triggerCN5GDownstreamJob (jobName) {
  def fullRanTag = params.internalRegistry + '/oai-gnb:' + "${branch}"
  // Workaround for the "cancelled" GitLab pipeline notification
  // The downstream job is triggered with the propagate false so the following commands are executed
  // Its status is now PASS/SUCCESS from a stage pipeline point of view
  // localStatus variable MUST be analyzed to properly assess the status
  def localStatus = build job: jobName,
    parameters: [
      string(name: 'FULL_RAN_TAG', value: String.valueOf(fullRanTag))
    ], propagate: false
  def localResult = localStatus.getResult()
  echo "${jobName} Slave Job status is ${localResult}"
  downstreamResults[jobName] = localStatus.resultIsBetterOrEqualTo('SUCCESS') ? 'SUCCESS' : 'FAILURE'
  if (localStatus.resultIsBetterOrEqualTo('SUCCESS')) {
    echo "${jobName} Slave Job is OK"
  } else {
    error "${jobName} Slave Job is KO"
  }
}

def finalizeDownstreamJob(jobName) {
  lock ('Parent-Lock') {
    // In case of any non-success, we are retrieving the HTML report of the last completed
    // downstream job. The only drop-back is that we may retrieve the HTML report of a previous build
    def fileName
    if (jobName == 'OAI-CN5G-COTS-UE-Test') {
      fileName = "test_results_oai_cn5g_cots_ue.html"
    } else {
      fileName = "test_results-${jobName}.html"
    }
    def artifactUrl = BUILD_URL
    if (!fileExists(fileName)) {
      copyArtifacts(projectName: jobName,
        filter: 'test_results*.html',
        selector: lastCompleted(),
        optional: true)
      if (fileExists(fileName)) {
        sh "sed -i -e 's#TEMPLATE_BUILD_TIME#${env.JOB_TIMESTAMP}#' ${fileName}"
      } else {
        sh "echo \"could not copy results from ${jobName}, please check pipeline ${BUILD_URL}\" > ${fileName}"
      }
      archiveArtifacts artifacts: fileName
      // BUILD_URL is like  http://server:port/jenkins/job/foo/15/
      // no need to add a prefixed '/'
      artifactUrl += 'artifact/' + fileName
    }
    if (env.GITHUB_PR_NUMBER) {
      githubNotify account: params.gitAccount,
                   repo: params.gitRepo,
                   credentialsId: "github-jenkins-duranta",
                   sha: env.GITHUB_PR_HEAD_SHA,
                   status: downstreamResults.getOrDefault(jobName, 'FAILURE'),
                   targetUrl: "${artifactUrl}",
                   context: jobName
    }
    artifactUrl = "\n * [${jobName}](${artifactUrl})"
    return artifactUrl
  }
}
