---
name: Changelog & versions
"on":
  workflow_call:
  workflow_dispatch:
  schedule:
    # Run daily at 6:00 UTC for bump-versions job.
    - cron: "0 6 * * *"
  push:
    branches:
      - main
    paths:
      - changelog.md
      - "**/pyproject.toml"
      # Trigger on any workflow change to make sure version gets hard-coded everywhere.
      - .github/workflows/*.yaml
      # Trigger on lockfile changes so bump-versions recreates its PRs before they conflict.
      - uv.lock
  # Trigger after release workflow completes to ensure tags exist before bump-versions.
  # This avoids race conditions where changelog workflow checks for tags before they're pushed.
  workflow_run:
    workflows:
      - "Build & release"
    types:
      - completed
    branches:
      - main

concurrency:
  # Include event_name so workflow_run events (from "Build & release" completing)
  # cannot cancel push-triggered runs. Without this, the fast-completing
  # "Build & release" fires workflow_run before prepare-release finishes,
  # killing it via the shared concurrency group.
  group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
  cancel-in-progress: true

jobs:

  project-metadata:
    name: Project metadata
    # Run on schedule, manual dispatch, push, or after release workflow completes successfully.
    # Push events keep bump-version PRs conflict-free by recreating them on every main change.
    if: >
      github.event_name == 'schedule' ||
      github.event_name == 'workflow_dispatch' ||
      github.event_name == 'push' ||
      (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success')
    runs-on: ubuntu-slim
    outputs:
      # Bump eligibility to prevent double version increments within a development cycle.
      minor_bump_allowed: ${{ steps.project-metadata.outputs.minor_bump_allowed }}
      major_bump_allowed: ${{ steps.project-metadata.outputs.major_bump_allowed }}
    steps:
      - uses: actions/checkout@v6.0.2
        with:
          # github.sha resolves correctly for all trigger types: push (pushed commit),
          # schedule/workflow_dispatch/workflow_run (latest commit on default branch).
          # Do NOT use workflow_run.head_sha — it points to the commit that triggered
          # "Build & release", which may be stale after the release cycle adds commits.
          ref: ${{ github.sha }}
          # Fetch all tags so we can compare against the latest release.
          fetch-tags: true
      - uses: astral-sh/setup-uv@v7.2.1
      - name: Run gha-utils metadata
        id: project-metadata
        run: uvx --no-progress 'gha-utils==5.10.1' metadata --output "$GITHUB_OUTPUT"

  bump-versions:
    needs:
      - project-metadata
    # Always run since project-metadata already filters appropriate events.
    runs-on: ubuntu-slim
    strategy:
      matrix:
        part:
          - minor
          - major
    # The condition must be repeated on each step because:
    # 1. Job-level `if:` is evaluated before matrix expansion, so `matrix.*` isn't available there
    # 2. GitHub Actions lacks conditional step groups to skip multiple steps with one condition
    steps:
      - uses: actions/checkout@v6.0.2
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
        with:
          # github.sha resolves correctly for all trigger types: push (pushed commit),
          # schedule/workflow_dispatch/workflow_run (latest commit on default branch).
          # Do NOT use workflow_run.head_sha — it points to the commit that triggered
          # "Build & release", which may be stale after the release cycle adds commits.
          ref: ${{ github.sha }}
      - uses: astral-sh/setup-uv@v7.2.1
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
      - name: Install bump-my-version
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
        run: uv tool install 'bump-my-version==1.2.6'
      - name: ${{ matrix.part }} version bump
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
        run: bump-my-version bump --verbose ${{ matrix.part }}
      - name: Sync uv.lock
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
        run: uv --no-progress sync
      - id: pr-metadata
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
        run: >
          uvx --no-progress 'gha-utils==5.10.1' pr-body
          --template bump-version
          --part "${{ matrix.part }}"
          --output "$GITHUB_OUTPUT"
      - uses: peter-evans/create-pull-request@v8.1.0
        if: needs.project-metadata.outputs[format('{0}_bump_allowed', matrix.part)] == 'true'
        with:
          assignees: ${{ github.actor }}
          commit-message: ${{ steps.pr-metadata.outputs.commit_message }}
          title: ${{ steps.pr-metadata.outputs.title }}
          body: ${{ steps.pr-metadata.outputs.body }}
          labels: "🆙 changelog"
          base: main
          branch: ${{ matrix.part }}-version-increment
          delete-branch: true
          draft: always-true

  prepare-release:
    # Skip schedule (exists for bump-versions only) and workflow_run (would double-run
    # on every push to main, since push already triggers this job directly).
    if: github.event_name != 'schedule' && github.event_name != 'workflow_run'
    runs-on: ubuntu-slim
    steps:
      - uses: actions/checkout@v6.0.2
      - uses: astral-sh/setup-uv@v7.2.1
      - name: Install bump-my-version
        run: uv tool install 'bump-my-version==1.2.6'
      # --- Freeze commit: freeze everything to the release version. ---
      - name: Strip dev suffix for release
        # Bump the "dev" part: .dev0 → release (omitted), producing a clean X.Y.Z version.
        run: bump-my-version bump --verbose dev
      - name: Extract version
        id: get_version
        run: echo "current_version=$( bump-my-version show current_version )" | tee -a "$GITHUB_OUTPUT"
      - name: Prepare release
        # Updates changelog and citation dates, comparison URL, and removes warning.
        # Also hard-codes version in workflow URLs for kdeldycke/workflows repository.
        run: uvx --no-progress 'gha-utils==5.10.1' release-prep
      - name: Prepare repository
        run: |
          git config --global user.name "${{ github.actor }}"
          git config --global user.email "${{ github.actor }}@users.noreply.github.com"
          git clean -fd
      - name: Create freeze commit
        run: git commit --all --message="[changelog] Release v${{ steps.get_version.outputs.current_version }}"
      # --- Unfreeze commit: revert to development references. ---
      - name: Re-target main branch in workflows
        # This step is only used in the original repository to automate remote URL tagging.
        if: github.repository == 'kdeldycke/workflows'
        run: uvx --no-progress 'gha-utils==5.10.1' release-prep --post-release
      - name: Add new changelog entry
        run: uvx --no-progress 'gha-utils==5.10.1' changelog ./changelog.md
      - name: Version bump
        run: bump-my-version bump --verbose patch
      - name: Sync uv.lock
        run: uv --no-progress sync
      - name: Create unfreeze commit
        run: >
          git commit --all --message="[changelog] Post-release bump
          v${{ steps.get_version.outputs.current_version }} → v$(bump-my-version show current_version)"
      - id: pr-metadata
        run: >
          uvx --no-progress 'gha-utils==5.10.1' pr-body
          --template prepare-release
          --version "${{ steps.get_version.outputs.current_version }}"
          --output "$GITHUB_OUTPUT"
      - uses: peter-evans/create-pull-request@v8.1.0
        with:
          # We need custom PAT with workflows permission to hard-code version numbers in URLs in
          # .github/workflows/*.yaml files.
          token: ${{ secrets.WORKFLOW_UPDATE_GITHUB_PAT || secrets.GITHUB_TOKEN }}
          assignees: ${{ github.actor }}
          commit-message: ${{ steps.pr-metadata.outputs.commit_message }}
          title: ${{ steps.pr-metadata.outputs.title }}
          body: ${{ steps.pr-metadata.outputs.body }}
          labels: "🆙 changelog"
          base: main
          branch: prepare-release
          delete-branch: true
          draft: always-true
