name: Dispatch release

# Kicks off a release by materializing news fragments into the selected
# packages' CHANGELOG.md files at the next version — the changelogs are the
# source of truth for publishing (see release_from_changelog.yml, which
# builds any changelog version that has no git tag yet and uploads it once
# the pypi environment deployment is approved).
#
# Leaving every package unchecked auto-selects the packages to release:
# those with pending news fragments — or, for release-from-prerelease, those
# whose changelog is topped by an alpha (the train's fragments are already
# consumed). Fragments in the repo-root news/ select reflex-base, which
# releases reflex alongside it.
#
# Prerelease actions (new-prerelease-*, continued-prerelease) write alpha
# versions and push them straight to an r/pre-<date> branch (continued
# prereleases push back to the r/pre-* branch the workflow was dispatched
# on); alphas build immediately and upload after pypi environment approval —
# no PR review involved. To pull new work into a prerelease train, merge
# main into the r/pre-* branch, then dispatch continued-prerelease on it.
#
# Release actions (release-*) open a pull request with the changelog changes
# instead — reviewing and merging that PR is how final versions land for
# publishing. The PR targets main, or the r/hotfix/** branch the workflow
# was dispatched on (hotfix branches may publish final versions directly).
# release-from-prerelease collapses the accumulated alpha sections into the
# single final-version section, so alpha headings never appear in a
# published final changelog.
#
# Materializing changelogs never publishes by itself: every PyPI upload —
# alphas included — additionally requires approval by the `pypi`
# environment's required reviewers (see publish.yml).
#
# REQUIRED CONFIGURATION:
# - The `pypi` environment must have required reviewers (asserted, fail
#   closed, by publish.yml).
# - "Allow GitHub Actions to create and approve pull requests" must be
#   enabled in the repo's Actions settings for release-* actions to open PRs.
# - Branch protection on main should require review; merging a release PR is
#   what lands final versions for publishing.
# - Recommended: a ruleset restricting who can create/push r/** and
#   release/** branches to maintainers plus the github-actions[bot] app
#   (this workflow pushes as github-actions[bot] via GITHUB_TOKEN).
# - If the pypi environment restricts deployment branches, allow main,
#   r/pre-*, and r/hotfix/*.
# - The old dispatch-release-approval environment is no longer used; the
#   pypi environment reviewers and release-PR review replace it.

on:
  workflow_dispatch:
    inputs:
      action:
        description: "Release action"
        required: true
        type: choice
        # default is continued-prerelease to prevent accidental releases.
        default: continued-prerelease
        options:
          - new-prerelease-patch
          - new-prerelease-minor
          - new-prerelease-major
          - continued-prerelease
          - release-from-prerelease
          - release-post
          - release-patch
          - release-minor
          - release-major
      hatch_reflex_pyi:
        description: "hatch-reflex-pyi"
        type: boolean
        default: false
      reflex_base:
        description: "reflex-base"
        type: boolean
        default: false
      reflex_components_code:
        description: "reflex-components-code"
        type: boolean
        default: false
      reflex_components_core:
        description: "reflex-components-core"
        type: boolean
        default: false
      reflex_components_dataeditor:
        description: "reflex-components-dataeditor"
        type: boolean
        default: false
      reflex_components_gridjs:
        description: "reflex-components-gridjs"
        type: boolean
        default: false
      reflex_components_lucide:
        description: "reflex-components-lucide"
        type: boolean
        default: false
      reflex_components_markdown:
        description: "reflex-components-markdown"
        type: boolean
        default: false
      reflex_components_moment:
        description: "reflex-components-moment"
        type: boolean
        default: false
      reflex_components_plotly:
        description: "reflex-components-plotly"
        type: boolean
        default: false
      reflex_components_radix:
        description: "reflex-components-radix"
        type: boolean
        default: false
      reflex_components_react_player:
        description: "reflex-components-react-player"
        type: boolean
        default: false
      reflex_components_recharts:
        description: "reflex-components-recharts"
        type: boolean
        default: false
      reflex_components_sonner:
        description: "reflex-components-sonner"
        type: boolean
        default: false
      reflex_docgen:
        description: "reflex-docgen"
        type: boolean
        default: false
      reflex_hosting_cli:
        description: "reflex-hosting-cli"
        type: boolean
        default: false

permissions:
  contents: read

concurrency:
  group: dispatch-release-${{ github.ref }}
  cancel-in-progress: false

jobs:
  detect:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    outputs:
      packages: ${{ steps.detect.outputs.packages }}
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          persist-credentials: false
      - id: detect
        env:
          hatch_reflex_pyi: ${{ inputs.hatch_reflex_pyi }}
          reflex_base: ${{ inputs.reflex_base }}
          reflex_components_code: ${{ inputs.reflex_components_code }}
          reflex_components_core: ${{ inputs.reflex_components_core }}
          reflex_components_dataeditor: ${{ inputs.reflex_components_dataeditor }}
          reflex_components_gridjs: ${{ inputs.reflex_components_gridjs }}
          reflex_components_lucide: ${{ inputs.reflex_components_lucide }}
          reflex_components_markdown: ${{ inputs.reflex_components_markdown }}
          reflex_components_moment: ${{ inputs.reflex_components_moment }}
          reflex_components_plotly: ${{ inputs.reflex_components_plotly }}
          reflex_components_radix: ${{ inputs.reflex_components_radix }}
          reflex_components_react_player: ${{ inputs.reflex_components_react_player }}
          reflex_components_recharts: ${{ inputs.reflex_components_recharts }}
          reflex_components_sonner: ${{ inputs.reflex_components_sonner }}
          reflex_docgen: ${{ inputs.reflex_docgen }}
          reflex_hosting_cli: ${{ inputs.reflex_hosting_cli }}
        run: bash .github/scripts/dispatch_release/detect.sh

  materialize:
    needs: detect
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write
      actions: write
    steps:
      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
        with:
          fetch-tags: true
          fetch-depth: 0
          persist-credentials: false
      - uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
      - name: Plan versions
        id: plan
        env:
          PACKAGES_JSON: ${{ needs.detect.outputs.packages }}
          ACTION: ${{ inputs.action }}
        run: uv run --no-config --locked --script scripts/release.py plan
      - name: Materialize changelogs
        env:
          RELEASES_JSON: ${{ steps.plan.outputs.releases }}
          ACTION: ${{ inputs.action }}
        run: uv run --no-config --locked --script scripts/release.py materialize
      - name: Push prerelease branch
        if: ${{ !startsWith(inputs.action, 'release-') }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ACTION: ${{ inputs.action }}
          REF_NAME: ${{ github.ref_name }}
          RELEASES: ${{ steps.plan.outputs.releases }}
        run: bash .github/scripts/dispatch_release/push_prerelease.sh
      - name: Open release pull request
        if: ${{ startsWith(inputs.action, 'release-') }}
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          ACTION: ${{ inputs.action }}
          REF_NAME: ${{ github.ref_name }}
          RELEASES: ${{ steps.plan.outputs.releases }}
        run: bash .github/scripts/dispatch_release/open_release_pr.sh
