name: Alpha Channel (macOS arm64)

# Every merge to `dev` publishes a fresh macOS arm64 build to the OpenWork
# alpha release channel.
#
# The alpha channel is macOS-only today. It lives as a rolling GitHub
# release. Each run also updates a small manifest on the fixed
# `alpha-macos-latest` release so the Electron updater feed stays stable while
# historical alpha artifacts remain available.
#
# See:
#   - ARCHITECTURE.md#release-channels
#   - .github/workflows/release-macos-aarch64.yml (stable channel)

on:
  push:
    branches:
      - dev
  workflow_dispatch:
    inputs:
      publish_pointer:
        description: "Update alpha-macos-latest (leave off for isolated branch validation)"
        required: false
        type: boolean
        default: false

permissions:
  contents: write

concurrency:
  group: alpha-macos-aarch64-${{ github.ref }}
  cancel-in-progress: true

jobs:
  publish-alpha-macos-aarch64:
    name: Build + Publish alpha (aarch64-apple-darwin)
    runs-on: macos-14
    timeout-minutes: 180

    env:
      ALPHA_RELEASE_TAG: alpha-macos-latest
      ALPHA_RELEASE_NAME: OpenWork Alpha (macOS arm64)
      # Apple signing + notarization are required so alpha bundles install
      # and launch without Gatekeeper friction. Alpha builds are served
      # from GitHub Releases like stable, just from a different tag.
      MACOS_NOTARIZE: ${{ vars.MACOS_NOTARIZE || 'true' }}

    steps:
      - name: Checkout
        uses: actions/checkout@v6
        with:
          ref: ${{ github.sha }}

      - name: Setup Node
        uses: actions/setup-node@v6
        with:
          node-version: 24

      - name: Setup pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 11.4.0

      - name: Setup Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: "1.3.6"

      - name: Get pnpm store path
        id: pnpm-store
        shell: bash
        run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"

      - name: Cache pnpm store
        uses: actions/cache@v5
        continue-on-error: true
        with:
          path: ${{ steps.pnpm-store.outputs.path }}
          key: macos-pnpm-${{ hashFiles('pnpm-lock.yaml') }}
          restore-keys: |
            macos-pnpm-

      - name: Install dependencies
        run: pnpm install --frozen-lockfile --prefer-offline

      - name: Resolve alpha version
        id: alpha-version
        shell: bash
        env:
          GITHUB_RUN_NUMBER: ${{ github.run_number }}
          GITHUB_SHA: ${{ github.sha }}
        run: |
          set -euo pipefail
          node <<'NODE' >> "$GITHUB_OUTPUT"
          const fs = require("node:fs");
          const path = "apps/desktop/package.json";
          const raw = JSON.parse(fs.readFileSync(path, "utf8"));
          const current = String(raw.version || "").trim();
          const match = current.match(/^(\d+)\.(\d+)\.(\d+)(?:-.+)?$/);
          if (!match) {
            throw new Error(`Unsupported version in ${path}: ${current}`);
          }
          const [, major, minor, patch] = match;
          // Alpha builds advertise the *next* patch version so semver
          // comparison makes the alpha newer than the current stable
          // (e.g. stable 0.11.207 < alpha 0.11.208-alpha.<run>). Once
          // stable 0.11.208 ships, its semver beats the alpha prerelease
          // tag and alpha users cleanly migrate forward.
          const nextPatch = Number(patch) + 1;
          const run = process.env.GITHUB_RUN_NUMBER || "0";
          const sha = (process.env.GITHUB_SHA || "").slice(0, 7) || "local";
          const alpha = `${major}.${minor}.${nextPatch}-alpha.${run}+${sha}`;
          const releaseTag = `alpha-macos-v${major}.${minor}.${nextPatch}-alpha.${run}-${sha}`;
          console.log(`alpha_version=${alpha}`);
          console.log(`base_version=${major}.${minor}.${nextPatch}`);
          console.log(`release_tag=${releaseTag}`);
          NODE

      - name: Write alpha Electron package version
        shell: bash
        env:
          ALPHA_VERSION: ${{ steps.alpha-version.outputs.alpha_version }}
        run: |
          set -euo pipefail
          node <<'NODE'
          const fs = require("node:fs");
          for (const path of ["apps/desktop/package.json", "apps/app/package.json"]) {
            const json = JSON.parse(fs.readFileSync(path, "utf8"));
            json.version = process.env.ALPHA_VERSION;
            fs.writeFileSync(path, `${JSON.stringify(json, null, 2)}\n`);
          }
          NODE

      - name: Write notary API key
        if: env.MACOS_NOTARIZE == 'true'
        env:
          APPLE_NOTARY_API_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARY_API_KEY_P8_BASE64 }}
        run: |
          set -euo pipefail

          NOTARY_KEY_PATH="$RUNNER_TEMP/AuthKey.p8"
          printf '%s' "$APPLE_NOTARY_API_KEY_P8_BASE64" | base64 --decode > "$NOTARY_KEY_PATH"
          chmod 600 "$NOTARY_KEY_PATH"

          echo "NOTARY_KEY_PATH=$NOTARY_KEY_PATH" >> "$GITHUB_ENV"

      - name: Reject unsigned Electron alpha release
        if: env.MACOS_NOTARIZE != 'true'
        shell: bash
        run: |
          echo "Electron alpha artifacts must be signed and notarized. Set MACOS_NOTARIZE=true and provide Apple signing secrets." >&2
          exit 1

      - name: Build Electron alpha app
        if: env.MACOS_NOTARIZE == 'true'
        shell: bash
        env:
          TARGET: aarch64-apple-darwin
        run: pnpm --filter @openwork/desktop build:electron

      - name: Package Electron alpha (macOS, signed + notarized)
        if: env.MACOS_NOTARIZE == 'true'
        env:
          CSC_LINK: ${{ secrets.APPLE_CODESIGN_CERT_P12_BASE64 }}
          CSC_KEY_PASSWORD: ${{ secrets.APPLE_CODESIGN_CERT_PASSWORD }}
          MACOS_NOTARIZE: true
          APPLE_API_KEY: ${{ secrets.APPLE_NOTARY_API_KEY_ID }}
          APPLE_API_ISSUER: ${{ secrets.APPLE_NOTARY_API_ISSUER_ID }}
          APPLE_API_KEY_PATH: ${{ env.NOTARY_KEY_PATH }}
        run: |
          set -euo pipefail
          pnpm --dir apps/desktop exec electron-builder \
            --config electron-builder.yml \
            --mac \
            --arm64 \
            --publish never

      - name: Create immutable alpha prerelease
        if: env.MACOS_NOTARIZE == 'true'
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
          ALPHA_VERSION: ${{ steps.alpha-version.outputs.alpha_version }}
          ALPHA_RUN_RELEASE_TAG: ${{ steps.alpha-version.outputs.release_tag }}
        run: |
          set -euo pipefail
          body="Rolling alpha build for OpenWork (macOS arm64) from ${GITHUB_SHA}."
          if gh release view "$ALPHA_RUN_RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
            echo "Alpha prerelease $ALPHA_RUN_RELEASE_TAG already exists; reusing it."
            exit 0
          fi
          gh release create "$ALPHA_RUN_RELEASE_TAG" \
            --repo "$GITHUB_REPOSITORY" \
            --title "OpenWork Alpha ${ALPHA_VERSION}" \
            --notes "$body" \
            --target "$GITHUB_SHA" \
            --prerelease

      - name: Upload Electron alpha updater assets
        if: env.MACOS_NOTARIZE == 'true'
        env:
          GH_TOKEN: ${{ github.token }}
          ALPHA_RUN_RELEASE_TAG: ${{ steps.alpha-version.outputs.release_tag }}
        run: |
          set -euo pipefail
          shopt -s nullglob
          assets=(
            apps/desktop/dist-electron/*.dmg
            apps/desktop/dist-electron/*.zip
            apps/desktop/dist-electron/*.blockmap
            apps/desktop/dist-electron/latest-mac.yml
          )
          if [ ${#assets[@]} -eq 0 ]; then
            echo "No Electron alpha assets found in apps/desktop/dist-electron" >&2
            exit 1
          fi
          gh release upload "$ALPHA_RUN_RELEASE_TAG" "${assets[@]}" \
            --repo "$GITHUB_REPOSITORY" \
            --clobber

      - name: Update alpha updater pointer
        if: env.MACOS_NOTARIZE == 'true' && (github.event_name == 'push' || inputs.publish_pointer == true)
        shell: bash
        env:
          GH_TOKEN: ${{ github.token }}
          ALPHA_RUN_RELEASE_TAG: ${{ steps.alpha-version.outputs.release_tag }}
        run: |
          set -euo pipefail
          POINTER_MANIFEST="$RUNNER_TEMP/latest-mac.yml"
          export POINTER_MANIFEST
          node <<'NODE'
          const fs = require("node:fs");
          const manifestPath = "apps/desktop/dist-electron/latest-mac.yml";
          const releaseTag = process.env.ALPHA_RUN_RELEASE_TAG;
          const baseUrl = `https://github.com/different-ai/openwork/releases/download/${releaseTag}`;
          const rewritten = fs.readFileSync(manifestPath, "utf8")
            .replace(/(url:\s*)(openwork-[^\n]+)/g, `$1${baseUrl}/$2`)
            .replace(/(path:\s*)(openwork-[^\n]+)/g, `$1${baseUrl}/$2`);
          fs.writeFileSync(process.env.POINTER_MANIFEST, rewritten);
          NODE

          if ! gh release view "$ALPHA_RELEASE_TAG" --repo "$GITHUB_REPOSITORY" >/dev/null 2>&1; then
            gh release create "$ALPHA_RELEASE_TAG" \
              --repo "$GITHUB_REPOSITORY" \
              --title "$ALPHA_RELEASE_NAME" \
              --notes "Stable Electron updater pointer for the newest macOS arm64 alpha prerelease." \
              --target "$GITHUB_SHA" \
              --prerelease
          fi

          gh release upload "$ALPHA_RELEASE_TAG" "$POINTER_MANIFEST#latest-mac.yml" \
            --repo "$GITHUB_REPOSITORY" \
            --clobber
