on:
  workflow_dispatch: {}

name: Release PR

jobs:
  sentry-dsym-debug:
    runs-on: namespace-profile-ghostty-sm
    needs: [build-macos-debug]
    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

      - name: Install sentry-cli
        run: |
          curl -sL https://sentry.io/get-cli/ | bash

      - name: Download dSYM
        run: |
          GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)
          curl -L https://pr.files.ghostty.dev/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-dsym.zip > dsym.zip

      - name: Upload dSYM to Sentry
        env:
          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
        run: |
          sentry-cli dif upload --project ghostty --wait dsym.zip

  sentry-dsym:
    runs-on: namespace-profile-ghostty-sm
    needs: [build-macos]
    steps:
      - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

      - name: Install sentry-cli
        run: |
          curl -sL https://sentry.io/get-cli/ | bash

      - name: Download dSYM
        run: |
          GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)
          curl -L https://pr.files.ghostty.dev/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-dsym.zip > dsym.zip

      - name: Upload dSYM to Sentry
        env:
          SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
        run: |
          sentry-cli dif upload --project ghostty --wait dsym.zip

  build-macos:
    runs-on: namespace-profile-ghostty-macos-tahoe
    timeout-minutes: 90
    steps:
      - name: Checkout code
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          # Important so that build number generation works
          fetch-depth: 0

      # Install Nix and use that to run our tests so our environment matches exactly.
      - uses: DeterminateSystems/nix-installer-action@main
        with:
          determinate: true
      - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
        with:
          name: ghostty
          authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

      # Setup Sparkle
      - name: Setup Sparkle
        env:
          SPARKLE_VERSION: 2.6.4
        run: |
          mkdir -p .action/sparkle
          cd .action/sparkle
          curl -L https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-for-Swift-Package-Manager.zip > sparkle.zip
          unzip sparkle.zip
          echo "$(pwd)/bin" >> $GITHUB_PATH

      # Load Build Number
      - name: Build Number
        run: |
          echo "GHOSTTY_BUILD=$(git rev-list --count head)" >> $GITHUB_ENV
          echo "GHOSTTY_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
          echo "GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)" >> $GITHUB_ENV

      # GhosttyKit is the framework that is built from Zig for our native
      # Mac app to access. Build this in release mode.
      - name: Build GhosttyKit
        run: nix develop -c zig build -Doptimize=ReleaseSafe -Demit-macos-app=false

      # The native app is built with native XCode tooling. This also does
      # codesigning. IMPORTANT: this must NOT run in a Nix environment.
      # Nix breaks xcodebuild so this has to be run outside.
      - name: Build Ghostty.app
        run: |
          cd macos
          sudo xcode-select -s /Applications/Xcode_26.0.app
          xcodebuild -version
          xcodebuild -target Ghostty -configuration Release

      # We inject the "build number" as simply the number of commits since HEAD.
      # This will be a monotonically always increasing build number that we use.
      - name: Update Info.plist
        env:
          SPARKLE_KEY_PUB: ${{ secrets.PROD_MACOS_SPARKLE_KEY_PUB }}
        run: |
          # Version Info
          /usr/libexec/PlistBuddy -c "Set :GhosttyCommit $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
          /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GHOSTTY_BUILD" "macos/build/Release/Ghostty.app/Contents/Info.plist"
          /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"

          # Updater
          /usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
          /usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"

      - name: Codesign app bundle
        env:
          MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
          MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
          MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
          MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
        run: |
          # Turn our base64-encoded certificate back to a regular .p12 file
          echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12

          # We need to create a new keychain, otherwise using the certificate will prompt
          # with a UI dialog asking for the certificate password, which we can't
          # use in a headless CI environment
          security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
          security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain

          # Codesign Sparkle. Some notes here:
          #   - The XPC services aren't used since we don't sandbox Ghostty,
          #     but since they're part of the build, they still need to be
          #     codesigned.
          #   - The binaries in the "Versions" folders need to NOT be symlinks.
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework"

          # Codesign the app bundle
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime --entitlements "macos/Ghostty.entitlements" macos/build/Release/Ghostty.app

      - name: "Notarize app bundle"
        env:
          APPLE_NOTARIZATION_ISSUER: ${{ secrets.APPLE_NOTARIZATION_ISSUER }}
          APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
          APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
        run: |
          # Store the notarization credentials so that we can prevent a UI password dialog
          # from blocking the CI
          echo "Create keychain profile"
          echo "$APPLE_NOTARIZATION_KEY" > notarization_key.p8
          xcrun notarytool store-credentials "notarytool-profile" --key notarization_key.p8 --key-id "$APPLE_NOTARIZATION_KEY_ID" --issuer "$APPLE_NOTARIZATION_ISSUER"
          rm notarization_key.p8

          # We can't notarize an app bundle directly, but we need to compress it as an archive.
          # Therefore, we create a zip file containing our app bundle, so that we can send it to the
          # notarization service
          echo "Creating temp notarization archive"
          ditto -c -k --keepParent "macos/build/Release/Ghostty.app" "notarization.zip"

          # Here we send the notarization request to the Apple's Notarization service, waiting for the result.
          # This typically takes a few seconds inside a CI environment, but it might take more depending on the App
          # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
          # you're curious
          echo "Notarize app"
          xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait

          # Finally, we need to "attach the staple" to our executable, which will allow our app to be
          # validated by macOS even when an internet connection is not available.
          echo "Attach staple"
          xcrun stapler staple "macos/build/Release/Ghostty.app"

      # Zip up the app
      - name: Zip App
        run: |
          cd macos/build/Release
          zip -9 -r --symlinks ../../../ghostty-macos-universal.zip Ghostty.app
          zip -9 -r --symlinks ../../../ghostty-macos-universal-dsym.zip Ghostty.app.dSYM/

      # Update Blob Storage
      - name: Prep R2 Storage
        run: |
          mkdir blob
          mkdir -p blob/${GHOSTTY_COMMIT_LONG}
          cp ghostty-macos-universal.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal.zip
          cp ghostty-macos-universal-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-dsym.zip
      - name: Upload to R2
        uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
        with:
          r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
          r2-secret-access-key: ${{ secrets.CF_R2_PR_SECRET_KEY }}
          r2-bucket: ghostty-pr
          source-dir: blob
          destination-dir: ./

  build-macos-debug:
    runs-on: namespace-profile-ghostty-macos-tahoe
    timeout-minutes: 90
    steps:
      - name: Checkout code
        uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
        with:
          # Important so that build number generation works
          fetch-depth: 0

      # Install Nix and use that to run our tests so our environment matches exactly.
      - uses: DeterminateSystems/nix-installer-action@main
        with:
          determinate: true
      - uses: cachix/cachix-action@0fc020193b5a1fa3ac4575aa3a7d3aa6a35435ad # v16
        with:
          name: ghostty
          authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}"

      # Setup Sparkle
      - name: Setup Sparkle
        env:
          SPARKLE_VERSION: 2.5.1
        run: |
          mkdir -p .action/sparkle
          cd .action/sparkle
          curl -L https://github.com/sparkle-project/Sparkle/releases/download/${SPARKLE_VERSION}/Sparkle-for-Swift-Package-Manager.zip > sparkle.zip
          unzip sparkle.zip
          echo "$(pwd)/bin" >> $GITHUB_PATH

      # Load Build Number
      - name: Build Number
        run: |
          echo "GHOSTTY_BUILD=$(git rev-list --count head)" >> $GITHUB_ENV
          echo "GHOSTTY_COMMIT=$(git rev-parse --short HEAD)" >> $GITHUB_ENV
          echo "GHOSTTY_COMMIT_LONG=$(git rev-parse HEAD)" >> $GITHUB_ENV

      # GhosttyKit is the framework that is built from Zig for our native
      # Mac app to access. Build this in release mode.
      - name: Build GhosttyKit
        run: nix develop -c zig build -Doptimize=Debug -Demit-macos-app=false

      # The native app is built with native XCode tooling. This also does
      # codesigning. IMPORTANT: this must NOT run in a Nix environment.
      # Nix breaks xcodebuild so this has to be run outside.
      - name: Build Ghostty.app
        run: |
          cd macos
          sudo xcode-select -s /Applications/Xcode_26.0.app
          xcodebuild -version
          xcodebuild -target Ghostty -configuration Release

      # We inject the "build number" as simply the number of commits since HEAD.
      # This will be a monotonically always increasing build number that we use.
      - name: Update Info.plist
        env:
          SPARKLE_KEY_PUB: ${{ secrets.PROD_MACOS_SPARKLE_KEY_PUB }}
        run: |
          # Version Info
          /usr/libexec/PlistBuddy -c "Set :GhosttyCommit $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"
          /usr/libexec/PlistBuddy -c "Set :CFBundleVersion $GHOSTTY_BUILD" "macos/build/Release/Ghostty.app/Contents/Info.plist"
          /usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString $GHOSTTY_COMMIT" "macos/build/Release/Ghostty.app/Contents/Info.plist"

          # Updater
          /usr/libexec/PlistBuddy -c "Set :SUPublicEDKey $SPARKLE_KEY_PUB" "macos/build/Release/Ghostty.app/Contents/Info.plist"
          /usr/libexec/PlistBuddy -c "Delete :SUEnableAutomaticChecks" "macos/build/Release/Ghostty.app/Contents/Info.plist"

      - name: Codesign app bundle
        env:
          MACOS_CERTIFICATE: ${{ secrets.PROD_MACOS_CERTIFICATE }}
          MACOS_CERTIFICATE_PWD: ${{ secrets.PROD_MACOS_CERTIFICATE_PWD }}
          MACOS_CERTIFICATE_NAME: ${{ secrets.PROD_MACOS_CERTIFICATE_NAME }}
          MACOS_CI_KEYCHAIN_PWD: ${{ secrets.PROD_MACOS_CI_KEYCHAIN_PWD }}
        run: |
          # Turn our base64-encoded certificate back to a regular .p12 file
          echo $MACOS_CERTIFICATE | base64 --decode > certificate.p12

          # We need to create a new keychain, otherwise using the certificate will prompt
          # with a UI dialog asking for the certificate password, which we can't
          # use in a headless CI environment
          security create-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
          security default-keychain -s build.keychain
          security unlock-keychain -p "$MACOS_CI_KEYCHAIN_PWD" build.keychain
          security import certificate.p12 -k build.keychain -P "$MACOS_CERTIFICATE_PWD" -T /usr/bin/codesign
          security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_CI_KEYCHAIN_PWD" build.keychain

          # Codesign Sparkle. Some notes here:
          #   - The XPC services aren't used since we don't sandbox Ghostty,
          #     but since they're part of the build, they still need to be
          #     codesigned.
          #   - The binaries in the "Versions" folders need to NOT be symlinks.
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Downloader.xpc"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/XPCServices/Installer.xpc"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Autoupdate"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework/Versions/B/Updater.app"
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime "macos/build/Release/Ghostty.app/Contents/Frameworks/Sparkle.framework"

          # Codesign the app bundle
          /usr/bin/codesign --verbose -f -s "$MACOS_CERTIFICATE_NAME" -o runtime --entitlements "macos/Ghostty.entitlements" macos/build/Release/Ghostty.app

      - name: "Notarize app bundle"
        env:
          APPLE_NOTARIZATION_ISSUER: ${{ secrets.APPLE_NOTARIZATION_ISSUER }}
          APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
          APPLE_NOTARIZATION_KEY: ${{ secrets.APPLE_NOTARIZATION_KEY }}
        run: |
          # Store the notarization credentials so that we can prevent a UI password dialog
          # from blocking the CI
          echo "Create keychain profile"
          echo "$APPLE_NOTARIZATION_KEY" > notarization_key.p8
          xcrun notarytool store-credentials "notarytool-profile" --key notarization_key.p8 --key-id "$APPLE_NOTARIZATION_KEY_ID" --issuer "$APPLE_NOTARIZATION_ISSUER"
          rm notarization_key.p8

          # We can't notarize an app bundle directly, but we need to compress it as an archive.
          # Therefore, we create a zip file containing our app bundle, so that we can send it to the
          # notarization service
          echo "Creating temp notarization archive"
          ditto -c -k --keepParent "macos/build/Release/Ghostty.app" "notarization.zip"

          # Here we send the notarization request to the Apple's Notarization service, waiting for the result.
          # This typically takes a few seconds inside a CI environment, but it might take more depending on the App
          # characteristics. Visit the Notarization docs for more information and strategies on how to optimize it if
          # you're curious
          echo "Notarize app"
          xcrun notarytool submit "notarization.zip" --keychain-profile "notarytool-profile" --wait

          # Finally, we need to "attach the staple" to our executable, which will allow our app to be
          # validated by macOS even when an internet connection is not available.
          echo "Attach staple"
          xcrun stapler staple "macos/build/Release/Ghostty.app"

      # Zip up the app
      - name: Zip App
        run: |
          cd macos/build/Release
          zip -9 -r --symlinks ../../../ghostty-macos-universal-debug.zip Ghostty.app
          zip -9 -r --symlinks ../../../ghostty-macos-universal-debug-dsym.zip Ghostty.app.dSYM/

      # Update Blob Storage
      - name: Prep R2 Storage
        run: |
          mkdir blob
          mkdir -p blob/${GHOSTTY_COMMIT_LONG}
          cp ghostty-macos-universal-debug.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug.zip
          cp ghostty-macos-universal-debug-dsym.zip blob/${GHOSTTY_COMMIT_LONG}/ghostty-macos-universal-debug-dsym.zip
      - name: Upload to R2
        uses: ryand56/r2-upload-action@b801a390acbdeb034c5e684ff5e1361c06639e7c # v1.4
        with:
          r2-account-id: ${{ secrets.CF_R2_PR_ACCOUNT_ID }}
          r2-access-key-id: ${{ secrets.CF_R2_PR_AWS_KEY }}
          r2-secret-access-key: ${{ secrets.CF_R2_PR_SECRET_KEY }}
          r2-bucket: ghostty-pr
          source-dir: blob
          destination-dir: ./
