/*
 * The Jenkins job should be configured with the following properties:
 *
 * - Disable concurrent builds
 * - Parameters (all must be trimmed; all are strings):
 *   - RELEASE_TYPE
 *      defaultValue: auto
 *      description: Valid values are: auto, minor, snapshot.0, snapshot.1, edge. When "auto" is
 *                   specified, the type of the release will be determined based on the current date.
 *   - RELEASING_BRANCHES
 *      defaultValue: refs/heads/master
 *      description: Name of branch of all repositories to checkout and run the release. The only
 *                   exception is the website which is always run on the `master` branch.
 *   - BACKEND_REPO
 *      defaultValue: kiali/kiali
 *      description: The GitHub repo of the back-end sources, in owner/repo format.
 *   - UI_REPO
 *      defaultValue: kiali/kiali-ui
 *      description: The GitHub repo of the front-end sources, in owner/repo format.
 *   - OPERATOR_REPO
 *      defaultValue: kiali/kiali-operator
 *      description: The GitHub repo of the kiali-operator sources, in owner/repo format.
 *   - SITE_REPO
 *      defaultValue: kiali/kiali.io
 *      description: The GitHub repo of the website sources, in owner/repo format.
 *   - HELM_REPO
 *      defaultValue: kiali/helm-charts
 *      description: The GitHub repo of the Helm charts sources, in owner/repo format.
 *   - QUAY_NAME
 *      defaultValue: quay.io/kiali/kiali
 *      description: The name of the Quay repository to push the release
 *   - QUAY_OPERATOR_NAME
 *      defaultValue: quay.io/kiali/kiali-operator
 *      description: The name of the Quay repository to push the operator release
 *   - SKIP_KIALI_SERVER_RELEASE
 *      defaultValue: n
 *      description: Set to 'y' if you don't want to release the server (back-end and front-end)
 *   - SKIP_OPERATOR_RELEASE
 *      defaultValue: n
 *      description: Set to 'y' if you don't want to release the operator
 *   - SKIP_HELM_RELEASE
 *      defaultValue: n
 *      description: Set to 'y' if you don't want to release the helm charts
 *   - SKIP_SITE_RELEASE
 *      defaultValue: n
 *      description: Set to 'y' if you don't want to release the website
 *   - NPM_CONFIG_REGISTRY
 *       defaultValue: ''
 *       description: NPM registry to use for fetching packages. This is not used for publishing releases.
 *                    Do not include the trailing slash.
 *   - NOTIFICATIONS_EMAIL
 *       defaultValue: ''
 *       description: E-mail for sending build failure notifications.
 */

def determineReleaseType() {
  // This script determines the type of release that
  // should be done, given the current date.
  // It is possible to specify a different date
  // by setting the NOW_DATE environment variable.
  // The script will print a text:
  // - "minor": if it's determined that a minor release
  //     should be built.
  // - "snapshot.0": if it's determined that a snapshot
  //     release should be built (specifically, the first
  //     snapshot of the sprint.
  // - "snapshot.1": if it's determined that a snapshot
  //     release should be built (specifically, the second
  //     snapshot of the sprint.
  // - "snapshot.2": for some sprints with longer duration.
  //
  // The reference date (base date) can be set in the
  // environment variable BASE_DATE. By default, it is the
  // last day of Kiali Sprint #14. Starting at end of Sprint #33,
  // BASE_DATE is the last day of Sprint #33.
  //
  // Both NOW_DATE and BASE_DATE should be given in seconds
  // since EPOCH. It is assumed that this script is run weekly
  // starting in the base date. Running at different timespans
  // won't guarantee a good result.
  return sh (script: '''
BASE_DATE=${BASE_DATE:-$(date -d '2021-12-03' '+%s')} # Use last day of Sprint #66 as the base date for calcs
NOW_DATE=${NOW_DATE:-$(date -d 'now' '+%s')}

# Transitional calculations
DATE_DIFF=$(( $NOW_DATE - $BASE_DATE ))
DAYS_ELAPSED=$(( $DATE_DIFF / (24*60*60) ))
WEEKS_ELAPSED=$(( $DAYS_ELAPSED / 7))

# This value will be used to determine the type of the release
WEEKS_MOD3=$(( $WEEKS_ELAPSED % 3 ))

# Between Dec 23th 2021 and Jan 14th 2022, use Mod6 (six-week sprint)
if [ $NOW_DATE -ge $(date -d '2021-12-23' '+%s') ] && [ $NOW_DATE -lt $(date -d '2022-01-14' '+%s') ];
then
  WEEKS_MOD3=$(( $WEEKS_ELAPSED % 6 ))
fi

case $WEEKS_MOD3 in
  0)
    RELEASE_TYPE='minor' ;;
  1)
    RELEASE_TYPE='snapshot.0' ;;
  2)
    RELEASE_TYPE='snapshot.1' ;;
  3)
    RELEASE_TYPE='snapshot.2' ;;
  4)
    RELEASE_TYPE='snapshot.3' ;;
  5)
    RELEASE_TYPE='snapshot.4' ;;
esac

# Print the determined type
echo $RELEASE_TYPE
  ''',
  returnStdout: true).trim()
}

node('kiali-build && fedora') {
  def backendDir = 'src/github.com/kiali/kiali'
  def backendMakefile = 'deploy/jenkins-ci/Makefile'

  def uiDir = 'src/github.com/kiali/kiali/frontend'
  def uiMakefile = 'Makefile.jenkins'

  def buildServer = params.SKIP_KIALI_SERVER_RELEASE != "y"
  def buildOperator = params.SKIP_OPERATOR_RELEASE != "y"
  def buildHelm = params.SKIP_HELM_RELEASE != "y" // Temptative value. It's re-assigned later.
  def buildSite = params.SKIP_SITE_RELEASE != "y" // Temptative value. It's re-assigned later.
  def quayTag = ""

  if ( !buildServer && !buildOperator && !buildHelm && !buildSite ) {
    currentBuild.result = 'ABORTED'
    echo "Nothing to release. Stopping."
    return
  }

  try {
    cleanWs()
    stage('Checkout code') {
      if ( buildServer ) {
        checkout([
          $class: 'GitSCM',
          branches: [[name: params.RELEASING_BRANCHES]],
          doGenerateSubmoduleConfigurations: false,
          extensions: [
            [$class: 'RelativeTargetDirectory', relativeTargetDir: backendDir]
          ],
          submoduleCfg: [],
          userRemoteConfigs: [[
            credentialsId: 'kiali-bot-gh-ssh',
            url: "git@github.com:${params.BACKEND_REPO}.git"]]
        ])

        sh "cd ${backendDir}; git config user.email 'kiali-dev@googlegroups.com'"
        sh "cd ${backendDir}; git config user.name 'kiali-bot'"
      }
    }

    // Determine release type if "auto" was specified
    def releaseType = "${params.RELEASE_TYPE}"
    if ( releaseType == "auto" ) {
      releaseType = determineReleaseType()
    }

    buildSite = params.SKIP_SITE_RELEASE != "y" && releaseType == "minor"
    buildHelm = params.SKIP_HELM_RELEASE != "y" && (releaseType == "minor" || releaseType == "patch")
    echo "Resolved release type: ${releaseType}"
    echo "Will build back-end? ${buildServer}"
    echo "Will build front-end? ${buildServer}"
    echo "Will build operator? ${buildOperator}"
    echo "Will build Helm charts? ${buildHelm}"
    echo "Will build site? ${buildSite}"

    if ( buildServer ) {
      withEnv(["PATH+TOOLS=${env.WORKSPACE}/${backendDir}/deploy/jenkins-ci/bin",
              "GOPATH=${env.WORKSPACE}",
              "BACKEND_GITHUB_URI=git@github.com:${params.BACKEND_REPO}.git",
              "BACKEND_FORK_URI=git@github.com:kiali-bot/kiali.git",
              "BACKEND_PULL_URI=https://api.github.com/repos/${params.BACKEND_REPO}/pulls",
              "NPM_DRY_RUN=y",  // Just for safety, as we may be running Makefiles for pre-v1.35
              "UI_GITHUB_URI=git@github.com:${params.UI_REPO}.git",
              "UI_FORK_URI=git@github.com:kiali-bot/kiali-ui.git",
              "UI_PULL_URI=https://api.github.com/repos/${params.UI_REPO}/pulls",
              "RELEASE_TYPE=${releaseType}"
      ]) {
        parallel backend: {
          withEnv(["GOPATH=${env.WORKSPACE}"]) {
            stage('Build backend') {
              sh "make -f ${backendMakefile} -C ${backendDir} backend-build-release"
            }
            stage('Test backend') {
              sh "make -f ${backendMakefile} -C ${backendDir} backend-test"
            }
          }
        }, ui: {
          stage('Build UI') {
            sh "make -f ${uiMakefile} -C ${uiDir} ui-build"
          }
          stage('Test UI') {
            sh "make -f ${uiMakefile} -C ${uiDir} ui-test"
          }
        },
        failFast: true

        stage('Release Kiali to Container Repositories') {
          withCredentials([usernamePassword(credentialsId: 'kiali-quay', passwordVariable: 'QUAY_PASSWORD', usernameVariable: 'QUAY_USER')]) {
            sh "make -f ${backendMakefile} -C ${backendDir} backend-push-docker"
            quayTag = sh(returnStdout: true, script: "sed -rn 's/^VERSION \\?= v(.*)/v\\1/p' ${backendDir}/Makefile").trim()
          }
        }

        stage('Create release cut in back-end repo') {
          withCredentials([string(credentialsId: 'kiali-bot-gh-token', variable: 'GH_TOKEN')]) {
            sshagent(['kiali-bot-gh-ssh']) {
              sh "make -f ${backendMakefile} -C ${backendDir} backend-push-version-tag backend-prepare-next-version"
            }
          }
        }
      }

      stage('Invoke other jobs') {
        if ( buildOperator ) {
          build(job: 'kiali-operator-release',
              parameters: [
              [$class: 'StringParameterValue', value: releaseType, name: 'RELEASE_TYPE'],
              [$class: 'StringParameterValue', value: params.OPERATOR_REPO, name: 'OPERATOR_REPO'],
              [$class: 'StringParameterValue', value: params.RELEASING_BRANCHES, name: 'OPERATOR_RELEASING_BRANCH'],
              [$class: 'StringParameterValue', value: params.QUAY_OPERATOR_NAME, name: 'QUAY_OPERATOR_NAME']
              ], wait: false
              )
        }
        if ( buildSite ) {
          // Although the `kiali-website-release` can receive the "releasing branch",
          // don't pass it and let it use the default `refs/heads/master`. It's unused at the moment.
          // The website is "released" only on minor and major releases and it's only tagged. No "version branch"
          // is created. This is because versioned docs are simple folders within the repository
          // that are in the `master` branch. So, if a previous version of the doc needs to be
          // updated, that's done manually. Docs tend to not change for patch releases and also
          // tend to be fixed regardless if there is (or not) a patch release.
          build(job: 'kiali-website-release',
              parameters: [
              [$class: 'StringParameterValue', value: params.SITE_REPO, name: 'SITE_REPO']
              ], wait: false
              )
        }
        if ( buildHelm ) {
          build(job: 'kiali-helm-release',
              parameters: [
              [$class: 'StringParameterValue', value: releaseType, name: 'RELEASE_TYPE'],
              [$class: 'StringParameterValue', value: params.HELM_REPO, name: 'HELM_REPO'],
              [$class: 'StringParameterValue', value: params.RELEASING_BRANCHES, name: 'HELM_RELEASING_BRANCH']
              ], wait: false
              )
        }
      }
    }
  } catch (e) {
    echo e.toString()
    if (params.NOTIFICATIONS_EMAIL.length() != 0) {
        emailext(
            to: params.NOTIFICATIONS_EMAIL,
            subject: 'Kiali build failure',
            body: '${JELLY_SCRIPT,template="html"}',
            attachLog: true,
            mimeType: 'text/html'
        )
    }
    throw e
  } finally {
    cleanWs()
  }
}

