# release.yml — markdown-tui-explorer binary release pipeline
#
# WHEN IT FIRES
#   Push of a tag matching v*.*.* (e.g. v1.35.0). Arbitrary pushes and
#   branch pushes are ignored. A `workflow_dispatch` trigger is also present
#   so any tag can be back-filled manually from the Actions UI.
#
# WHAT IT PRODUCES
#   Per-platform release archives uploaded to the corresponding GitHub Release:
#     markdown-reader-<version>-x86_64-unknown-linux-gnu.tar.gz
#     markdown-reader-<version>-aarch64-unknown-linux-gnu.tar.gz
#     markdown-reader-<version>-x86_64-unknown-linux-musl.tar.gz
#     markdown-reader-<version>-x86_64-apple-darwin.tar.gz
#     markdown-reader-<version>-aarch64-apple-darwin.tar.gz
#     markdown-reader-<version>-x86_64-pc-windows-msvc.zip
#     SHA256SUMS
#
#   Each archive also contains LICENSE and README.md.
#
# MACOS SIGNING STATUS — UNSIGNED (Gatekeeper warning expected)
#   The macOS binaries are NOT code-signed or notarized. Users on macOS will
#   see a Gatekeeper warning ("cannot be opened because it is from an
#   unidentified developer") and must right-click → Open to proceed once.
#
# TODO: Adding Apple Developer ID signing + notarization
#   1. Obtain an Apple Developer ID Application certificate from
#      https://developer.apple.com/account/resources/certificates/
#   2. Export the certificate + private key as a .p12 and base64-encode it:
#        base64 -i cert.p12 | pbcopy
#   3. Add these repository secrets:
#        APPLE_DEVELOPER_ID_CERT   — the base64-encoded .p12
#        APPLE_DEVELOPER_ID_PASS   — the .p12 export password
#        APPLE_NOTARY_PROFILE      — name of the notarytool credential
#                                    (see `xcrun notarytool store-credentials`)
#        APPLE_TEAM_ID             — your 10-character Apple Team ID
#   4. In the build-macos job, after the "Strip binary" step, add:
#        - name: Sign binary
#          run: |
#            echo "$APPLE_DEVELOPER_ID_CERT" | base64 --decode > cert.p12
#            security create-keychain -p "" build.keychain
#            security import cert.p12 -k build.keychain \
#              -P "$APPLE_DEVELOPER_ID_PASS" -T /usr/bin/codesign
#            security set-key-partition-list -S apple-tool:,apple: \
#              -s -k "" build.keychain
#            codesign --force --options runtime \
#              --sign "Developer ID Application: <Your Name> ($APPLE_TEAM_ID)" \
#              target/${{ matrix.target }}/release/markdown-reader
#          env:
#            APPLE_DEVELOPER_ID_CERT: ${{ secrets.APPLE_DEVELOPER_ID_CERT }}
#            APPLE_DEVELOPER_ID_PASS: ${{ secrets.APPLE_DEVELOPER_ID_PASS }}
#            APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
#        - name: Notarize binary
#          run: |
#            xcrun notarytool submit target/${{ matrix.target }}/release/markdown-reader \
#              --keychain-profile "$APPLE_NOTARY_PROFILE" --wait
#            xcrun stapler staple target/${{ matrix.target }}/release/markdown-reader
#          env:
#            APPLE_NOTARY_PROFILE: ${{ secrets.APPLE_NOTARY_PROFILE }}

name: Release

# Triggered by `v*.*.*` tags (the parent crate `markdown-tui-explorer`'s
# version line). Builds cross-platform binaries of `markdown-reader`,
# attaches them to a GitHub Release, publishes the parent crate to
# crates.io, and updates the Homebrew tap formula.
#
# The library crate `mermaid-text` has its own tag pattern
# (`mermaid-text-*`) handled by `release-mermaid-text.yml` — that one
# only publishes to crates.io, no binary release.

on:
  push:
    tags:
      - "v[0-9]*.[0-9]*.[0-9]*"
  workflow_dispatch:
    inputs:
      tag:
        description: "Tag to build (e.g. v1.35.0) — used for manual back-fills"
        required: false
      publish_crate:
        description: "Publish to crates.io"
        type: boolean
        default: true

permissions:
  contents: write

env:
  CARGO_TERM_COLOR: always
  CARGO_INCREMENTAL: 0
  # Resolve the tag once at workflow level so every job uses the same value
  # regardless of trigger. Under `push` of a tag, github.ref_name is the
  # tag (e.g. "v1.34.38"). Under workflow_dispatch (manual back-fill),
  # github.ref_name is the branch ("master"), so we honour the user-supplied
  # `inputs.tag` instead. Without this, GITHUB_REF_NAME under workflow_dispatch
  # leaks "master" into VERSION extraction and the gh-release action dies
  # with `##[error]⚠️ GitHub Releases requires a tag`.
  RESOLVED_TAG: ${{ inputs.tag || github.ref_name }}

jobs:
  build-linux:
    name: Build Linux (${{ matrix.target }})
    runs-on: ubuntu-latest
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-unknown-linux-gnu
          - aarch64-unknown-linux-gnu
          - x86_64-unknown-linux-musl
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2
        with:
          key: linux-${{ matrix.target }}

      - name: Install cross
        run: cargo install cross --locked

      - name: Build binary
        # Build the parent crate's `markdown-reader` binary.
        # `--package markdown-tui-explorer` ensures we don't try to build
        # mermaid-text's binary (which is a separate workspace member
        # released on its own tag pattern).
        #
        # `cross` handles cross-compilation for aarch64-unknown-linux-gnu
        # via a Docker image with the correct GCC cross-toolchain — no
        # manual linker setup required.
        run: cross build --release --target ${{ matrix.target }} --package markdown-tui-explorer

      - name: Strip binary
        run: |
          TARGET_STRIP=""
          case "${{ matrix.target }}" in
            aarch64-unknown-linux-gnu)
              TARGET_STRIP="aarch64-linux-gnu-strip"
              sudo apt-get install -y binutils-aarch64-linux-gnu
              ;;
            *)
              TARGET_STRIP="strip"
              ;;
          esac
          $TARGET_STRIP target/${{ matrix.target }}/release/markdown-reader

      - name: Package tarball
        run: |
          VERSION=${RESOLVED_TAG#v}
          STAGING="markdown-reader-${VERSION}-${{ matrix.target }}"
          mkdir -p "$STAGING"
          cp target/${{ matrix.target }}/release/markdown-reader "$STAGING/"
          cp LICENSE README.md "$STAGING/"
          TARBALL="${STAGING}.tar.gz"
          tar -czf "$TARBALL" "$STAGING"
          echo "TARBALL=$TARBALL" >> "$GITHUB_ENV"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: markdown-reader-${{ matrix.target }}
          path: ${{ env.TARBALL }}
          retention-days: 1

  build-macos:
    name: Build macOS (${{ matrix.target }})
    # Both targets build on macos-latest (Apple Silicon). The x86_64
    # target cross-compiles via `rustup target add x86_64-apple-darwin`
    # — the host toolchain and `strip` both handle it natively.
    #
    # NOTE: Binaries are NOT signed or notarized. See the comment block at
    # the top of this file for instructions on adding signing later.
    runs-on: macos-latest
    strategy:
      fail-fast: false
      matrix:
        target:
          - x86_64-apple-darwin
          - aarch64-apple-darwin
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: ${{ matrix.target }}

      - uses: Swatinem/rust-cache@v2
        with:
          key: macos-${{ matrix.target }}

      - name: Build binary
        run: cargo build --release --target ${{ matrix.target }} --package markdown-tui-explorer

      - name: Strip binary
        run: strip target/${{ matrix.target }}/release/markdown-reader

      - name: Package tarball
        run: |
          VERSION=${RESOLVED_TAG#v}
          STAGING="markdown-reader-${VERSION}-${{ matrix.target }}"
          mkdir -p "$STAGING"
          cp target/${{ matrix.target }}/release/markdown-reader "$STAGING/"
          cp LICENSE README.md "$STAGING/"
          TARBALL="${STAGING}.tar.gz"
          tar -czf "$TARBALL" "$STAGING"
          echo "TARBALL=$TARBALL" >> "$GITHUB_ENV"

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: markdown-reader-${{ matrix.target }}
          path: ${{ env.TARBALL }}
          retention-days: 1

  build-windows:
    name: Build Windows (x86_64-pc-windows-msvc)
    runs-on: windows-latest
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}

      - uses: dtolnay/rust-toolchain@stable
        with:
          targets: x86_64-pc-windows-msvc

      - uses: Swatinem/rust-cache@v2
        with:
          key: windows-x86_64-pc-windows-msvc

      - name: Build binary
        run: cargo build --release --target x86_64-pc-windows-msvc --package markdown-tui-explorer

      - name: Package zip
        shell: pwsh
        run: |
          $version = $env:RESOLVED_TAG -replace '^v',''
          $staging = "markdown-reader-${version}-x86_64-pc-windows-msvc"
          New-Item -ItemType Directory -Path $staging | Out-Null
          Copy-Item "target\x86_64-pc-windows-msvc\release\markdown-reader.exe" $staging\
          Copy-Item "LICENSE" $staging\
          Copy-Item "README.md" $staging\
          $zipName = "${staging}.zip"
          Compress-Archive -Path $staging -DestinationPath $zipName
          "ZIPFILE=$zipName" | Out-File -FilePath $env:GITHUB_ENV -Append

      - name: Upload artifact
        uses: actions/upload-artifact@v7
        with:
          name: markdown-reader-x86_64-pc-windows-msvc
          path: ${{ env.ZIPFILE }}
          retention-days: 1

  release:
    name: Create GitHub Release
    needs: [build-linux, build-macos, build-windows]
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}
          fetch-depth: 0

      - name: Download all artifacts
        uses: actions/download-artifact@v8
        with:
          path: artifacts
          merge-multiple: true

      - name: Generate SHA-256 checksums
        working-directory: artifacts
        run: sha256sum *.tar.gz *.zip > SHA256SUMS

      - name: Create unversioned asset aliases
        working-directory: artifacts
        run: |
          # Mirror each versioned archive under a stable filename so
          # https://github.com/<owner>/<repo>/releases/latest/download/<unversioned>
          # resolves. SHA256SUMS only references the versioned canonical
          # files; the aliases are convenience pointers with identical
          # bytes.
          shopt -s nullglob
          for f in markdown-reader-*.tar.gz markdown-reader-*.zip; do
            unversioned=$(echo "$f" | sed -E 's/^(markdown-reader)-[0-9]+\.[0-9]+\.[0-9]+-/\1-/')
            if [ "$f" != "$unversioned" ]; then
              cp "$f" "$unversioned"
            fi
          done

      - name: Extract changelog section
        id: changelog
        run: |
          VERSION=${RESOLVED_TAG#v}
          if [ -f CHANGELOG.md ]; then
            # Extract the block between ## [version] and the next ## [
            BODY=$(awk "/^## \[${VERSION}\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md)
          else
            BODY="Release ${RESOLVED_TAG}"
          fi
          printf '%s' "$BODY" > release_notes.md

      - name: Publish GitHub Release
        uses: softprops/action-gh-release@v3
        with:
          tag_name: ${{ env.RESOLVED_TAG }}
          body_path: release_notes.md
          files: |
            artifacts/*.tar.gz
            artifacts/*.zip
            artifacts/SHA256SUMS
          token: ${{ secrets.GITHUB_TOKEN }}

  publish-crate:
    name: Publish parent crate to crates.io
    needs: [release]
    runs-on: ubuntu-latest
    if: >
      (github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')) ||
      (github.event_name == 'workflow_dispatch' && inputs.publish_crate == true)
    steps:
      - uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}

      - uses: dtolnay/rust-toolchain@stable

      - uses: Swatinem/rust-cache@v2

      - name: Publish parent crate
        # Only the parent (markdown-tui-explorer) ships on `v*.*.*` tags.
        # The library (mermaid-text) is released independently via
        # `mermaid-text-*` tags; if a `v*.*.*` release depends on a new
        # mermaid-text version, tag mermaid-text first.
        run: cargo publish --package markdown-tui-explorer --token ${{ secrets.CARGO_REGISTRY_TOKEN }}

  publish-homebrew:
    name: Update Homebrew tap
    needs: [release]
    runs-on: ubuntu-latest
    # Note: GitHub Actions does not allow `secrets.*` in job-level `if:`
    # (https://github.com/orgs/community/discussions/17245). Instead,
    # every step guards on a `HAS_TOKEN` job env that evaluates the
    # secret into a plain string. When the token is absent the job
    # "runs" but all steps no-op, leaving a clean summary.
    env:
      HAS_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN != '' && 'true' || 'false' }}
    steps:
      - name: Report skip reason
        if: env.HAS_TOKEN != 'true'
        run: echo "HOMEBREW_TAP_TOKEN is not set on this repo — skipping Homebrew formula publish."

      - name: Checkout source repo
        if: env.HAS_TOKEN == 'true'
        uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}

      - name: Download release checksums
        if: env.HAS_TOKEN == 'true'
        # SHA256SUMS is an asset on the GitHub Release, not an inter-job
        # build artifact — it's generated inside the `release` job and
        # attached via softprops/action-gh-release. Fetch it directly.
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mkdir -p artifacts
          gh release download "$RESOLVED_TAG" \
            -R "$GITHUB_REPOSITORY" \
            -p SHA256SUMS \
            -D artifacts

      - name: Checkout tap repo
        if: env.HAS_TOKEN == 'true'
        uses: actions/checkout@v6
        with:
          repository: leboiko/homebrew-tap
          token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
          path: homebrew-tap

      - name: Render formula
        if: env.HAS_TOKEN == 'true'
        run: |
          VERSION=${RESOLVED_TAG#v}
          mkdir -p homebrew-tap/Formula
          ./scripts/render-homebrew-formula.sh "$VERSION" artifacts/SHA256SUMS \
            > homebrew-tap/Formula/markdown-reader.rb

      - name: Commit and push
        if: env.HAS_TOKEN == 'true'
        working-directory: homebrew-tap
        run: |
          VERSION=${RESOLVED_TAG#v}
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          # Stage first, then check the index. Plain `git diff --quiet`
          # misses brand-new files (Formula/markdown-reader.rb on a
          # first-ever release wouldn't exist in the tap yet — the file
          # would be untracked, so unstaged-diff returns clean and the
          # workflow would exit thinking it was a no-op).
          git add Formula/markdown-reader.rb
          if git diff --cached --quiet; then
            echo "Formula already up to date for v${VERSION}"
            exit 0
          fi
          git commit -m "markdown-reader ${VERSION}"
          git push

  publish-aur:
    name: Publish AUR -bin package
    needs: [release]
    runs-on: ubuntu-latest
    # Same `HAS_KEY` gate pattern as `publish-homebrew` — the job runs
    # on every release, but every step is no-op'd when the secret is
    # missing so an unconfigured fork stays green.
    env:
      HAS_KEY: ${{ secrets.AUR_SSH_KEY != '' && 'true' || 'false' }}
    steps:
      - name: Report skip reason
        if: env.HAS_KEY != 'true'
        run: echo "AUR_SSH_KEY is not set on this repo — skipping AUR publish."

      - name: Checkout source repo
        if: env.HAS_KEY == 'true'
        uses: actions/checkout@v6
        with:
          ref: ${{ env.RESOLVED_TAG }}

      - name: Download release checksums
        if: env.HAS_KEY == 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          mkdir -p artifacts
          gh release download "$RESOLVED_TAG" \
            -R "$GITHUB_REPOSITORY" \
            -p SHA256SUMS \
            -D artifacts

      - name: Set up SSH for AUR
        if: env.HAS_KEY == 'true'
        env:
          AUR_SSH_KEY: ${{ secrets.AUR_SSH_KEY }}
        run: |
          mkdir -p ~/.ssh
          echo "$AUR_SSH_KEY" > ~/.ssh/aur
          chmod 600 ~/.ssh/aur
          # Pin the AUR host key fingerprint to prevent MITM. Pulled
          # from `ssh-keyscan -H aur.archlinux.org` and verified
          # against the published fingerprint at
          # https://wiki.archlinux.org/title/Aurweb_RPC_interface.
          ssh-keyscan -H aur.archlinux.org >> ~/.ssh/known_hosts 2>/dev/null
          {
            echo "Host aur.archlinux.org"
            echo "  IdentityFile ~/.ssh/aur"
            echo "  User aur"
            echo "  StrictHostKeyChecking yes"
          } > ~/.ssh/config
          chmod 600 ~/.ssh/config

      - name: Clone AUR repo
        if: env.HAS_KEY == 'true'
        run: |
          # Clone (creates a local copy of the AUR-side repo for this
          # package). On first publish the AUR-side repo must exist —
          # see docs/RELEASING-AUR.md for the manual one-time setup.
          git clone ssh://aur@aur.archlinux.org/markdown-reader-bin.git aur-pkg

      - name: Render PKGBUILD + .SRCINFO
        if: env.HAS_KEY == 'true'
        run: |
          VERSION=${RESOLVED_TAG#v}
          ./scripts/render-aur-pkgbuild.sh "$VERSION" artifacts/SHA256SUMS aur-pkg

      - name: Commit and push
        if: env.HAS_KEY == 'true'
        working-directory: aur-pkg
        run: |
          VERSION=${RESOLVED_TAG#v}
          git config user.name "github-actions[bot]"
          git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
          git add PKGBUILD .SRCINFO
          if git diff --cached --quiet; then
            echo "AUR PKGBUILD already up to date for v${VERSION}"
            exit 0
          fi
          git commit -m "markdown-reader ${VERSION}"
          git push
