// Global Variable so it can be changed between stages
def GIT_BRANCH_NAME=getGitBranchName()

pipeline {
  agent {
    kubernetes{
      label 'slave-2cpu-8gb'
    }
  }
  parameters {
    string(name: 'REPOSITORY_SERVER', defaultValue: 'gcr.io/stack-test-186501', description: 'Registry server URL to pull/push images', trim: true)
    string(name: 'BASE_NAMESPACE', defaultValue: 'default', description: 'In which namespace micro services needs to be deploy', trim: true)
    string(name: 'CONNECTION_ID', defaultValue: 'test', description: 'connection id', trim: true)
    string(name: 'WORKSPACE_ID', defaultValue: 'fullstack-pro', description: 'workspace id', trim: true)
    string(name: 'UNIQUE_NAME', defaultValue: 'default', description: 'chart name', trim: true)
    string(name: 'VERSION', defaultValue: 'v1', description: 'version of the deployment', trim: true)
    string(name: 'HEMERA_LOG_LEVEL', defaultValue: 'info', description: 'log level for hemera')
    string(name: 'LOG_LEVEL', defaultValue: 'info', description: 'log level')
    string(name: 'DEPLOYMENT_PATH', defaultValue: '/servers', description: 'folder path to load helm charts')
    string(name: 'PUBLISH_BRANCH', defaultValue: 'devpublish', description: 'the publish branch for packages release')
    string(name: 'EXCLUDE_SETTING_NAMESPACE_FILTER', defaultValue: 'brigade', description: 'exclude setting namespace that matches search string')
    string(name: 'GIT_CREDENTIAL_ID', defaultValue: 'fullstack-pro-github-deploy-key', description: 'jenkins credential id of git deploy secret')
    string(name: 'BUILD_MODULE_TO_INCLUDE', defaultValue: '@sample-stack*', description: 'build env')
    string(name: 'REPOSITORY_SSH_URL', defaultValue: 'git@github.com:CDEBase/fullstack-pro.git', description: 'ssh url of the git repository')
    string(name: 'REPOSITORY_BRANCH', defaultValue: 'develop', description: 'the branch with changes')
    string(name: 'DEVELOP_BRANCH', defaultValue: 'develop', description: 'the branch for the development')
    string(name: 'MASTER_BRANCH', defaultValue: 'master', description: 'Master branch as default branch for production.')

    // by default first value of the choice will be choosen
    choice choices: ['auto', 'force'], description: 'Choose merge strategy', name: 'NPM_PUBLISH_STRATEGY'
    choice choices: ['yarn', 'npm'], description: 'Choose build strategy', name: 'BUILD_STRATEGY'
    choice choices: ['0.7.12','0.7.11', '0.6.0'], description: 'Choose Idestack chart version', name: 'IDESTACK_CHART_VERSION'
    choice choices: ['nodejs20', 'nodejs18', 'nodejs22'], description: 'Choose NodeJS version', name: 'NODEJS_TOOL_VERSION'    
    choice choices: ['buildOnly', 'buildAndTest', 'buildAndPublish',  'mobileBuild', 'mobilePreview', 'mobilePreviewLocal', 'mobilePreviewSubmit', 'mobileProd', 'mobileProdSubmit', 'devDeployOnly', 'stageDeploy', 'stageDeployOnly', 'prodDeploy', 'prodDeployOnly', 'allenv'], description: 'Where to deploy micro services?', name: 'ENV_CHOICE'
    choice choices: ['all', 'ios', 'android' ], description: 'Mobile type if it is mobile build?', name: 'MOBILE_CHOICE'
    booleanParam (defaultValue: false, description: 'Skip production release approval', name: 'SKIP_RELEASE_APPROVAL')
    booleanParam (defaultValue: false, description: 'Tick to enable debug mode', name: 'ENABLE_DEBUG')
    string(name: 'BUILD_TIME_OUT', defaultValue: '120', description: 'Build timeout in minutes', trim: true)
  }

  // Setup common + secret key variables for pipeline.
  environment {
    BUILD_COMMAND = getBuildCommand()
    NAMESPACE = "${params.BASE_NAMESPACE}-${params.VERSION}"
    PYTHON = '/usr/bin/python'
    GCR_KEY = credentials('jenkins-gcr-login-key')
    EXPO_TOKEN = credentials('expo_cdmbase_token')
    GIT_PR_BRANCH_NAME = getGitPrBranchName()
    GITHUB_HELM_REPO_TOKEN = credentials('github-helm-repo-access-token')
  }

  // Initialize npm and docker commands using plugins
  tools {
    nodejs params.NODEJS_TOOL_VERSION
  }

  stages {
    stage('define environment') {
      steps {
        // skip the build if ends with `[skip ci]` which is equivalent to regex `.*\[skip ci\\]\\s`
        scmSkip(deleteBuild: true, skipPattern:'.*\\[skip ci\\]\\s')
        checkout([$class: 'GitSCM', branches: [[name: '*/' + params.REPOSITORY_BRANCH]],
          doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'WipeWorkspace']],
          submoduleCfg: [], userRemoteConfigs: [[credentialsId: params.GIT_CREDENTIAL_ID, url: params.REPOSITORY_SSH_URL]]])
        sh "git checkout ${env.GIT_PR_BRANCH_NAME}"
      }
    }

    stage('Unlock secrets') { //unlock keys for all runs
      environment { deployment_env = 'dev' }
      steps {
        sh '''
          gpg --import /tmp/gpg-public-key/gpg-public-key.pub
          gpg --import /tmp/gpg-private-key/gpg-private-key.key
          git-crypt unlock
        '''
        load "./jenkins_variables.groovy"
      }
    }

    // Install packages. If
    // a. any branch
    // b. ENV_CHOICE set not selected `dev`, `stage` or `prod`
    stage('Install git repository') {
      steps {
        sh """
          echo "what is docker git version $GIT_BRANCH_NAME -- ${params.ENV_CHOICE}"
          ${params.BUILD_STRATEGY} install
        """
      }
    }

    stage('Mobile Build') {
      when {
        expression { params.ENV_CHOICE == 'mobileBuild' || params.ENV_CHOICE == 'mobilePreview' || params.ENV_CHOICE == 'mobilePreviewLocal' || params.ENV_CHOICE == 'mobilePreviewSubmit' || params.ENV_CHOICE == 'mobileProd' || params.ENV_CHOICE == 'mobileProdSubmit' }
      }
      steps {
        sshagent(credentials: [params.GIT_CREDENTIAL_ID]) {
          sh """
            rm .npmrc
            npx lerna exec --scope=*mobile-device ${params.BUILD_STRATEGY} ${env.BUILD_COMMAND}
            git checkout -- .npmrc
            yarn git:pull
            yarn gitcommit
            yarn git:push
          """
        }
      }
    }

    // Run build for all cases except when ENV_CHOICE is 'buildAndPublish' and `dev`, `stage` or `prod`
    stage('Build Packages') {
      when {
        expression { params.ENV_CHOICE == 'buildOnly' || params.ENV_CHOICE == 'buildAndTest' || params.ENV_CHOICE == 'buildAndPublish' }
      }
      steps {
        sh """
          ${params.BUILD_STRATEGY} run build
        """
      }
    }

    // Test build for all cases except when ENV_CHOICE is 'buildAndPublish' and `dev`, `stage` or `prod`
    stage('Test Packages') {
      when {
        expression { params.ENV_CHOICE == 'buildAndTest' }
      }
      steps {
        sh """
          ${params.BUILD_STRATEGY} run test
        """
      }
    }

    // if PR is from branch other than `develop` then merge to `develop` if we chose ENV_CHOICE as 'buildAndPublish'.
    // Skip this stage. Future implementation.
    stage('Merge PR, Install, Build') {
      when {
        expression { params.ENV_CHOICE == '1' }
      }
      steps {
        sh """
          git checkout ${params.DEVELOP_BRANCH}
          git merge ${env.GIT_PR_BRANCH_NAME} -m 'auto merging ${params.GIT_PR_BRANCH_NAME} \r\n[skip ci]'
          git push origin ${params.DEVELOP_BRANCH}
          ${params.BUILD_STRATEGY} install
          ${params.BUILD_STRATEGY} run build
        """
        script {
          GIT_BRANCH_NAME = params.DEVELOP_BRANCH
        }
      }
    }

    // publish packages to npm repository.
    // commit new package-lock.json that might get generated during install
    // Build will be ignore with tag '[skip ci]'
    stage('Publish Packages') {
      when {
        expression { GIT_BRANCH_NAME == params.DEVELOP_BRANCH }
        expression { params.ENV_CHOICE == 'buildOnly' || params.ENV_CHOICE == 'buildAndPublish' }
      }
      steps {
        script {
          GIT_BRANCH_NAME = params.PUBLISH_BRANCH
        }
        sshagent(credentials: [params.GIT_CREDENTIAL_ID]) {
          sh """
            git add -A
            git diff --staged --quiet || git commit -am 'auto build [skip ci] \r\n'
            git fetch origin ${params.DEVELOP_BRANCH}
            git checkout ${params.DEVELOP_BRANCH}
            ${params.BUILD_STRATEGY} run devpublish:${params.NPM_PUBLISH_STRATEGY};
            git push origin ${params.DEVELOP_BRANCH}
            git checkout ${params.PUBLISH_BRANCH}
          """
        }
      }
    }

    stage('Docker login') {
      steps {
        sh 'cat "$GCR_KEY" | docker login -u _json_key --password-stdin https://gcr.io'
      }
    }

    stage('Dev Docker Images') {
      options {
        timeout(time: params.BUILD_TIME_OUT, unit: 'MINUTES')
      }
      when {
        // Docker build need be performed in PUBLISH branch only
        expression { GIT_BRANCH_NAME == params.PUBLISH_BRANCH }
        expression { params.ENV_CHOICE == 'buildOnly' }
      }

      // Below variable is only set to load all (variables, functions) from jenkins_variables.groovy file.
      environment {
        deployment_env = 'dev'
        BUILD_MODULE_TO_INCLUDE = "${params.BUILD_MODULE_TO_INCLUDE}"
      }
      steps {
        load "./jenkins_variables.groovy"
        script {
          def servers = getDirs(pwd() + params.DEPLOYMENT_PATH)
          def frontendProjects = servers.findAll { it.startsWith('frontend-') }
          def otherProjects = servers - frontendProjects

          // Create parallel stages for non-frontend projects
          def parallelStagesMap = otherProjects.collectEntries {
            ["${it}" : generateBuildStage(it)]
          }

          // Run the first frontend project in parallel with others
          if (frontendProjects.size() > 0) {
            parallelStagesMap["${frontendProjects[0]}"] = generateBuildStage(frontendProjects[0])
            frontendProjects.remove(0)
          }

          // Run non-frontend projects in parallel
          parallel parallelStagesMap

          // Run remaining frontend projects sequentially
          frontendProjects.each {
            stage("${it}") {
                runBuildStage(it)
            }
          }
        }
      }
    }

    // Below are dev stages
    stage('Dev deployment') {
      environment {
        deployment_env = 'dev'
      }
      when {
        expression { GIT_BRANCH_NAME == params.PUBLISH_BRANCH || GIT_BRANCH_NAME == params.DEVELOP_BRANCH }
        expression { params.ENV_CHOICE == 'buildOnly' || params.ENV_CHOICE == 'devDeployOnly' }
        beforeInput true
      }

      steps {
        withKubeConfig([credentialsId: 'kubernetes-dev-cluster-r1', serverUrl: "https://34.74.64.165"]) {         
          sh """
            helm repo add stable https://charts.helm.sh/stable
            helm repo add incubator https://charts.helm.sh/incubator
            helm repo add kube-orchestration https://"""+ GITHUB_HELM_REPO_TOKEN +"""@raw.githubusercontent.com/cdmbase/kube-orchestration/develop/helm-packages
            helm repo update
          """
          script {
            nameSpaceCheck = sh(script: "kubectl get ns | tr '\\n' ','", returnStdout: true)
            if (!nameSpaceCheck.contains(env.NAMESPACE)) { sh "kubectl create ns " + env.NAMESPACE }

            def servers = getDirs(pwd() + params.DEPLOYMENT_PATH)
            def parallelStagesMap = servers.collectEntries {
              ["${it}" : generateStage(it, deployment_env)]
            }
            parallel parallelStagesMap
          }
        }
      }
    } // End of dev deployment code block.

    // Only master branch will be merged
    stage('Merge Develop to master & Install') {
      when {
        expression { GIT_BRANCH_NAME == params.MASTER_BRANCH }
        expression { params.ENV_CHOICE == 'stageDeploy' || params.ENV_CHOICE == 'prodDeploy' }
      }
      steps {
        sh """
          git add -A
          git diff --staged --quiet || git commit -am 'pre merge to master \r\n[skip ci]'
          git checkout ${params.REPOSITORY_BRANCH}
          git merge origin/${params.DEVELOP_BRANCH} -m 'auto merging ${params.DEVELOP_BRANCH} \r\n[skip ci]'
          ${params.BUILD_STRATEGY} install
        """
        script {
          GIT_BRANCH_NAME = params.REPOSITORY_BRANCH
        }
      }
    }

    // Run build for all cases except when ENV_CHOICE is 'buildAndPublish' and `dev`, `stage` or `prod`
    stage('Prod Build Packages') {
      when {
        expression { GIT_BRANCH_NAME == params.MASTER_BRANCH }
        expression { params.ENV_CHOICE == 'stageDeploy' || params.ENV_CHOICE == 'prodDeploy' }
      }
      steps {
        sh """
          ${params.BUILD_STRATEGY} run build
        """
      }
    }

    // publish packages to npm repository.
    // commit new package-lock.json that might get generated during install
    // Build will be ignore with tag '[skip ci]'
    stage('Prod Publish Packages') {
      when {
        expression { GIT_BRANCH_NAME == params.MASTER_BRANCH }
        expression { params.ENV_CHOICE == 'stageDeploy' || params.ENV_CHOICE == 'prodDeploy' }
      }
      steps {
        script {
          GIT_BRANCH_NAME = params.PUBLISH_BRANCH
        }
        sshagent(credentials: [params.GIT_CREDENTIAL_ID]) {
          sh """
            git add -A
            git diff --staged --quiet || git commit -am 'auto build [skip ci]\r\n'
            git fetch origin ${params.MASTER_BRANCH}
            git checkout ${params.MASTER_BRANCH}
            ${params.BUILD_STRATEGY} run publish:${params.NPM_PUBLISH_STRATEGY};
            git push origin ${params.MASTER_BRANCH}
            git checkout ${params.PUBLISH_BRANCH}
          """
        }
      }
    }

    // Build Docker containers for production.
    stage('Prod Docker Images') {
      options {
        timeout(time: params.BUILD_TIME_OUT, unit: 'MINUTES')
      }
      when {
        // required to be in Publish branch to build docker
        expression { GIT_BRANCH_NAME == params.PUBLISH_BRANCH }
        expression { params.ENV_CHOICE == 'stageDeploy' || params.ENV_CHOICE == 'prodDeploy' }
      }

      // Below variable is only set to load all (variables, functions) from jenkins_variables.groovy file.
      environment { deployment_env = 'prod' }
      steps {
        load "./jenkins_variables.groovy"
        script {
          def servers = getDirs(pwd() + params.DEPLOYMENT_PATH)
          def frontendProjects = servers.findAll { it.startsWith('frontend-') }
          def otherProjects = servers - frontendProjects

          // Create parallel stages for non-frontend projects
          def parallelStagesMap = otherProjects.collectEntries {
            ["${it}" : generateBuildStage(it)]
          }

          // First frontend project
          if (frontendProjects) {
            parallelStagesMap["${frontendProjects[0]}"] = generateBuildStage(frontendProjects[0])
            frontendProjects.remove(0)
          }

          // Run non-frontend projects in parallel
          parallel parallelStagesMap

          // Run remaining frontend projects sequentially
          frontendProjects.each {
            stage("${it}") {
              steps {
                runBuildStage(it)
              }
            }
          }
        }
      }
    } // End of production docker build.

    // Below are stage code block
    stage('Stage Deployment') {
      options {
        timeout(time: 300, unit: 'SECONDS')
      }
      environment {
        deployment_env = 'stage'
      }
      when {
        expression { GIT_BRANCH_NAME == params.MASTER_BRANCH || GIT_BRANCH_NAME == params.PUBLISH_BRANCH }
        expression { params.ENV_CHOICE == 'stageDeploy' || params.ENV_CHOICE == 'stageDeployOnly' }
        beforeInput true
      }

      steps {
        load "./jenkins_variables.groovy"
        withKubeConfig([credentialsId: 'kubernetes-staging-cluster', serverUrl: 'https://34.139.244.149']) {
          sh """
            helm repo add stable https://charts.helm.sh/stable
            helm repo add incubator https://charts.helm.sh/incubator
            helm repo add kube-orchestration https://"""+ GITHUB_HELM_REPO_TOKEN +"""@raw.githubusercontent.com/cdmbase/kube-orchestration/develop/helm-packages
            helm repo update
          """
          script {
            nameSpaceCheck = sh(script: "kubectl get ns | tr '\\n' ','", returnStdout: true)
            if (!nameSpaceCheck.contains(env.NAMESPACE)) { sh "kubectl create ns " + env.NAMESPACE }

            def servers = getDirs(pwd() + params.DEPLOYMENT_PATH)
            def parallelStagesMap = servers.collectEntries {
              ["${it}" : generateStage(it, deployment_env)]
            }
            parallel parallelStagesMap
          }
        }
      }
    } // End of staging deployment code block.

    stage('Release?') {
      when {
        expression { GIT_BRANCH_NAME == params.PUBLISH_BRANCH }
        expression { params.ENV_CHOICE == 'prodDeploy' || params.ENV_CHOICE == 'prodDeployOnly' }
        expression { params.SKIP_RELEASE_APPROVAL == false }
      }
      options {
        timeout time: 900, unit: 'SECONDS'
      }
      steps {
        slackSend (color: '#2596BE', message: "Approval Needed for Production Release:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  to be approved. Click <${env.RUN_DISPLAY_URL}|here> to approve it.", channel: 'idestack-automation')

        script {
          env.DO_RELEASE = input message: 'Want to deploy fullstack-pro on prod cluster?',
                                    parameters: [choice(choices: ['yes', 'no'], description: 'Deploy branch in Production?', name: 'PROD_DEPLOYMENT')]
        }
        milestone 1
      }
    }

    // Below are production stages
    stage('Prod Deployment') {
      options {
        timeout(time: 300, unit: 'SECONDS')
      }
      environment {
        deployment_env = 'prod'
      }
      when {
        // Only execute the step when the release has been approved or skipped in the deployment.
        expression { env.DO_RELEASE == 'yes' || params.SKIP_RELEASE_APPROVAL == true }
        expression { GIT_BRANCH_NAME == params.PUBLISH_BRANCH }
        expression { params.ENV_CHOICE == 'prodDeploy' || params.ENV_CHOICE == 'prodDeployOnly' }
      }

      steps {
        lock('release') {
          milestone 2
          load "./jenkins_variables.groovy"
          withKubeConfig([credentialsId: 'kubernetes-prod-cluster-r1', serverUrl: 'https://35.229.71.215']) {
            sh """
               helm repo add stable https://charts.helm.sh/stable
               helm repo add incubator https://charts.helm.sh/incubator
               helm repo add kube-orchestration https://"""+ GITHUB_HELM_REPO_TOKEN +"""@raw.githubusercontent.com/cdmbase/kube-orchestration/develop/helm-packages
               helm repo update
             """
            script {
              nameSpaceCheck = sh(script: "kubectl get ns | tr '\\n' ','", returnStdout: true)
              if (!nameSpaceCheck.contains(env.NAMESPACE)) { sh "kubectl create ns " + env.NAMESPACE }

              def servers = getDirs(pwd() + params.DEPLOYMENT_PATH)
              def parallelStagesMap = servers.collectEntries {
                ["${it}" : generateStage(it, deployment_env)]
              }
              parallel parallelStagesMap

              slackSend (color: '#2596BE', message: "Done:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  is completed. click <${env.RUN_DISPLAY_URL}|here> to see the log.", channel: 'idestack-automation')
            }
          }
        }
      }
    } // End of production deployment code block.
  }

  post {
    always {
      deleteDir()
    }
    success {
      slackSend (color: '#00FF00', message: "SUCCESSFUL:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  Job success. click <${env.RUN_DISPLAY_URL}|here> to see the log.", channel: 'idestack-automation')
    }
    failure {
      slackSend (color: '#FF0000', message: "FAILED:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  Job failed. click <${env.RUN_DISPLAY_URL}|here> to see the log.", channel: 'idestack-automation')
    }
  }
}

def getBuildCommand() {
  if (params.ENV_CHOICE == 'mobileBuild') {
    return 'build:auto'
  }
  if (params.ENV_CHOICE == 'mobilePreview') {
    return 'build:preview:' + params.MOBILE_CHOICE
  }
  if (params.ENV_CHOICE == 'mobilePreviewLocal') {
    return 'build:previewLocal:' + params.MOBILE_CHOICE
  }
  if (params.ENV_CHOICE == 'mobilePreviewSubmit') {
    return 'build:previewSubmit:' + params.MOBILE_CHOICE
  }
  if (params.ENV_CHOICE == 'mobileProd') {
    return 'build:prod:' + params.MOBILE_CHOICE
  }
  if (params.ENV_CHOICE == 'mobileProdSubmit') {
    return 'build:prodSubmit:' + params.MOBILE_CHOICE
  }
  if (params.ENABLE_DEBUG.toBoolean()) {
    return 'build:debug'
  } else {
    return 'build'
  }
}

def getGitPrBranchName() {
  if (env.ghprbSourceBranch) {
    return env.ghprbSourceBranch
  } else {
    return params.REPOSITORY_BRANCH
  }
}

def getGitBranchName() {
  if (env.ghprbSourceBranch) {
    return env.ghprbSourceBranch
  } else {
    return params.REPOSITORY_BRANCH
  }
}

@NonCPS
//TODO: Fix below get method for Jenkins slave if possible.
def getDirs1(path) {
  def currentDir = new File(path)
  def dirs = []
  currentDir.eachDir() {
    dirs << it.name
  }
  return dirs
}

// Below function to work in Jenkins slave
def getDirs(path) {
  def currentDir = sh(script: "ls -CF " + path + " | tr '/' ' '", returnStdout: true)
  def dirs = []
  (currentDir.split()).each {
    dirs << "${it}"
  }
  return dirs
}

def generateStage(server, environmentType) {
  return {
    stage("stage: ${server}") {
      echo "This is ${server}."
      def filterExist = "${server}".contains(params.EXCLUDE_SETTING_NAMESPACE_FILTER)
      def namespace = filterExist ? '' : "--namespace=${env.NAMESPACE}"
      def name = getName(pwd() + "${params.DEPLOYMENT_PATH}/${server}/package.json")
      def version = getVersion(pwd() + params.DEPLOYMENT_PATH + "/${server}/package.json")
      def valuesFile = "values-${environmentType}.yaml"
      // deploy anything matching `*backend-server` or `*frontend-server` to use idestack chart
      try {
        if ("${server}".endsWith("backend-server") | "${server}".endsWith("frontend-server")) {
          echo "add deployment flag to - ${server} "

          if ("${server}".endsWith("frontend-server")) {
            deployment_flag = " --set backend.enabled='false' --set external.enabled='true'"
          }

          if ("${server}".endsWith("backend-server")) {
            deployment_flag = " --set frontend.enabled='false' --set external.enabled='false' --set ingress.enabled=false "
          }

          sh """
            helm upgrade -i \
            ${server} \
            -f "${valuesFile}" \
            ${namespace} \
            ${deployment_flag} \
            --set frontend.image="${REPOSITORY_SERVER}/${name}" \
            --set frontend.imageTag=${version} \
            --set backend.image="${REPOSITORY_SERVER}/${name}" \
            --set backend.imageTag=${version} \
            --set settings.workspaceId="${WORKSPACE_ID}" \
            --set frontend.pullPolicy=Always \
            --set backend.pullPolicy=Always \
            --set VERSION=${VERSION} \
            --version=${IDESTACK_CHART_VERSION} \
              kube-orchestration/idestack
          """
        } else {
          sh """
            cd .${params.DEPLOYMENT_PATH}/${server}
            helm dependency update charts/chart/
            helm upgrade -i \
            ${server}-api \
            -f "charts/chart/${valuesFile}" \
            ${namespace} \
            --set global.image.repository=${REPOSITORY_SERVER}/${name} \
            --set global.image.tag=${version} \
            --set VERSION=${VERSION} \
            charts/chart
          """
        }
      } catch (Exception err) {
        slackSend (color: '#FF0000', message: "FAILED:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  Job failed in stage deployment ${server}. click <${env.RUN_DISPLAY_URL}|here> to see the log. Error: ${err.toString()}", channel: 'idestack-automation')
        println err
        throw(err)
      }
    }
  }
}

// Docker build parllel loop
def generateBuildStage(server) {
  return {
    stage("stage: ${server}") {
      try {
        echo "This is ${server}."
        def name = getName(pwd() + params.DEPLOYMENT_PATH + "/${server}/package.json")
        def version = getVersion(pwd() + params.DEPLOYMENT_PATH + "/${server}/package.json")
        
        buildAndPushDockerImage(server, name, version)
      } catch (e) {
        slackSend(color: '#FF0000', message: "FAILED:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  Job failed in stage docker-build ${server}. click <${env.RUN_DISPLAY_URL}|here> to see the log. Error: ${e}", channel: 'idestack-automation')
        throw(e)
      }
    }
  }
}

def runBuildStage(server) {
  try {
    echo "This is ${server}."
    def name = getName(pwd() + params.DEPLOYMENT_PATH + "/${server}/package.json")
    def version = getVersion(pwd() + params.DEPLOYMENT_PATH + "/${server}/package.json")
    
    buildAndPushDockerImage(server, name, version)
  } catch (e) {
    slackSend(color: '#FF0000', message: "FAILED:  Job  '${env.JOB_NAME}'  BUILD NUMBER:  '${env.BUILD_NUMBER}'  Job failed in stage docker-build ${server}. click <${env.RUN_DISPLAY_URL}|here> to see the log. Error: ${e}", channel: 'idestack-automation')
    throw(e)
  }
}

def buildAndPushDockerImage(server, name, version) {
  def imageExists = sh(script: "docker manifest inspect ${REPOSITORY_SERVER}/${name}:${version} > /dev/null 2>&1 && echo 'true' || echo 'false'", returnStdout: true).trim()

  if (imageExists == 'true') {
    echo "Docker image ${REPOSITORY_SERVER}/${name}:${version} already exists. Skipping build."
  } else {
    sh """
      npx lerna exec --scope=*${server} ${params.BUILD_STRATEGY} run ${env.BUILD_COMMAND};
      npx lerna exec --scope=*${server} ${params.BUILD_STRATEGY} copycommon
      docker buildx create --name ${server} --driver docker-container --use
      docker buildx inspect ${server} --bootstrap

      docker buildx build --progress=plain --builder=${server} \
          --cache-to type=local,dest=/global-docker-cache/${name},mode=max \
          --cache-from type=local,src=/global-docker-cache/${name} \
          --push servers/${server} -t ${REPOSITORY_SERVER}/${name}:${version}
    """
  }
}

import groovy.json.JsonSlurper
def getVersion(json_file_path) {
  def inputFile = readFile(json_file_path)
  def InputJSON = new JsonSlurper().parseText(inputFile)
  def version = InputJSON.version
  return version
}

def getName(json_file_path) {
  def inputFile = readFile(json_file_path)
  def InputJSON = new JsonSlurper().parseText(inputFile)
  def name = InputJSON.name
  return name
} 