name: Installer Package
on:
  push:
    branches:
      - '**'
    tags-ignore:
      - '**'
    paths:
      - .github/workflows/pkg-installer.yml
      - package/**/*
  release:
    types:
      - published
env:
  PKG_APPLE_DEVELOPER_TEAM_ID: ${{ secrets.PKG_APPLE_DEVELOPER_TEAM_ID }}
  HOMEBREW_NO_ANALYTICS_THIS_RUN: 1
  HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT: 1

permissions: {}

defaults:
  run:
    shell: bash -xeuo pipefail {0}

jobs:
  build:
    if: github.repository_owner == 'Homebrew' && github.actor != 'dependabot[bot]'
    runs-on: macos-15
    outputs:
      installer_path: "Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg"
    env:
      TEMPORARY_CERTIFICATE_FILE: 'homebrew_developer_id_installer_certificate.p12'
      TEMPORARY_KEYCHAIN_FILE: 'homebrew_installer_signing.keychain-db'
      # Set to the oldest supported version of macOS
      HOMEBREW_MACOS_OLDEST_SUPPORTED: '13.0'
    permissions:
      contents: read # for code access
      attestations: write # for actions/attest-build-provenance
      id-token: write # for actions/attest-build-provenance
    steps:
      - name: Remove existing API cache (to force update)
        run: rm -rvf ~/Library/Caches/Homebrew/api

      - name: Set up Homebrew
        id: set-up-homebrew
        uses: Homebrew/actions/setup-homebrew@main
        with:
          core: false
          cask: false
          test-bot: false

      - name: Install Pandoc
        run: brew install pandoc

      - name: Create and unlock temporary macOS keychain
        run: |
          TEMPORARY_KEYCHAIN_PASSWORD="$(openssl rand -base64 20)"
          TEMPORARY_KEYCHAIN_PATH="${RUNNER_TEMP}/${TEMPORARY_KEYCHAIN_FILE}"
          security create-keychain -p "${TEMPORARY_KEYCHAIN_PASSWORD}" "${TEMPORARY_KEYCHAIN_PATH}"
          security set-keychain-settings -l -u -t 21600 "${TEMPORARY_KEYCHAIN_PATH}"
          security unlock-keychain -p "${TEMPORARY_KEYCHAIN_PASSWORD}" "${TEMPORARY_KEYCHAIN_PATH}"

      - name: Create temporary certificate file
        env:
          PKG_APPLE_SIGNING_CERTIFICATE_BASE64: ${{ secrets.PKG_APPLE_SIGNING_CERTIFICATE_BASE64 }}
        run: echo -n "${PKG_APPLE_SIGNING_CERTIFICATE_BASE64}" |
             base64 --decode --output="${RUNNER_TEMP}/${TEMPORARY_CERTIFICATE_FILE}"

      - name: Import certificate file into macOS keychain
        env:
          PKG_APPLE_SIGNING_CERTIFICATE_PASSWORD: ${{ secrets.PKG_APPLE_SIGNING_CERTIFICATE_PASSWORD }}
        run: security import "${RUNNER_TEMP}/${TEMPORARY_CERTIFICATE_FILE}"
                             -k "${RUNNER_TEMP}/${TEMPORARY_KEYCHAIN_FILE}"
                             -P "${PKG_APPLE_SIGNING_CERTIFICATE_PASSWORD}"
                             -t cert
                             -f pkcs12
                             -A

      - name: Clean up temporary certificate file
        if: ${{ always() }}
        run: rm -f "${RUNNER_TEMP}/${TEMPORARY_CERTIFICATE_FILE}"

      - name: Checkout another Homebrew to brew subdirectory
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
        with:
          path: brew
          fetch-depth: 0
          persist-credentials: false

      - name: Get Homebrew version from Git
        id: homebrew-version
        run: echo "version=$(git -C brew describe --tags --always)" >> "${GITHUB_OUTPUT}"

      - name: Copy Homebrew API cache to brew subdirectory
        run: cp -vR ~/Library/Caches/Homebrew/api brew/cache_api

      - name: Open macOS keychain
        run: security list-keychain -d user -s "${RUNNER_TEMP}/${TEMPORARY_KEYCHAIN_FILE}"

      - name: Build Homebrew installer component package
        env:
          HOMEBREW_VERSION: ${{ steps.homebrew-version.outputs.version }}
        # Note: `Library/Homebrew/test/support/fixtures/` contains unsigned
        # binaries so it needs to be excluded from notarization.
        run: pkgbuild --root brew
                      --scripts brew/package/scripts
                      --identifier sh.brew.homebrew
                      --version "${HOMEBREW_VERSION}"
                      --install-location /opt/homebrew
                      --filter .DS_Store
                      --filter "(.*)/Library/Homebrew/test/support/fixtures/"
                      --min-os-version "${HOMEBREW_MACOS_OLDEST_SUPPORTED}"
                      --sign "${PKG_APPLE_DEVELOPER_TEAM_ID}"
                      Homebrew.pkg

      - name: Convert Homebrew license file to RTF
        run: (printf "### " && cat brew/LICENSE.txt) |
              pandoc --from markdown --standalone --output brew/package/resources/LICENSE.rtf

      - name: Build Homebrew installer product package
        env:
          HOMEBREW_VERSION: ${{ steps.homebrew-version.outputs.version }}
        run: productbuild --resources brew/package/resources
                          --distribution brew/package/Distribution.xml
                          --package-path Homebrew.pkg
                          --sign "${PKG_APPLE_DEVELOPER_TEAM_ID}"
                          "Homebrew-${HOMEBREW_VERSION}.pkg"

      - name: Clean up temporary macOS keychain
        if: ${{ always() }}
        run: |
          if [[ -f "${RUNNER_TEMP}/${TEMPORARY_KEYCHAIN_FILE}" ]]
          then
            security delete-keychain "${RUNNER_TEMP}/${TEMPORARY_KEYCHAIN_FILE}"
          fi

      - name: Generate build provenance
        uses: actions/attest-build-provenance@e8998f949152b193b063cb0ec769d69d929409be # v2.4.0
        with:
          subject-path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg

      - name: Upload installer to GitHub Actions
        uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
        with:
          name: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
          path: Homebrew-${{ steps.homebrew-version.outputs.version }}.pkg
  test:
    needs: build
    name: "test (${{matrix.name}})"
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        include:
          # Intel
          - runner: macos-13
            name: macos-13-x86_64
          # Apple Silicon
          - runner: macos-14
            name: macos-14-arm64
          - runner: macos-15
            name: macos-15-arm64
    steps:
      - name: Download installer from GitHub Actions
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: "${{ needs.build.outputs.installer_path }}"

      - name: Unset global Git safe directory setting
        run: git config --global --unset-all safe.directory

      - name: Remove existing Homebrew installations
        run: |
          sudo rm -rf brew /{usr/local,opt/homebrew}/{Cellar,Caskroom,Homebrew/Library/Taps}
          brew cleanup --prune-prefix
          sudo rm -rf /usr/local/{bin/brew,Homebrew} /opt/homebrew /home/linuxbrew

      - name: Zero existing installer logs
        run: echo | sudo tee /var/log/install.log

      - name: Install Homebrew from installer package
        env:
          INSTALLER_PATH: ${{ needs.build.outputs.installer_path }}
        run: sudo installer -verbose -pkg "${INSTALLER_PATH}" -target /

      - name: Output installer logs
        if: ${{ always() }}
        run: sudo cat /var/log/install.log

      - run: brew config

      - run: brew doctor

      - name: Zero existing installer logs (again)
        run: echo | sudo tee /var/log/install.log

      - name: Reinstall Homebrew from installer package
        env:
          INSTALLER_PATH: ${{ needs.build.outputs.installer_path }}
        run: sudo installer -verbose -pkg "${INSTALLER_PATH}" -target /

      - name: Output installer logs (again)
        if: ${{ always() }}
        run: sudo cat /var/log/install.log

      - run: brew config

      - run: brew doctor

  upload:
    needs: [build, test]
    runs-on: macos-15
    permissions:
      # To write assets to GitHub release
      contents: write
    steps:
      - name: Download installer from GitHub Actions
        uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
        with:
          name: "${{ needs.build.outputs.installer_path }}"

      - name: Notarize Homebrew installer package
        env:
          PKG_APPLE_ID_EMAIL: ${{ secrets.PKG_APPLE_ID_EMAIL }}
          PKG_APPLE_ID_APP_SPECIFIC_PASSWORD: ${{ secrets.PKG_APPLE_ID_APP_SPECIFIC_PASSWORD }}
          INSTALLER_PATH: ${{ needs.build.outputs.installer_path }}
        run: xcrun notarytool submit "${INSTALLER_PATH}"
                                    --team-id  "${PKG_APPLE_DEVELOPER_TEAM_ID}"
                                    --apple-id "${PKG_APPLE_ID_EMAIL}"
                                    --password "${PKG_APPLE_ID_APP_SPECIFIC_PASSWORD}"
                                    --wait

      - name: Upload installer to GitHub release
        if: github.event_name == 'release'
        env:
          GH_TOKEN: ${{ github.token }}
          INSTALLER_PATH: ${{ needs.build.outputs.installer_path }}
        run: |
          VERSION="${INSTALLER_PATH#Homebrew-}"
          VERSION="${VERSION%.pkg}"
          gh release upload --repo Homebrew/brew "${VERSION}" "${INSTALLER_PATH}"

  issue:
    needs: [build, test, upload]
    if: always() && github.event_name == 'release'
    runs-on: ubuntu-latest
    env:
      RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
    permissions:
      # To create or update issues
      issues: write
    steps:
      - name: Open, update, or close pkg installer issue
        uses: Homebrew/actions/create-or-update-issue@main
        with:
          title: Failed to publish pkg installer
          body: >
            The pkg installer workflow [failed](${{ env.RUN_URL }}) for release
            ${{ github.ref_name }}. No pkg installer was uploaded to the GitHub
            release.
          labels: bug,release blocker
          update-existing: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}
          close-existing: ${{ needs.upload.result == 'success' }}
          close-from-author: github-actions[bot]
          close-comment: >
            The pkg installer workflow [succeeded](${{ env.RUN_URL }}) for
            release ${{ github.ref_name }}. Closing this issue.
