phrog 0.53.0

Mobile-friendly greeter for greetd
name: Release

on:
  push:
    tags:
      - "*"
  pull_request:
    types: [opened, synchronize, reopened, ready_for_review]

permissions:
  contents: write
  packages: write
  pull-requests: write

jobs:
  gate:
    runs-on: ubuntu-latest
    outputs:
      enabled: ${{ steps.detect.outputs.enabled }}
      mode: ${{ steps.detect.outputs.mode }}
      prerelease: ${{ steps.detect.outputs.prerelease }}
      version: ${{ steps.detect.outputs.version }}
      tag: ${{ steps.detect.outputs.tag }}
    steps:
      - id: detect
        env:
          EVENT_NAME: ${{ github.event_name }}
          REF_TYPE: ${{ github.ref_type }}
          REF_NAME: ${{ github.ref_name }}
          REPOSITORY: ${{ github.repository }}
          PR_HEAD_REF: ${{ github.event.pull_request.head.ref }}
          PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
        run: |
          set -euo pipefail

          enabled=false
          mode=none
          prerelease=false
          version=""

          if [[ "$EVENT_NAME" == "push" ]] \
            && [[ "$REF_TYPE" == "tag" ]] \
            && [[ "$REF_NAME" =~ ^([0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?)$ ]]; then
            enabled=true
            mode=tag
            version="${BASH_REMATCH[1]}"
          elif [[ "$EVENT_NAME" == "pull_request" ]] \
            && [[ "$PR_HEAD_REPO" == "$REPOSITORY" ]] \
            && [[ "$PR_HEAD_REF" =~ ^release/v([0-9]+\.[0-9]+\.[0-9]+(-rc\.[0-9]+)?)$ ]]; then
            enabled=true
            mode=pr
            version="${BASH_REMATCH[1]}"
          fi

          if [[ -n "$version" && "$version" =~ -rc\.[0-9]+$ ]]; then
            prerelease=true
          fi

          echo "enabled=$enabled" >> "$GITHUB_OUTPUT"
          echo "mode=$mode" >> "$GITHUB_OUTPUT"
          echo "prerelease=$prerelease" >> "$GITHUB_OUTPUT"
          echo "version=$version" >> "$GITHUB_OUTPUT"
          echo "tag=$version" >> "$GITHUB_OUTPUT"

  release:
    needs: gate
    if: needs.gate.outputs.enabled == 'true'
    runs-on: ubuntu-latest
    steps:
      - if: needs.gate.outputs.mode == 'tag'
        name: Require release automation token
        env:
          RELEASE_GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          if [[ -z "$RELEASE_GITHUB_TOKEN" ]]; then
            echo "RELEASE_GITHUB_TOKEN secret is required for release automation."
            exit 1
          fi

      - uses: actions/checkout@v4

      - uses: actions-rust-lang/setup-rust-toolchain@v1

      - name: Build release notes
        env:
          MODE: ${{ needs.gate.outputs.mode }}
          PRERELEASE: ${{ needs.gate.outputs.prerelease }}
          TAG_NAME: ${{ needs.gate.outputs.tag }}
          VERSION: ${{ needs.gate.outputs.version }}
        run: |
          set -euo pipefail

          notes_args=(release-notes "$VERSION")
          if [[ "$PRERELEASE" == "true" ]]; then
            notes_args=(release-notes --rc "$VERSION")
          fi

          if [[ "$MODE" == "tag" ]]; then
            {
              printf '<img align="right" width="180" height="360" src="https://github.com/samcday/phrog/releases/download/%s/demo.webp">\n\n' "$TAG_NAME"
              cargo xtask "${notes_args[@]}"
            } > body.md
          else
            cargo xtask "${notes_args[@]}" >/dev/null
          fi

      - if: needs.gate.outputs.mode == 'tag'
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.gate.outputs.tag }}
          body_path: body.md
          prerelease: ${{ needs.gate.outputs.prerelease }}
          draft: true
          token: ${{ secrets.RELEASE_GITHUB_TOKEN }}

      - if: needs.gate.outputs.mode == 'pr'
        run: |
          echo "release/v* PR mode: skipping draft creation"

  build:
    needs: [gate, release]
    if: needs.gate.outputs.enabled == 'true'
    uses: ./.github/workflows/build.yml
    with:
      release_mode: true
    secrets: inherit

  alpine:
    needs: [gate, release]
    if: needs.gate.outputs.enabled == 'true'
    uses: ./.github/workflows/alpine.yml
    with:
      release_mode: ${{ needs.gate.outputs.mode }}
    secrets: inherit

  debian:
    needs: [gate, release]
    if: needs.gate.outputs.enabled == 'true'
    uses: ./.github/workflows/debian-unstable.yml
    with:
      release_mode: ${{ needs.gate.outputs.mode }}
    secrets: inherit

  release-builds:
    needs: [gate, release]
    if: needs.gate.outputs.enabled == 'true'
    uses: ./.github/workflows/release-builds.yml
    secrets: inherit

  publish-assets:
    needs: [gate, release, build, alpine, debian, release-builds]
    if: needs.gate.outputs.enabled == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: demo-video
          path: demo-video/

      - uses: actions/download-artifact@v4
        with:
          name: APKBUILD
          path: packages/

      - uses: actions/download-artifact@v4
        with:
          pattern: packages-*
          path: packages/
          merge-multiple: true

      - uses: actions/download-artifact@v4
        with:
          pattern: debs-*
          path: debs/
          merge-multiple: true

      - uses: actions/download-artifact@v4
        with:
          pattern: release-*
          path: dist/
          merge-multiple: true

      - if: needs.gate.outputs.mode == 'pr'
        name: Verify staged release artifacts
        run: |
          set -euo pipefail
          test -f demo-video/demo.mp4
          test -f demo-video/demo.webp
          test -f packages/APKBUILD
          compgen -G 'packages/*.apk' >/dev/null
          compgen -G 'debs/*.deb' >/dev/null
          compgen -G 'dist/*.tar.gz' >/dev/null

      - if: needs.gate.outputs.mode == 'tag'
        name: Require release automation token
        env:
          RELEASE_GITHUB_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }}
        run: |
          set -euo pipefail
          if [[ -z "$RELEASE_GITHUB_TOKEN" ]]; then
            echo "RELEASE_GITHUB_TOKEN secret is required for release asset publication."
            exit 1
          fi

      - if: needs.gate.outputs.mode == 'tag'
        name: Upload artifacts to draft release
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ needs.gate.outputs.tag }}
          token: ${{ secrets.RELEASE_GITHUB_TOKEN }}
          files: |
            demo-video/demo.mp4
            demo-video/demo.webp
            packages/APKBUILD
            packages/*.apk
            debs/*.deb
            dist/*.tar.gz

  publish-crate:
    needs: [gate, release]
    if: needs.gate.outputs.enabled == 'true'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - if: needs.gate.outputs.mode == 'pr'
        name: Validate crate publish (dry-run)
        run: |
          set -euo pipefail
          cargo publish --locked --no-verify --dry-run

      - if: needs.gate.outputs.mode == 'tag'
        name: Require crates.io token
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          set -euo pipefail
          if [[ -z "$CARGO_REGISTRY_TOKEN" ]]; then
            echo "CARGO_REGISTRY_TOKEN secret is required for crates.io publishing."
            exit 1
          fi

      - if: needs.gate.outputs.mode == 'tag'
        name: Publish phrog to crates.io
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}
        run: |
          set -euo pipefail
          cargo publish --locked --no-verify

  pass:
    name: "✅ Pass"
    needs: [gate, release, build, alpine, debian, release-builds, publish-assets, publish-crate]
    runs-on: ubuntu-latest
    if: always() && needs.gate.outputs.enabled == 'true'
    steps:
      - uses: re-actors/alls-green@release/v1
        with:
          jobs: ${{ toJSON(needs) }}

  publish-release:
    needs: [gate, pass]
    if: needs.gate.outputs.enabled == 'true'
    runs-on: ubuntu-latest
    steps:
      - if: needs.gate.outputs.mode == 'tag' && needs.pass.result == 'success'
        name: Publish draft release
        env:
          GH_TOKEN: ${{ secrets.RELEASE_GITHUB_TOKEN }}
          TAG_NAME: ${{ needs.gate.outputs.tag }}
        run: |
          set -euo pipefail

          if [[ -z "$GH_TOKEN" ]]; then
            echo "RELEASE_GITHUB_TOKEN secret is required to publish draft releases."
            exit 1
          fi

          gh release edit "$TAG_NAME" --repo "$GITHUB_REPOSITORY" --draft=false >/dev/null

          draft_state="$(gh api "repos/${GITHUB_REPOSITORY}/releases/tags/${TAG_NAME}" --jq '.draft')"
          if [[ "$draft_state" != "false" ]]; then
            echo "::error::release ${TAG_NAME} is still marked as draft"
            exit 1
          fi

      - if: needs.gate.outputs.mode == 'pr'
        name: Skip release publication in PR mode
        run: |
          echo "release/v* PR mode: skipping draft publication"