// A completed rc build tags the project <RELEASE>-rc, and build_rc refuses to run
// again while that tag exists. It is therefore the authoritative record of "already
// built", and lets the pipeline be rerun after a partial failure. The docker registry
// cannot answer this: an rc build only pushes the floating <MAJOR>.<MINOR>.latestrc
// tag, never the exact release, so inspecting <repository>:<release> reports every
// project as missing.
//
// The tag is read over SSH rather than the GitLab REST API: the build agents reach
// gitlab.com through the git SSH transport, and HTTPS calls to the API time out.
// ls-remote exits 0 with an empty output when the tag does not exist, and non-zero
// when the repository cannot be reached at all. Only the first case means "not built
// yet": an unreachable repository is reported so a network problem is not mistaken
// for a list of projects left to rebuild.
def rcTagExists(project, release) {
  def tagRef = "refs/tags/${release}-rc"

  def result = sh(
    script: "git ls-remote --tags git@gitlab.com:xivo.solutions/${project}.git '${tagRef}' 2>/dev/null; echo \"exit=\$?\"",
    returnStdout: true
  ).trim()

  if (!result.endsWith('exit=0')) {
    echo "Could not read the tags of ${project} (${result.split('\n').last()}), will attempt the build."
    return false
  }

  return result.contains(tagRef)
}

pipeline {
  agent none
  parameters {
    string(name: 'RELEASE', description: 'Release numeric name (e.g. 2022.10.01)', trim: true)
    string(name: 'DOCKER_LIST', description: 'List of projects with docker images to build for this release separated with , (e.g. xucserver,xivo-agid,edge-coturn)', trim: true)
    string(name: 'LTS_BRANCH', description: 'The name of the LTS branch (e.g. 2022.10, 2023.10, or master)', trim: true)
  }
  stages {
    stage('build-docker-images') {
      agent any
      steps {
        echo "Now build job will be ran for each docker image: ${DOCKER_LIST}."
        echo "And they will be launched with these parameters:"
        echo "- TAG_OR_BRANCH=${LTS_BRANCH}"
        echo "- BUILD_MODE=rc"
        script {
          if (!params.RELEASE) {
            error 'RELEASE is required: it is the version the already-tagged check compares against.'
          }

          def projects = "$DOCKER_LIST".split(",").collect { it.trim() }.findAll { it }

          def alreadyBuilt = []
          def built = []
          def failed = []
          def notBumped = []

          projects.each { project ->
            if (rcTagExists(project, params.RELEASE)) {
              echo "${project} is already tagged ${params.RELEASE}-rc, skipping."
              alreadyBuilt << project
              return
            }

            def jobName = "${project}-docker-auto-v2"
            def jobStatus = build job: jobName,
            propagate: false,
            parameters: [
              string(name: 'BRANCH_OR_TAG', value: "$LTS_BRANCH" ),
              string(name: 'BUILD_MODE', value: 'rc')
            ]

            if (jobStatus.result == 'SUCCESS' || jobStatus.result == 'UNSTABLE') {
              echo "Job for ${project} was successful."
              built << project
              return
            }

            def buildLog = sh(
              script: "curl -s -X GET http://localhost:8080/job/${jobName}/${jobStatus.number}/consoleText",
              returnStdout: true
            )
            if (buildLog.contains("${params.RELEASE}-rc is already tagged, cannot continue.") ||
                buildLog.contains("${params.RELEASE} is already tagged, cannot continue.")) {
              echo "Build job for ${project} has been already ran."
              alreadyBuilt << project
            } else if (buildLog.contains("is already tagged, cannot continue.")) {
              echo "Build job for ${project} failed: TARGET_VERSION is not bumped to ${params.RELEASE}."
              notBumped << project
            } else {
              echo "Build job for ${project} has failed."
              failed << project
            }
          }

          echo """Docker build summary for ${params.RELEASE}:
- built now: ${built.join(', ') ?: 'none'}
- already built: ${alreadyBuilt.join(', ') ?: 'none'}
- TARGET_VERSION not bumped: ${notBumped.join(', ') ?: 'none'}
- failed: ${failed.join(', ') ?: 'none'}"""

          if (notBumped || failed) {
            error "Docker build incomplete. Rerun this job with the same parameters to retry only: ${(notBumped + failed).join(',')}"
          }

          echo "All docker images are built."
        }
      }
    }
  }
}
