# This workflow runs after a merge to any release branch of the action. It:
# 1. Tags the merge commit on the release branch that represents the new release with an `vN.x.y`
#    tag
# 2. Updates the `vN` tag to refer to this merge commit.
# 3. Iff vN == vLatest, merges any changes from the release back into the main branch.
#    Typically, this is two commits – one to update the version number and one to rebuild.
name: Tag release and merge back

on:
  workflow_dispatch:
    inputs:
      baseBranch:
        description: 'The base branch to merge into'
        default: main
        required: false

  push:
    branches:
      - releases/v*

defaults:
  run:
    shell: bash

jobs:
  merge-back:
    runs-on: ubuntu-latest
    environment: Automation
    if: github.repository == 'github/codeql-action'
    env:
      BASE_BRANCH: "${{ github.event.inputs.baseBranch || 'main' }}"
      HEAD_BRANCH: "${{ github.head_ref || github.ref }}"

    permissions:
      contents: write # needed to create tags and push commits
      pull-requests: write

    steps:
      - name: Dump environment
        run: env

      - name: Dump GitHub context
        env:
          GITHUB_CONTEXT: '${{ toJson(github) }}'
        run: echo "${GITHUB_CONTEXT}"

      - uses: actions/checkout@v6
        with:
          fetch-depth: 0  # ensure we have all tags and can push commits
      - uses: actions/setup-node@v6
        with:
          node-version: 24
          cache: 'npm'
      - uses: actions/setup-python@v6
        with:
          python-version: '3.12'

      - name: Update git config
        run: |
          git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git config --global user.name "github-actions[bot]"

      - name: Get version and new branch
        id: getVersion
        run: |
          VERSION="v$(jq '.version' -r 'package.json')"
          echo "version=${VERSION}" >> $GITHUB_OUTPUT
          short_sha="${GITHUB_SHA:0:8}"
          NEW_BRANCH="mergeback/${VERSION}-to-${BASE_BRANCH}-${short_sha}"
          echo "newBranch=${NEW_BRANCH}" >> $GITHUB_OUTPUT
          LATEST_RELEASE_BRANCH=$(git branch -r | grep -E "origin/releases/v[0-9]+$" | sed 's/origin\///g' | sort -V | tail -1 | xargs)
          echo "latest_release_branch=${LATEST_RELEASE_BRANCH}" >> $GITHUB_OUTPUT

      - name: Dump branches
        env:
          NEW_BRANCH: "${{ steps.getVersion.outputs.newBranch }}"
        run: |
          echo "BASE_BRANCH ${BASE_BRANCH}"
          echo "HEAD_BRANCH ${HEAD_BRANCH}"
          echo "NEW_BRANCH ${NEW_BRANCH}"
          echo "LATEST_RELEASE_BRANCH ${LATEST_RELEASE_BRANCH}"
          echo "GITHUB_REF ${GITHUB_REF}"

      - name: Create mergeback branch
        env:
          NEW_BRANCH: "${{ steps.getVersion.outputs.newBranch }}"
        run: |
          git checkout -b "${NEW_BRANCH}"

      - name: Check for tag
        id: check
        env:
          VERSION: "${{ steps.getVersion.outputs.version }}"
        run: |
          set +e # don't fail on an errored command
          git ls-remote --tags origin | grep "${VERSION}"
          exists="$?"
          if [ "${exists}" -eq 0 ]; then
            echo "Tag ${VERSION} exists. Not going to re-release."
            echo "exists=true" >> $GITHUB_OUTPUT
          else
            echo "Tag ${VERSION} does not exist yet."
          fi

      # we didn't tag the release during the update-release-branch workflow because the
      # commit that actually makes it to the release branch is a merge commit,
      # and not yet known during the first workflow. We tag now because we know the correct commit.
      - name: Tag release
        if: steps.check.outputs.exists != 'true'
        env:
          VERSION: ${{ steps.getVersion.outputs.version }}
        run: |
          # Create the `vx.y.z` tag
          git tag --annotate "${VERSION}" --message "${VERSION}"
          # Update the `vx` tag
          major_version_tag=$(cut -d '.' -f1 <<< "${VERSION}")
          # Use `--force` to overwrite the major version tag
          git tag --annotate "${major_version_tag}" --message "${major_version_tag}" --force
          # Push the tags, using:
          # - `--atomic` to make sure we either update both tags or neither (an intermediate state,
          #   e.g. where we update the vN.x.y tag on the remote but not the vN tag, could result in
          #   unwanted Dependabot updates, e.g. from vN to vN.x.y)
          # - `--force` since we're overwriting the `vN` tag
          git push origin --atomic --force refs/tags/"${VERSION}" refs/tags/"${major_version_tag}"

      - name: Prepare partial Changelog
        env:
          PARTIAL_CHANGELOG: "${{ runner.temp }}/partial_changelog.md"
        run: |
          python .github/workflows/script/prepare_changelog.py CHANGELOG.md > $PARTIAL_CHANGELOG

          echo "::group::Partial CHANGELOG"
          cat $PARTIAL_CHANGELOG
          echo "::endgroup::"

      - name: Generate token
        uses: actions/create-github-app-token@v3.2.0
        id: app-token
        with:
          app-id: ${{ vars.AUTOMATION_APP_ID }}
          private-key: ${{ secrets.AUTOMATION_PRIVATE_KEY }}

      - name: Create the GitHub release
        if: steps.check.outputs.exists != 'true'
        env:
          PARTIAL_CHANGELOG: "${{ runner.temp }}/partial_changelog.md"
          VERSION: "${{ steps.getVersion.outputs.version }}"
          GH_TOKEN: ${{ steps.app-token.outputs.token }}
        run: |
          # Do not mark this release as latest. The most recent CLI release must be marked as latest.
          gh release create \
            "$VERSION" \
            --latest=false \
            --title "$VERSION" \
            --notes-file "$PARTIAL_CHANGELOG"

      - name: Create mergeback branch and PR
        if: ${{ endsWith(github.ref_name, steps.getVersion.outputs.latest_release_branch) }}
        uses: ./.github/actions/prepare-mergeback-branch
        with:
          base: "${{ env.BASE_BRANCH }}"
          head: "${{ env.HEAD_BRANCH }}"
          branch: "${{ steps.getVersion.outputs.newBranch }}"
          version: "${{ steps.getVersion.outputs.version }}"
          token: "${{ secrets.GITHUB_TOKEN }}"
