#!/bin/groovy
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (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.openairinterface.org/?page_id=698
 *
 * 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 KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

// Abstraction function to send social media messages:
// like on Slack or Mattermost
def sendSocialMediaMessage(pipeChannel, pipeColor, pipeMessage) {
    if (params.pipelineUsesSlack != null) {
        if (params.pipelineUsesSlack) {
            slackSend channel: pipeChannel, color: pipeColor, message: pipeMessage
        }
    }
}

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

// VM Lockable resources
def vmResource = params.vmLockableResource

// Tags to shorten pipeline duration
def doMandatoryTests = false
def doFullTestsuite = false

pipeline {
    agent {
        label nodeExecutor
    }
    options {
        disableConcurrentBuilds()
        timestamps()
        gitLabConnection('OAI GitLab')
        ansiColor('xterm')
    }

    stages {
        stage ("Verify Parameters") {
            steps {
                script {
                    JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"'
                    JOB_TIMESTAMP = JOB_TIMESTAMP.trim()

                    echo '\u2705 \u001B[32mVerify Parameters\u001B[0m'
                    def allParametersPresent = true
                    if (env.TESTPLATFORM_OWNER) {
                        echo "Platform is ${env.TESTPLATFORM_OWNER}"
                    }

                    if (fileExists("flexran")) {
                        sh "rm -Rf flexran > /dev/null 2>&1"
                    }

                    echo '\u2705 \u001B[32mVerify Labels\u001B[0m'
                    if ("MERGE".equals(env.gitlabActionType)) {
                        LABEL_CHECK = sh returnStdout: true, script: 'ci-scripts/checkGitLabMergeRequestLabels.sh --mr-id ' + env.gitlabMergeRequestIid
                        LABEL_CHECK = LABEL_CHECK.trim()
                        if (LABEL_CHECK == 'NONE') {
                            def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Your merge request has none of the mandatory labels:\n\n"
                            message += " - BUILD-ONLY\n"
                            message += " - 4G-LTE\n"
                            message += " - 5G-NR\n"
                            message += " - CI\n\n"
                            message += "Not performing CI due to lack of labels"
                            addGitLabMRComment comment: message
                            error('Not performing CI due to lack of labels')
                        } else if (LABEL_CHECK == 'FULL' || LABEL_CHECK == 'SHORTEN-4G') {
                            doMandatoryTests = true
                            doFullTestsuite = true
                        } else if (LABEL_CHECK == 'SHORTEN-5G') {
                            doMandatoryTests = true
                        } else {
                            def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): We will perform only build stages on your Merge Request"
                            addGitLabMRComment comment: message
                        }
                    } else {
                        doMandatoryTests = true
                        doFullTestsuite = true
                    }
                }
            }
        }
        stage ("Verify Guidelines") {
            steps {
                echo "Git URL         is ${GIT_URL}"
                echo "GitLab Act      is ${env.gitlabActionType}"
                script {
                    if ("MERGE".equals(env.gitlabActionType)) {
                        // since a bit, in push events, gitlabUserEmail is not populated
                        gitCommitAuthorEmailAddr = env.gitlabUserEmail
                        echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
                        // GitLab-Jenkins plugin integration is lacking to perform the merge by itself
                        // Doing it manually --> it may have merge conflicts
                        sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
                        sh "zip -r -qq localZip.zip ."

                        // here was an astyle formatting check, with corresponding messages. The same could be done with clang-format
                    } else {
                        echo "Git Branch      is ${GIT_BRANCH}"
                        echo "Git Commit      is ${GIT_COMMIT}"
                        // since a bit, in push events, gitlabUserEmail is not populated
                        gitCommitAuthorEmailAddr = sh returnStdout: true, script: 'git log -n1 --pretty=format:%ae ${GIT_COMMIT}'
                        gitCommitAuthorEmailAddr = gitCommitAuthorEmailAddr.trim()
                        echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
                        sh "git log -n1 --pretty=format:\"%s\" > .git/CI_COMMIT_MSG"

                        sh "zip -r -qq localZip.zip ."
                    }
                }
            }
            post {
                failure {
                    script {
                        def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Merge Conflicts -- Cannot perform CI"
                        addGitLabMRComment comment: message
                        currentBuild.result = 'FAILURE'
                    }
                }
            }
        }

        stage ("Start VM -- gnb-usrp") {
            steps {
              lock (vmResource) {
                timeout (time: 5, unit: 'MINUTES') {
                    sh "./ci-scripts/oai-ci-vm-tool build --workspace $WORKSPACE --variant gnb-usrp --job-name ${JOB_NAME} --build-id ${BUILD_ID} --daemon"
                }
              }
            }
        }

        stage ("Start VM -- nr-ue-usrp") {
            steps {
              lock (vmResource) {
                timeout (time: 5, unit: 'MINUTES') {
                    sh "./ci-scripts/oai-ci-vm-tool build --workspace $WORKSPACE --variant nr-ue-usrp --job-name ${JOB_NAME} --build-id ${BUILD_ID} --daemon"
                }
              }
            }
        }

        stage ("Start VM -- enb-ethernet") {
            steps {
              lock (vmResource) {
                timeout (time: 7, unit: 'MINUTES') {
                    sh "./ci-scripts/oai-ci-vm-tool build --workspace $WORKSPACE --variant enb-ethernet --job-name ${JOB_NAME} --build-id ${BUILD_ID} --daemon"
                }
              }
            }
        }

        stage ("Start VM -- ue-ethernet") {
            steps {
              lock (vmResource) {
                timeout (time: 7, unit: 'MINUTES') {
                    sh "./ci-scripts/oai-ci-vm-tool build --workspace $WORKSPACE --variant ue-ethernet --job-name ${JOB_NAME} --build-id ${BUILD_ID} --daemon"
                }
              }
            }
        }
        
        stage ("Variant Builds") {
            parallel {
                stage ("Build 5G gNB-USRP") {
                    steps {
                        gitlabCommitStatus(name: "Build gNB-USRP") {
                            timeout (time: 45, unit: 'MINUTES') {
                                sh "./ci-scripts/oai-ci-vm-tool wait --workspace $WORKSPACE --variant gnb-usrp --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                            }
                        }
                    }
                }
                stage ("Build 5G NR-UE-USRP") {
                    steps {
                        gitlabCommitStatus(name: "Build nr-UE-USRP") {
                            timeout (time: 45, unit: 'MINUTES') {
                                sh "./ci-scripts/oai-ci-vm-tool wait --workspace $WORKSPACE --variant nr-ue-usrp --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                            }
                        }
                    }
                }
                stage ("Build eNB-ethernet") {
                    steps {
                        gitlabCommitStatus(name: "Build eNB-ethernet") {
                            timeout (time: 45, unit: 'MINUTES') {
                                sh "./ci-scripts/oai-ci-vm-tool wait --workspace $WORKSPACE --variant enb-ethernet --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                            }
                        }
                    }
                }
                stage ("Build UE-ethernet") {
                    steps {
                      // This is typically the last one to finish.
                      lock (vmResource) {
                        gitlabCommitStatus(name: "Build UE-ethernet") {
                            timeout (time: 45, unit: 'MINUTES') {
                                sh "./ci-scripts/oai-ci-vm-tool wait --workspace $WORKSPACE --variant ue-ethernet --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                            }
                        }
                      }
                    }
                }
            }
            post {
                failure {
                    script {
                        currentBuild.result = 'FAILURE'
                    }
                }
                always {
                    script {
                        dir ('archives') {
                            if (fileExists('red_hat')) {
                                sh "zip -r -qq vm_build_logs.zip enb_eth ue_eth gnb_usrp nr_ue_usrp red_hat"
                            } else {
                                sh "zip -r -qq vm_build_logs.zip enb_eth ue_eth gnb_usrp nr_ue_usrp"
                            }
                        }
                        if(fileExists('archives/vm_build_logs.zip')) {
                            archiveArtifacts artifacts: 'archives/vm_build_logs.zip'
                        }
                        if ("MERGE".equals(env.gitlabActionType)) {
                            sh "./ci-scripts/oai-ci-vm-tool report-build --workspace $WORKSPACE --git-url ${GIT_URL} --job-name ${JOB_NAME} --build-id ${BUILD_ID} --trigger merge-request --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
                            // If the merge request has introduced compilation warnings, notifications in GitLab
                            sh "./ci-scripts/checkAddedWarnings.sh --src-branch ${env.gitlabSourceBranch} --target-branch ${env.gitlabTargetBranch}"
                            def res=readFile('./oai_warning_files.txt').trim();
                            if ("0".equals(res)) {
                                echo "No issues w/ warnings/errors in this merge request"
                            } else {
                                def fileList=readFile('./oai_warning_files_list.txt').trim();
                                def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): Some modified files in Merge Request MAY have INTRODUCED WARNINGS (" + fileList + ")"
                                addGitLabMRComment comment: message
                            }
                        } else {
                            sh "./ci-scripts/oai-ci-vm-tool report-build --workspace $WORKSPACE --git-url ${GIT_URL} --job-name ${JOB_NAME} --build-id ${BUILD_ID} --trigger push --branch ${GIT_BRANCH} --commit ${GIT_COMMIT}"
                        }
                        if(fileExists('build_results.html')) {
                            sh "sed -i -e 's#Build-ID: ${BUILD_ID}#Build-ID: <a href=\"${BUILD_URL}\">${BUILD_ID}</a>#' -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' build_results.html"
                            archiveArtifacts artifacts: 'build_results.html'
                        }
                    }
                }
            }
        }

        stage ("Variant Tests") {
            parallel {
                stage ("VM-based tests") {
                    stages {
                        stage ("Test L1 simulator") {
                            when {
                                expression {doMandatoryTests}
                            }
                            steps {
                              lock (vmResource) {
                                script {
                                    timeout (time: 30, unit: 'MINUTES') {
                                        try {
                                            gitlabCommitStatus(name: "Test L1-sim") {
                                                sh "./ci-scripts/oai-ci-vm-tool test --workspace $WORKSPACE --variant l1-sim --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                                            }
                                        } catch (Exception e) {
                                            currentBuild.result = 'FAILURE'
                                        }
                                    }
                                }
                              }
                            }
                        }
                        stage ("Test RF simulator") {
                            when {
                                expression {doMandatoryTests}
                            }
                            steps {
                              lock (vmResource) {
                                script {
                                    timeout (time: 40, unit: 'MINUTES') {
                                        try {
                                            gitlabCommitStatus(name: "Test RF-sim") {
                                                sh "./ci-scripts/oai-ci-vm-tool test --workspace $WORKSPACE --variant rf-sim --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                                            }
                                        } catch (Exception e) {
                                            currentBuild.result = 'FAILURE'
                                        }
                                    }
                                }
                              }
                            }
                        }
                        stage ("Test 5G RF simulator") {
                            when {
                                expression {doMandatoryTests}
                            }
                            steps {
                              lock (vmResource) {
                                script {
                                    timeout (time: 40, unit: 'MINUTES') {
                                        try {
                                            gitlabCommitStatus(name: "Test 5G RF-sim") {
                                                sh "./ci-scripts/oai-ci-vm-tool test --workspace $WORKSPACE --variant rf5g-sim --job-name ${JOB_NAME} --build-id ${BUILD_ID} --keep-vm-alive"
                                            }
                                        } catch (Exception e) {
                                            currentBuild.result = 'FAILURE'
                                        }
                                    }
                                }
                              }
                            }
                        }
                        stage ("Destroy all Virtual Machines") {
                            when {
                                expression {doMandatoryTests}
                            }
                            steps {
                              lock (vmResource) {
                                sh "./ci-scripts/oai-ci-vm-tool destroy --job-name ${JOB_NAME} --build-id ${BUILD_ID}"
                              }
                            }
                        }
                    }
                }
            }
            post {
                always {
                    script {
                      if (doMandatoryTests) {
                        dir ('archives') {
                            sh "if [ -d rf_sim/test ] || [ -d l2_sim/test ]; then zip -r -qq vm_tests_logs.zip */test ; fi"
                        }
                        if(fileExists('archives/vm_tests_logs.zip')) {
                            archiveArtifacts artifacts: 'archives/vm_tests_logs.zip'
                            if ("MERGE".equals(env.gitlabActionType)) {
                                sh "./ci-scripts/oai-ci-vm-tool report-test --workspace $WORKSPACE --git-url ${GIT_URL} --job-name ${JOB_NAME} --build-id ${BUILD_ID} --trigger merge-request --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
                            } else {
                                sh "./ci-scripts/oai-ci-vm-tool report-test --workspace $WORKSPACE --git-url ${GIT_URL} --job-name ${JOB_NAME} --build-id ${BUILD_ID} --trigger push --branch ${GIT_BRANCH} --commit ${GIT_COMMIT}"
                            }
                            if(fileExists('test_simulator_results.html')) {
                                sh "sed -i -e 's#Build-ID: ${BUILD_ID}#Build-ID: <a href=\"${BUILD_URL}\">${BUILD_ID}</a>#' -e 's#TEMPLATE_BUILD_TIME#${JOB_TIMESTAMP}#' test_simulator_results.html"
                                archiveArtifacts artifacts: 'test_simulator_results.html'
                            }
                        }
                      }
                    }
                }
            }
        }
    }
    post {
        always {
            script {
                // Stage destroy may not be run if error in previous stage
                sh "./ci-scripts/oai-ci-vm-tool destroy --job-name ${JOB_NAME} --build-id ${BUILD_ID}"
                emailext attachmentsPattern: '*results*.html',
                     body: '''Hi,

Here are attached HTML report files for $PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!

Regards,
OAI CI Team''',
                     replyTo: 'no-reply@openairinterface.org',
                     subject: '$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!',
                     to: gitCommitAuthorEmailAddr

                if (fileExists('.git/CI_COMMIT_MSG')) {
                    sh "rm -f .git/CI_COMMIT_MSG"
                }
            }
        }
        success {
            script {
                def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): passed (" + BUILD_URL + ")"
                if ("MERGE".equals(env.gitlabActionType)) {
                    echo "This is a MERGE event"
                    addGitLabMRComment comment: message
                    def message2 = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): passed (" + BUILD_URL + ") -- MergeRequest #" + env.gitlabMergeRequestIid + " (" + env.gitlabMergeRequestTitle + ")"
                    sendSocialMediaMessage('ci-enb', 'good', message2)
                } else {
                    sendSocialMediaMessage('ci-enb', 'good', message)
                }
            }
        }
        failure {
            script {
                def message = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): failed (" + BUILD_URL + ")"
                if ("MERGE".equals(env.gitlabActionType)) {
                    echo "This is a MERGE event"
                    addGitLabMRComment comment: message
                    def message2 = "OAI " + JOB_NAME + " build (" + BUILD_ID + "): failed (" + BUILD_URL + ") -- MergeRequest #" + env.gitlabMergeRequestIid + " (" + env.gitlabMergeRequestTitle + ")"
                    sendSocialMediaMessage('ci-enb', 'danger', message2)
                } else {
                    sendSocialMediaMessage('ci-enb', 'danger', message)
                }
            }
        }
    }
}
