///////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2019, 2020 Kiel University and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
// 
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GPL-3.0 which is available at
// https://www.gnu.org/licenses/gpl-3.0-standalone.html.
// 
// SPDX-License-Identifier: EPL-2.0 OR GPL-3.0-or-later
///////////////////////////////////////////////////////////////////////////////

pipeline {
  // Build agent is defined by a custom pod. See documentation at
  // https://wiki.eclipse.org/Jenkins. We need to...
  // 
  // - ...build ELK with Maven
  // - ...build the website with Hugo
  // - ...deploy artifacts to download.eclipse.org
  // - ...use the local Nexus server as proxy for Maven Central
  // - ...deploy artifacts to OSSRH / Maven Central
  agent  {
    kubernetes {
      label 'elk-build-pod'
      yaml """
apiVersion: v1
kind: Pod
spec:
  containers:
  - name: maven
    image: maven:3.9-eclipse-temurin-17
    command:
    - cat
    tty: true
    env:
    - name: "MAVEN_OPTS"
      value: "-Duser.home=/home/jenkins"
    - name: "HOME"
      value: "/home/jenkins"
    volumeMounts:
    - name: "jenkins-home"
      mountPath: "/home/jenkins"
      readOnly: false
    - name: settings-xml
      mountPath: /home/jenkins/.m2/settings.xml
      subPath: settings.xml
      readOnly: true
    - name: toolchains-xml
      mountPath: /home/jenkins/.m2/toolchains.xml
      subPath: toolchains.xml
      readOnly: true
    - name: settings-security-xml
      mountPath: /home/jenkins/.m2/settings-security.xml
      subPath: settings-security.xml
      readOnly: true
    - name: m2-repo
      mountPath: /home/jenkins/.m2/repository
    resources:
      limits:
        memory: "2Gi"
        cpu: "1"
      requests:
        memory: "2Gi"
        cpu: "1"
  - name: hugo
    image: klakegg/hugo:alpine
    command:
    - cat
    tty: true
  - name: jnlp
    volumeMounts:
    - name: "jenkins-home"
      mountPath: "/home/jenkins"
      readOnly: false
    - name: volume-known-hosts
      mountPath: /home/jenkins/.ssh
  volumes:
  - name: volume-known-hosts
    configMap:
      name: known-hosts
  - name: settings-xml
    secret:
      secretName: m2-secret-dir
      items:
      - key: settings.xml
        path: settings.xml
  - name: toolchains-xml
    configMap:
      name: m2-dir
      items:
      - key: toolchains.xml
        path: toolchains.xml
  - name: settings-security-xml
    secret:
      secretName: m2-secret-dir
      items:
      - key: settings-security.xml
        path: settings-security.xml
  - name: "jenkins-home"
    emptyDir: {}
  - name: m2-repo
    emptyDir: {}
"""
    }
  }

  // Parameters to be supplied to the build
  parameters {
    // For the source repository
    string(
      name: 'BRANCH',
      description: 'The repository branch to checkout.',
      defaultValue: 'master'
    )

    // For the version number
    string(
      name: 'VERSION',
      description: 'The build version number. Used to generate download file names.',
      defaultValue: 'nightly'
    )

    // Regarding ELK proper...
    string(
      name: 'ELK_TARGET_DIR',
      description: 'Path to the directory the build will copy all downloadable artifacts to.',
      defaultValue: '/home/data/httpd/download.eclipse.org/elk/updates/nightly/'
    )
  }

  triggers {
    // Build each night sometime between 3:00am and 5:59am Berlin time
    cron '''TZ=Europe/Berlin
  H H(3-5) * * *'''
  }

  options {
    // We checkout our repositories explicitly
    skipDefaultCheckout true

    // Keep at most 14 builds for at most 14 days
    buildDiscarder logRotator(artifactDaysToKeepStr: '14', artifactNumToKeepStr: '4', daysToKeepStr: '14', numToKeepStr: '14')
  }

  stages {
    stage('Prepare') {
      // Checks out all of the repositories and creates all directories
      // our build requires
      steps {
        // Clone main ELK Repository
        checkout(
          [
            $class: 'GitSCM',
            branches: [[name: "*/${params.BRANCH}"]],
            extensions: [
              [
                $class: 'CloneOption',
                noTags: true,
                shallow: true
              ], [
                $class: 'RelativeTargetDirectory',
                relativeTargetDir: 'elk'
              ], [
                $class: 'CleanCheckout'
              ]
            ],
            submoduleCfg: [],
            userRemoteConfigs: [[url: 'https://github.com/eclipse-elk/elk.git']]
          ]
        )

        // Clone models repository (for unit tests)
        checkout(
          [
            $class: 'GitSCM',
            branches: [[name: "*/master"]],
            extensions: [
              [
                $class: 'CloneOption',
                noTags: true,
                shallow: true
              ], [
                $class: 'RelativeTargetDirectory',
                relativeTargetDir: 'elk-models'
              ], [
                $class: 'CleanCheckout'
              ]
            ],
            submoduleCfg: [],
            userRemoteConfigs: [[url: 'https://github.com/eclipse-elk/elk-models.git']]
          ]
        )
      }
    }

    stage('MelkCompiler') {
      // Builds and publishes the ELK Meta Data Compiler
      steps {
        container('maven') {
          sh "mvn -v"
          sh "mvn -f elk/build/pom.xml" +
            " -Pelk-meta" +
            " clean install"
        }
      }
    }

    stage('TestELK') {
      // Builds ELK and executes our unit tests. Ideally, we would want this stage to
      // be merged with BuildELK, but the build itself fails if we activate both, the
      // integration test and the deploy phases. If we can solve that problem, we can
      // merge the two steps.
      steps {
        container('maven') {
          sh "mvn -v"
          sh "mvn -f elk/build/pom.xml" +
            " -Pelk-update-site" +
            " --define tests.paths.elk-repo=${env.WORKSPACE}/elk" +
            " --define tests.paths.models-repo=${env.WORKSPACE}/elk-models" +
            " clean integration-test"
        }
      }
    }

    stage('DeployELK') {
      // Builds the ELK update site itself and publishes to Maven Central (again, this
      // should, in an ideal world, be merged with the TestELK stage)
      steps {
        container('maven') {
          // To sign our artifacts, we need the private key to be imported.
          withCredentials([file(credentialsId: 'secret-subkeys.asc', variable: 'KEYRING')]) {
              sh 'gpg --batch --import "${KEYRING}"'
              sh 'for fpr in $(gpg --list-keys --with-colons  | awk -F: \'/fpr:/ {print $10}\' | sort -u); do echo -e "5\ny\n" |  gpg --batch --command-fd 0 --expert --edit-key ${fpr} trust; done'
          }

          sh "mvn -v"
          sh "mvn -f elk/build/pom.xml" +
            " -Pelk-update-site -Pmaven-publish -Psign" +
            " --define elk.metadata.documentation.outputPath=elk/docs/" +
            " --define tests.paths.elk-repo=${env.WORKSPACE}/elk" +
            " --define tests.paths.models-repo=${env.WORKSPACE}/elk-models" +
            " clean deploy"
        }
      }
    }

    stage('Website') {
      steps {
        container('hugo') {
          dir ('elk/docs') {
            sh "hugo"
          }
        }

        container('jnlp') {
          // Deploy the website
          dir ('elk/build/releng') {
            sshagent(['	github-bot-ssh']) {
              sh "bash publish-website.sh"
            }
          }
        }
      }
    }

    stage('Publish') {
      steps {
        // Zip the update site
        dir ('elk/build/org.eclipse.elk.repository/target/repository') {
          sh "zip -r elk-${params.VERSION}.zip *"
        }
        
        // Zip the website, put the zip file into the update site directory as well
        dir ('elk/docs/public') {
          sh "zip -r ../../build/org.eclipse.elk.repository/target/repository/elk-${params.VERSION}-docs.zip *"
        }
        
        // Deploy the previously built artifacts
        container('jnlp') {
          // Deploy update site folder
          sshagent(['projects-storage.eclipse.org-bot-ssh']) {
            sh "ssh genie.elk@projects-storage.eclipse.org rm -rfv ${params.ELK_TARGET_DIR}"
            sh "ssh genie.elk@projects-storage.eclipse.org mkdir -pv ${params.ELK_TARGET_DIR}"
            sh "scp -vr elk/build/org.eclipse.elk.repository/target/repository/* genie.elk@projects-storage.eclipse.org:${params.ELK_TARGET_DIR}"
          }
        }
      }
    }
  }

  post {
    always {
      // Publish JUnit test result reports for them to show up in Jenkins
      junit testResults: 'elk/test/**/surefire-reports/*.xml', allowEmptyResults: true
    }
    
    regression {
      // Notify developers of regressions
      mail subject: "ELK ${currentBuild.fullDisplayName}: ${currentBuild.currentResult}",
        body: "Oh noes! The build has fallen into the hands of an evil regression! Check console output at $BUILD_URL to view the results.",
        to: 'sdo@informatik.uni-kiel.de, mka@informatik.uni-kiel.de'
    }

    fixed {
      // Notify that build is stable again
      mail subject: "ELK ${currentBuild.fullDisplayName}: ${currentBuild.currentResult}",
        body: "Hail to the source, the build is fixed! Check console output at $BUILD_URL to view the results.",
        to: 'sdo@informatik.uni-kiel.de, mka@informatik.uni-kiel.de'
    }
  }
}
