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: '14.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@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 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@977bb373ede98d70efdf65b84cb5f73e068dcc2a # v3.0.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: - 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@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.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@634f93cb2916e3fdff6788551b99b062d0335ce0 # v5.0.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: ${{ secrets.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.