name: Apple release (macOS DMG)

# Builds, signs, notarizes, and uploads the direct-download macOS build:
#   - a Developer ID–signed, notarized VniDrop-<version>.dmg,
#   - a Sparkle appcast.xml.
#
# The central release workflow publishes these artifacts and updates Homebrew.
#
# The App Store / TestFlight build is NOT produced here — that goes through Xcode
# Organizer / App Store Connect. This workflow only covers direct distribution.
#
# Called by the central tag-release workflow, or run manually to validate the
# signed/notarized direct-download artifact.

on:
  workflow_call:
  workflow_dispatch:

permissions:
  contents: read

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

defaults:
  run:
    shell: bash

jobs:
  build:
    name: Build & notarize DMG
    runs-on: macos-latest
    timeout-minutes: 90
    outputs:
      version: ${{ steps.version.outputs.app }}
    steps:
      - name: Checkout
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          fetch-depth: 0
          persist-credentials: false

      - name: Verify tag is on master
        if: github.event_name == 'push'
        run: |
          if ! git merge-base --is-ancestor "$GITHUB_SHA" origin/master; then
            echo "Release tags must point to a commit on master" >&2
            exit 1
          fi

      - name: Resolve canonical version
        id: version
        run: |
          packaging/version/resolve-version.sh verify >/dev/null
          version="$(packaging/version/resolve-version.sh product)"
          echo "app=$version" >> "$GITHUB_OUTPUT"

      - name: Select Xcode
        run: sudo xcode-select -s /Applications/Xcode.app

      - name: Install Rust toolchain
        uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # v1
        with:
          toolchain: stable
          targets: aarch64-apple-darwin

      - name: Cache Cargo
        uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
        with:
          path: |
            ~/.cargo/registry
            ~/.cargo/git
            target
          key: apple-release-cargo-${{ hashFiles('Cargo.lock') }}
          restore-keys: apple-release-cargo-

      - name: Install tooling
        run: brew install xcodegen swiftlint create-dmg

      - name: Install Bun
        uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2

      - name: Download Sparkle tools
        # generate_appcast + sign_update ship in the Sparkle release tarball.
        run: |
          set -euo pipefail
          ver="2.9.4"
          curl -fsSL -o /tmp/sparkle.tar.xz \
            "https://github.com/sparkle-project/Sparkle/releases/download/${ver}/Sparkle-${ver}.tar.xz"
          mkdir -p /tmp/sparkle && tar -xJf /tmp/sparkle.tar.xz -C /tmp/sparkle
          echo "SPARKLE_BIN=/tmp/sparkle/bin" >> "$GITHUB_ENV"

      - name: Import Developer ID certificate
        env:
          CERT_P12_BASE64: ${{ secrets.DEVELOPER_ID_CERT_P12 }}
          CERT_PASSWORD: ${{ secrets.DEVELOPER_ID_CERT_PASSWORD }}
        run: |
          set -euo pipefail
          keychain="$RUNNER_TEMP/signing.keychain-db"
          kpw="$(openssl rand -hex 20)"
          security create-keychain -p "$kpw" "$keychain"
          security set-keychain-settings -lut 21600 "$keychain"
          security unlock-keychain -p "$kpw" "$keychain"
          echo "$CERT_P12_BASE64" | base64 --decode > "$RUNNER_TEMP/cert.p12"
          security import "$RUNNER_TEMP/cert.p12" -k "$keychain" -P "$CERT_PASSWORD" \
            -T /usr/bin/codesign -T /usr/bin/security
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$kpw" "$keychain"
          # Prepend our keychain so codesign/xcodebuild can find the identity.
          security list-keychains -d user -s "$keychain" $(security list-keychains -d user | tr -d '"')
          rm -f "$RUNNER_TEMP/cert.p12"

      - name: Store notarytool credentials
        env:
          NOTARY_KEY_P8: ${{ secrets.NOTARY_API_KEY }}
          NOTARY_KEY_ID: ${{ secrets.NOTARY_KEY_ID }}
          NOTARY_ISSUER: ${{ secrets.NOTARY_ISSUER }}
        run: |
          set -euo pipefail
          echo "$NOTARY_KEY_P8" | base64 --decode > "$RUNNER_TEMP/notary.p8"
          xcrun notarytool store-credentials vnidrop-notary \
            --key "$RUNNER_TEMP/notary.p8" \
            --key-id "$NOTARY_KEY_ID" \
            --issuer "$NOTARY_ISSUER"
          echo "NOTARY_PROFILE=vnidrop-notary" >> "$GITHUB_ENV"

      - name: Write Sparkle signing key
        env:
          SPARKLE_ED_PRIVATE_KEY: ${{ secrets.SPARKLE_ED_PRIVATE_KEY }}
        run: |
          printf '%s' "$SPARKLE_ED_PRIVATE_KEY" > "$RUNNER_TEMP/sparkle_ed_private_key"
          echo "SPARKLE_ED_KEY_FILE=$RUNNER_TEMP/sparkle_ed_private_key" >> "$GITHUB_ENV"

      - name: Build, sign & notarize DMG
        run: make build-apple-dmg

      - name: Package prebuilt core
        # build-apple-dmg builds the release Rust core + Swift bindings; bundle them
        # (xcframework + Vnidrop.swift + checksum) as a release asset so consumers can
        # skip building the core. See apple/scripts/package-core.sh.
        run: make package-apple-core

      - name: Upload notarization diagnostics
        if: failure()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: vnidrop-${{ steps.version.outputs.app }}-notarization-diagnostics
          path: apple/dist/*.notary-log.json
          if-no-files-found: ignore
          retention-days: 14

      - name: Generate appcast
        env:
          RELEASE_REPO: ${{ github.repository }}
        run: apple/scripts/generate-appcast.sh

      - name: Upload artifacts
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
        with:
          name: vnidrop-${{ steps.version.outputs.app }}-macos-dmg
          path: |
            apple/dist/VniDrop-*.dmg
            apple/dist/VniDrop-*.build-info.json
            apple/dist/appcast.xml
            apple/dist/VnidropCore-*.zip
            apple/dist/VnidropCore-*.zip.sha256
          if-no-files-found: error
          retention-days: 14
