library(params.LIBRARY_VERSION ? "jenkins-helper@${params.LIBRARY_VERSION}" : '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')
    string(name: 'LTS_BRANCH', defaultValue: '', description: 'Branch of xivo-solutions-doc the release note is published on, and documentation version the announcement links to (e.g. 2026.10, or master).', trim: true)
    booleanParam(name: 'SKIP_DOCUMENTATION', defaultValue: false, description: 'Skip publishing the release note to xivo-solutions-doc (it already completed in a previous run).')
    string(name: 'REDMINE_URL', defaultValue: 'https://projects.xivo.solutions', description: 'Base URL of the Redmine instance.', trim: true)
    booleanParam(name: 'SKIP_ANNOUNCEMENT', defaultValue: false, description: 'Skip the Redmine announcement and the Matrix message (BUGFIX only).')
    string(name: 'ANNOUNCEMENT_PROJECT', defaultValue: 'xivo-community', description: 'Redmine project the release announcement is published on.', trim: true)
    string(name: 'MATRIX_ROOM_ID', defaultValue: '', description: 'Matrix room the announcement is posted to. Leave empty to skip the Matrix message.', trim: true)
    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).')
    string(name: 'LIBRARY_VERSION', defaultValue: '', description: 'Optional. To test pipeline against a dev version of the jenkins-helper library. Fill in with Branch or tag of the jenkins-helper library (xivo-ci) to load. Leave empty for the default version (master)', trim: true)
  }
  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)
            ]
          )
        }
      }
    }
    stage('publish-documentation') {
      when {
        expression { !params.SKIP_DOCUMENTATION }
      }
      agent any
      steps {
        script {
          if (!params.LTS_BRANCH) {
            error 'LTS_BRANCH is required by the publish-documentation stage: it names the xivo-solutions-doc branch to publish to.'
          }

          def components = (env.DOCKER_PROJECTS.split(',') + env.DEBIAN_PROJECTS.split(','))
            .collect { it.trim() }
            .findAll { it }

          releaseDocumentation(
            releaseType: params.RELEASE_TYPE,
            ltsBranch: params.LTS_BRANCH,
            ltsCodename: params.LTS_CODENAME,
            redmineVersion: params.REDMINE_VERSION,
            release: params.RELEASE,
            releaseNote: readFile("/var/lib/jenkins/workspace/release-note/release_note_${params.REDMINE_VERSION}.rst"),
            components: components
          )
        }
      }
    }
    stage('announce-release') {
      when {
        expression { !params.SKIP_ANNOUNCEMENT && params.RELEASE_TYPE == 'BUGFIX' }
      }
      agent any
      steps {
        withCredentials([
          string(credentialsId: 'REDMINE_API_KEY', variable: 'REDMINE_API_KEY'),
          string(credentialsId: 'matrix-rd-bot', variable: 'MATRIX_TOKEN')
        ]) {
          script {
            if (!params.MATRIX_ROOM_ID) {
              error 'MATRIX_ROOM_ID is required by the announce-release stage: it names the room the announcement is posted to.'
            }
            if (!params.LTS_BRANCH) {
              error 'LTS_BRANCH is required by the announce-release stage: it names the documentation version the announcement links to.'
            }

            releaseAnnouncement(
              version: params.REDMINE_VERSION,
              ltsBranch: params.LTS_BRANCH,
              redmineUrl: params.REDMINE_URL,
              projectId: params.ANNOUNCEMENT_PROJECT,
              matrixRoomId: params.MATRIX_ROOM_ID,
              releaseNote: readFile("/var/lib/jenkins/workspace/release-note/release_note_${params.REDMINE_VERSION}.rst")
            )
          }
        }
      }
    }
  }
}

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.'
}
