@Library('jenkins-helper') _

pipeline {
  agent none
  parameters {
    string(name: 'RELEASE', description: 'Release numeric name (e.g. 2026.10.01)', trim: true)
    string(name: 'REDMINE_VERSION', description: 'The release note will be based on the tickets tagged with the given target version.', trim: true)
    string(name: 'RELEASE_NUMERIC_PREFIX', description: """
The numeric prefix of the release (like 2018.16).
For example for:
- for Gaia.18 it is 2021.07
- for 2022.06 it is 2022.06
- for Helios.05 it is 2021.15
- ...
""", trim: true)
    choice(name: 'RELEASE_TYPE', choices: ['IV', 'BUGFIX'], description: 'Select the type of release. IV promotes every docker image, BUGFIX only the ones of DOCKER_LIST.')
    booleanParam(name: 'SKIP_RELEASE_NOTES', defaultValue: false, description: 'Skip the release-note job. Requires DOCKER_LIST and PACKAGER_LIST to be set below.')
    string(name: 'DOCKER_LIST', defaultValue: '', description: """
List of projects with docker images to promote for this release separated with , (e.g. xucserver,xivo-agid,edge-coturn).

Only read when RELEASE_TYPE is BUGFIX: an IV promotes every image of resources/docker-images.json.

Optional. Overrides the docker project list normally read from the release-note job.
Set it when rerunning a partially failed build, so release-note does not need to run again.
The project lists of every run are printed in the 'resolve-projects' stage, ready to be copied here.
""", trim: true)
    string(name: 'PACKAGER_LIST', defaultValue: '', description: """
List of projects with debian package(s) (packagers) to release for this release separated with , (e.g. xivo,xivo-confgend,xivo-sysconfd).

Optional. Overrides the debian project list normally read from the release-note job.
""", trim: true)
    string(name: 'LTS_CODENAME', description: 'The production release name (eg. kuma)', trim: true)
    booleanParam(name: 'DO_TECHNICAL_RELEASE', defaultValue: true, description: 'Publish xivo-<X.Y>-latest on the archive repo')
    booleanParam(name: 'DO_PROD_RELEASE', defaultValue: true, description: 'Execute the production release on debian repo')
    booleanParam(name: 'SKIP_DOCKER', defaultValue: false, description: 'Skip the docker promotion stage (it already completed in a previous run).')
    booleanParam(name: 'SKIP_DEBIAN', defaultValue: false, description: 'Skip the debian release stage (it already completed in a previous run).')
  }
  stages {
    stage('check-parameters') {
      agent any
      steps {
        script {
          if (!(params.RELEASE ==~ /[0-9]{4}\.[0-9]{2}\.[0-9]{2}/)) {
            error "RELEASE must be a numeric release like 2026.10.01, got '${params.RELEASE}'."
          }
          if (!params.SKIP_DEBIAN && params.DO_PROD_RELEASE && !params.LTS_CODENAME) {
            error 'LTS_CODENAME is required by the prod-release stage: it names the repository to publish to.'
          }
          echo "PROD build for XiVO ${params.RELEASE} (${params.LTS_CODENAME}), release type ${params.RELEASE_TYPE}"
        }
      }
    }
    stage('build-release-notes') {
      when {
        expression { !params.SKIP_RELEASE_NOTES }
      }
      agent any
      steps {
        build(
          job: 'release-note',
          parameters: [
            string(name: 'REDMINE_VERSION', value: "${params.REDMINE_VERSION}"),
            string(name: 'RELEASE_NUMERIC_PREFIX', value: "${params.RELEASE_NUMERIC_PREFIX}")],
          wait: true
        )
      }
    }
    stage('resolve-projects') {
      agent any
      steps {
        script {
          def fromFile = [:]
          if (params.SKIP_RELEASE_NOTES) {
            if (!params.DOCKER_LIST && !params.PACKAGER_LIST) {
              error "SKIP_RELEASE_NOTES requires at least one project list to be given as a parameter."
            }
          } else {
            fromFile = readProperties file: '/var/lib/jenkins/workspace/release-note/TAGGED_PROJECTS'
          }

          env.DOCKER_PROJECTS = params.DOCKER_LIST ?: "${fromFile['DOCKER_PROJECTS'] ?: ''}"
          env.DEBIAN_PROJECTS = params.PACKAGER_LIST ?: "${fromFile['DEBIAN_PROJECTS'] ?: ''}"

          echo """Project lists for this release. Copy them as parameters to rerun this build without release-note:
- DOCKER_LIST=${env.DOCKER_PROJECTS}
- PACKAGER_LIST=${env.DEBIAN_PROJECTS}"""
        }
      }
    }
    stage('promote-docker-images') {
      agent any
      when {
        expression { !params.SKIP_DOCKER }
      }
      steps {
        script {
          switch (params.RELEASE_TYPE) {
            case 'BUGFIX':
              promoteListedImages()
              break
            case 'IV':
              build(
                job: 'build-all-docker-images',
                parameters: [
                  string(name: 'BRANCH_OR_TAG', value: "${params.RELEASE}-rc"),
                  string(name: 'BUILD_MODE', value: 'prod'),
                  string(name: 'RELEASE', value: "${params.RELEASE}")
                ]
              )
              break
            default:
              error "Invalid RELEASE_TYPE: ${params.RELEASE_TYPE}"
          }
        }
      }
    }
    stage('release-debian-packages') {
      agent any
      when {
        expression { !params.SKIP_DEBIAN }
      }
      steps {
        script {
          def packagers = env.DEBIAN_PROJECTS.split(',').collect { it.trim() }.findAll { it && it != 'asterisk' }

          if (!packagers) {
            packagers = ['xivo']
            echo "No debian project to release: adding 'xivo' anyway to have the debian part released."
          }

          build(
            job: 'debian-packages-prod-build-process',
            parameters: [
              string(name: 'RELEASE', value: "${params.RELEASE}"),
              string(name: 'PACKAGER_LIST', value: "${packagers.join(',')}"),
              string(name: 'LTS_CODENAME', value: "${params.LTS_CODENAME}"),
              booleanParam(name: 'DO_TECHNICAL_RELEASE', value: params.DO_TECHNICAL_RELEASE),
              booleanParam(name: 'DO_PROD_RELEASE', value: params.DO_PROD_RELEASE)
            ]
          )
        }
      }
    }
  }
}

void promoteListedImages() {
  def projects = env.DOCKER_PROJECTS.split(',').collect { it.trim() }.findAll { it }

  if (!projects) {
    echo 'No docker image to promote. Skipping stage.'
    return
  }

  echo "Now build job will be ran for each docker image: ${projects.join(',')}."
  echo 'And they will be launched with these parameters:'
  echo "- BRANCH_OR_TAG=${params.RELEASE}-rc"
  echo '- BUILD_MODE=prod'

  def alreadyPromoted = []
  def promoted = []
  def failed = []

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

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

    if (jobStatus.result == 'SUCCESS' || jobStatus.result == 'UNSTABLE') {
      echo "Job for ${project} was successful."
      promoted << project
    } else {
      echo "Build job for ${project} has failed."
      failed << project
    }
  }

  echo """Docker promotion summary for ${params.RELEASE}:
- promoted now: ${promoted.join(', ') ?: 'none'}
- already promoted: ${alreadyPromoted.join(', ') ?: 'none'}
- failed: ${failed.join(', ') ?: 'none'}"""

  if (failed) {
    error "Docker promotion incomplete. Rerun this job with DOCKER_LIST=${failed.join(',')}"
  }

  echo 'All docker images are promoted.'
}
