/**
 * Pipeline to run the test suite and build a Debian package.
 *
 * https://industrial.jenkins.tttech.com/view/NERVE/
 *
 * See also:
 *  https://confluence.tttech.com/display/NER/Debian+packages
 *  https://confluence.tttech.com/display/NER/Jenkins
 *  https://confluence.tttech.com/display/JENKINS/Pipeline+guides+and+examples
 *  https://confluence.tttech.com/display/INFRA/How+to+Configure+a+Jenkins+Pipeline+job+for+doc-builder+Docker
 */
pipeline {
    agent { label 'nerve && docker' }
    environment {
        /* variable used to determine if the package is already present. */
        PACKAGE_STATUS = 'not-present'
        ARTIFACTORY = "build.docker.tttech.com"
        ECR = "191911464942.dkr.ecr.eu-west-1.amazonaws.com"
        APT_MIRROR = 'infra.svc.nerve.cloud'
        APT_MIRROR_HOSTKEY = credentials("ssh-hostkey-${env.APT_MIRROR}")
        BUILDER = credentials('505a28bb-7065-4c02-8a4f-0477dc5275fb')
        HOME = sh(script: 'mkdir -p debian/build/ && mktemp -d -p `pwd`/debian/build/', returnStdout: true).trim()
        UID = sh(script: "id -u", returnStdout: true).trim()
        GID = sh(script: "id -g", returnStdout: true).trim()

        /* variables for Docker container */
        DOCKER_LABEL = "cloud.nerve.app=nerve-chisel"
        DOCKER_SAFE_BRANCH = BRANCH_NAME.replaceAll(/[^a-zA-Z0-9_.]/, "-")
        DOCKER_TAG = "nervesw/contrib/chisel:${DOCKER_SAFE_BRANCH}"
        DOCKER_TAG_SAFE = "${DOCKER_TAG}-${BUILD_NUMBER}"
        DOCKER_BUILD_ARGS = "--label=${DOCKER_LABEL} -t ${DOCKER_TAG_SAFE}"
    }
    options {
        buildDiscarder(logRotator(
            artifactDaysToKeepStr: '5',
            artifactNumToKeepStr: '10',
            daysToKeepStr: '31',
            numToKeepStr: '31'
        ))
        skipDefaultCheckout()
        timestamps()
    }
    parameters {
        string(
            name: 'DIST',
            defaultValue: 'buster',
            description: 'Release to build for (buster = development)'
        )
        choice(
            name: 'ARCH',
            choices: 'amd64\ni386',
            description: 'Architecture to build on (for now should always be "amd64")'
        )
        booleanParam (
            name : 'UPLOAD',
            defaultValue: env.BRANCH_NAME == 'integration',
            description: 'Upload package to APT repository.'
        )
        string(
            name: 'APT_COMPONENT',
            defaultValue: 'contrib',
            description: 'APT component to upload to.'
        )
    }
    stages {
        /**
         * Checkout the source code.
         *
         * The code is already checked out by Jenkins, but in a "detached HEAD" state, which will not work
         * with gbp-buildpackage.
         */
        stage ('Git checkout') {
            steps {
                /* see https://jenkins.io/doc/pipeline/steps/git/ */
                script {
                    checkout poll: false,
                    scm: [
                        $class: 'GitSCM',
                        branches: [[name: env.BRANCH_NAME]],
                        extensions: scm.extensions + [
                            [$class: 'LocalBranch'], [$class: 'WipeWorkspace'],

                            // This line makes git also fetch tags
                            [$class: 'CloneOption', noTags: false, shallow: false, depth: 0, reference: '']
                        ],
                        userRemoteConfigs: [[
                            url:  "https://git.tttech.com/scm/nerve/nerve-chisel.git",
                            branch: env.BRANCH_NAME,
                            credentialsId: '505a28bb-7065-4c02-8a4f-0477dc5275fb',
                        ]]
                    ]
                }
            }
        }

        stage ('Prepare environment') {
            steps {
                withCredentials([
                    [$class: 'AmazonWebServicesCredentialsBinding',
                     credentialsId: '02acf2ff-47e1-4b64-b5a0-f501c60a592f']
                ]) {
                    sh "\$(aws ecr get-login --no-include-email --region eu-west-1)"
                }

                sh "echo ${env.BUILDER_PSW} | docker login -u ${BUILDER_USR} --password-stdin ${ARTIFACTORY}"

                /* equivalent to: dpkg-parsechangelog -n 1 -S Version */
                script {
                    PACKAGE = sh(
                        returnStdout: true,
                        script: "sed '/^\\s*\$/d' debian/changelog | head -n 1 | awk '{ print \$1; }'"
                    ).trim()
                }
            }
        }

        /**
         * If we are building a released version without a timestamp, the package may already have been
         * uploaded to our APT repository.
         *
         * The remote "check-package" script will look for the package in our APT repositories and copy the
         * package from another distribution if necessary. The result is stored in the 'PACKAGE_STATUS'
         * variable and will be "not-present" if the package cannot be found in APT.
         *
         * This step only runs if we build from a tag and we would actually upload to APT.
         */
        stage ('Check APT') {
            when {
                allOf {
                    buildingTag()
                    expression { return params.UPLOAD }
                }
            }
            steps {
                echo "Building a tagged version, so check if package is already built elsewhere..."
                script {
                    /* equivalent to: dpkg-parsechangelog -n 1 -S Source */
                    VERSION = sh(
                        returnStdout: true,
                        script: "sed '/^\\s*\$/d' debian/changelog | head -n 1 | awk '{ print \$2; }' | tr -d '()'"
                    ).trim()

                    PACKAGE_STATUS = withCredentials([sshUserPrivateKey(
                        credentialsId: 'f0b278d1-0874-4b40-886a-c22622342fab',
                        keyFileVariable: 'SSH_KEYFILE',
                        passphraseVariable: '',
                        usernameVariable: 'SSH_USERNAME'
                    )]) {
                        def remote = [:]
                        remote.name = APT_MIRROR
                        remote.host = APT_MIRROR
                        remote.knownHosts = env.APT_MIRROR_HOSTKEY
                        remote.user = SSH_USERNAME
                        remote.identityFile = SSH_KEYFILE

                        lock(resource: env.APT_MIRROR) {
                            sshCommand remote: remote, command: "check-package ${PACKAGE} ${VERSION} ${params.DIST}"
                        }
                    }
                }
                echo "Package is ${PACKAGE_STATUS}."
            }
        }

        /**
         * Finally, actually build the package.
         */
        stage ('Build package') {
            when {
                expression {
                    return PACKAGE_STATUS == 'not-present'
                }
            }
            environment {
                GNUPG_SIGN_KEY = credentials('9dddc074-0903-487c-aa20-7cb6d9de403c')
                BUILD_IMAGE = "${ARTIFACTORY}/nervesw/nerve-gbp:${params.DIST}"
            }
            steps {
                sh """
                    # pull to make sure we have the newest version
                    docker pull ${BUILD_IMAGE}
                    docker run --rm -v `pwd`:/gbp \
                        -v ${GNUPG_SIGN_KEY}:/tmp/gpg.key \
                        -e UID=${UID} -e GID=${GID} \
                        -e EXPORT=${env.TAG_NAME} -e DEBIAN_BRANCH=${BRANCH_NAME} \
                        -e UPSTREAM_BRANCH=master \
                        -e EXPORT_DIR=debian/build/${params.DIST}-${params.ARCH}/ \
                        ${BUILD_IMAGE}
                """
            }
        }
        stage ('Upload to Artifactory') {
            when {
                expression {
                    return PACKAGE_STATUS == 'not-present'
                }
            }
            steps {
                rtBuildInfo (maxBuilds: 10, maxDays: 15, doNotDiscardBuilds: ["1"])
                rtUpload (
                    serverId: 'TTTech Artifactory',
                    spec: """{
                      "files": [
                          {
                            "pattern": "debian/build/${params.DIST}-${params.ARCH}/*",
                            "target": "build/nervesw/${PACKAGE}/deb/${BRANCH_NAME}/build$BUILD_NUMBER/"
                          }
                      ]
                    }"""
                )
                rtPublishBuildInfo (serverId: 'TTTech Artifactory')

            }
        }
        stage ('Upload to APT') {
            when {
                allOf {
                    expression {
                        return PACKAGE_STATUS == 'not-present'
                    }
                    expression { return params.UPLOAD }
                }
            }
            steps {
                script {
                    CHANGES_FILENAME = sh(
                        returnStdout: true,
                        script: "basename `ls debian/build/${params.DIST}-${params.ARCH}/*.changes | head -n 1`"
                    ).trim()

                    PACKAGE_STATUS = withCredentials([sshUserPrivateKey(
                        credentialsId: 'f0b278d1-0874-4b40-886a-c22622342fab',
                        keyFileVariable: 'SSH_KEYFILE',
                        passphraseVariable: '',
                        usernameVariable: 'SSH_USERNAME'
                    )]) {
                        def remote = [:]
                        remote.name = APT_MIRROR
                        remote.host = APT_MIRROR
                        remote.knownHosts = env.APT_MIRROR_HOSTKEY
                        remote.user = SSH_USERNAME
                        remote.identityFile = SSH_KEYFILE

                        lock(resource: env.APT_MIRROR) {
                            sshPut remote: remote, from: "debian/build/${params.DIST}-${params.ARCH}", into: "incoming/"
                            sshCommand remote: remote, command: "process-upload -c ${params.APT_COMPONENT} -d ${params.DIST} incoming/${params.DIST}-${params.ARCH}/${CHANGES_FILENAME}"
                        }
                    }
                }
            }
        }
        stage ('Docker image') {
            steps {
                sh "docker build ${DOCKER_BUILD_ARGS} ."

                script {
                    [ECR, ARTIFACTORY].each { registry ->
                        sh "docker tag ${DOCKER_TAG_SAFE} ${registry}/${DOCKER_TAG}"
                        sh "docker tag ${DOCKER_TAG_SAFE} ${registry}/${DOCKER_TAG_SAFE}"
                        sh "docker push ${registry}/${DOCKER_TAG}"
                        sh "docker push ${registry}/${DOCKER_TAG_SAFE}"
                    }
                }
            }
        }
    }
    post {
        always {
            archiveArtifacts artifacts: "debian/build/${params.DIST}-${params.ARCH}/*", fingerprint: true, allowEmptyArchive: true
            cleanWs()

            lock(resource: "${env.NODE_NAME}:docker-image-prune") {
                sh "docker image prune -f -a --filter=label=${DOCKER_LABEL} --filter=until=120h"
            }
        }
    }
}
