name: Make Release

on:
  workflow_dispatch:
    inputs:
      commit:
        description: 'Commit SHA to release (empty = branch HEAD)'
        required: false
        default: ''
        type: string
      dry_run:
        description: 'Dry run - validate without creating the tag'
        required: true
        type: boolean
        default: true
      skip_apiabi_check:
        description: 'Skip API/ABI compatibility check'
        required: false
        type: boolean
        default: false
      require_docker:
        description: 'Require the Docker workflow to have completed successfully'
        required: true
        type: boolean
        default: true
      apiabi_compare_tag:
        description: 'Tag to compare against for API/ABI check (default: latest release)'
        required: false
        type: string
        default: ''

env:
  GH_TOKEN: ${{ github.token }}

cache-mode: none
permissions:
  contents: write
  packages: write

jobs:
  make-release:
    runs-on: ubuntu-24.04  # previously ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ssh-key: ${{ secrets.DEPLOY_KEY_RELEASE }}
          ref: ${{ inputs.commit != '' && inputs.commit || github.ref_name }}
          fetch-depth: 0

      - name: Install API/ABI check tools
        if: ${{ github.event.inputs.skip_apiabi_check != 'true' }}
        run: sudo apt-get install -y abi-compliance-checker abigail-tools

      - name: Run release checks
        id: checks
        run: bash scripts/make-release-checks.sh ${{ github.event.inputs.dry_run == 'true' && '--dry-run' || '' }}
        env:
          GITHUB_REPOSITORY: ${{ github.repository }}
          RELEASE_BRANCH: ${{ github.ref_name }}
          SKIP_APIABI_CHECK: ${{ github.event.inputs.skip_apiabi_check }}
          APIABI_COMPARE_TAG: ${{ github.event.inputs.apiabi_compare_tag }}
          REQUIRE_DOCKER: ${{ github.event.inputs.require_docker }}

      - name: Create release tag
        if: ${{ github.event.inputs.dry_run == 'false' }}
        run: |
          VERSION="${{ steps.checks.outputs.version }}"
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git tag -a "${VERSION}" -m "Release ${VERSION}"
          git push origin "${VERSION}"
          echo "Created and pushed tag ${VERSION}"

      - name: Generate release description
        id: desc
        run: bash scripts/make-release-desc.sh "${{ steps.checks.outputs.version }}"
        env:
          GITHUB_REPOSITORY: ${{ github.repository }}

      - name: Create nightly-tag.txt
        id: nightly_tag_file
        run: |
          NIGHTLY_TAG="${{ steps.desc.outputs.nightly_tag }}"
          if [[ -z "${NIGHTLY_TAG}" ]]; then
            echo "Warning: no nightly tag found for the release commit - nightly-tag.txt will not be created"
            echo "create=false" >> "$GITHUB_OUTPUT"
            exit 0
          fi
          echo "${NIGHTLY_TAG}" > nightly-tag.txt
          echo "create=true" >> "$GITHUB_OUTPUT"
          echo "nightly-tag.txt:"
          cat nightly-tag.txt

      - name: Create release
        id: create_release
        if: ${{ github.event.inputs.dry_run == 'false' }}
        uses: ggml-org/action-create-release@v1
        env:
          GITHUB_TOKEN: ${{ github.token }}
        with:
          tag_name: ${{ steps.checks.outputs.version }}
          prerelease: false
          # TODO: enrich the body of the release with more information
          body: |
            ## Overview

            New version has been released.

            ## Assets

            ${{ steps.desc.outputs.nightly }}

            ## More info

            - [Releases and versioning of `ggml-org` projects](https://github.com/ggml-org/ggml/discussions/1579)

            ## ${{ steps.desc.outputs.changelog_title }}

            ${{ steps.desc.outputs.changelog }}

      - name: Upload nightly-tag.txt
        if: ${{ github.event.inputs.dry_run == 'false' && steps.nightly_tag_file.outputs.create == 'true' }}
        uses: actions/github-script@v8
        with:
          github-token: ${{secrets.GITHUB_TOKEN}}
          script: |
            const fs = require('fs');
            const release_id = '${{ steps.create_release.outputs.id }}';
            console.log('uploadReleaseAsset', 'nightly-tag.txt');
            await github.rest.repos.uploadReleaseAsset({
              owner: context.repo.owner,
              repo: context.repo.repo,
              release_id: release_id,
              name: 'nightly-tag.txt',
              data: await fs.readFileSync('./nightly-tag.txt')
            });

      - name: Re-tag container images with release version
        if: ${{ github.event.inputs.dry_run == 'false' && github.event.inputs.require_docker != 'false' && steps.desc.outputs.nightly_tag != '' }}
        env:
          GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }}
        run: |
          VERSION="${{ steps.checks.outputs.version }}"
          NIGHTLY_TAG="${{ steps.desc.outputs.nightly_tag }}"
          REPO_OWNER="${GITHUB_REPOSITORY_OWNER,,}"
          IMAGE_REPO="ghcr.io/${REPO_OWNER}/${{ github.event.repository.name }}"

          echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u "${{ github.actor }}" --password-stdin

          VARIANTS=("" "-cuda" "-cuda13" "-vulkan" "-rocm" "-intel" "-musa" "-openvino")
          TYPES=("full" "light" "server")
          # the release is already created at this point, so keep going on a
          # missing image and report all of them at the end
          MISSING=()
          for type in "${TYPES[@]}"; do
            for variant in "${VARIANTS[@]}"; do
              src="${IMAGE_REPO}:${type}${variant}-${NIGHTLY_TAG}"
              dst="${IMAGE_REPO}:${type}${variant}-${VERSION}"
              echo "Tagging ${src} -> ${dst}"
              if ! docker buildx imagetools create --tag "${dst}" "${src}"; then
                MISSING+=("${type}${variant}")
              fi
            done
          done
          if [[ ${#MISSING[@]} -gt 0 ]]; then
            echo "::error::failed to re-tag container images for ${NIGHTLY_TAG}:${MISSING[*]}"
            exit 1
          fi

      - name: Dry run summary
        if: ${{ github.event.inputs.dry_run == 'true' }}
        run: |
          if [[ "${{ steps.checks.outputs.checks_passed }}" == "true" ]]; then
            echo "Dry run complete - all checks passed."
            echo "Would have created tag: ${{ steps.checks.outputs.version }}"
            if [[ -n "${{ steps.desc.outputs.nightly_tag }}" ]]; then
              echo "Would have uploaded nightly-tag.txt: ${{ steps.desc.outputs.nightly_tag }}"
            fi
          else
            echo "::error::Dry run found release check failures. A release tag would not be created."
            exit 1
          fi
