name: CI on: push: branches: - main - master pull_request: merge_group: permissions: contents: read env: HOMEBREW_DEVELOPER: 1 HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_ENV_HINTS: 1 HOMEBREW_NO_INSTALL_CLEANUP: 1 HOMEBREW_VERIFY_ATTESTATIONS: 1 defaults: run: shell: bash -xeuo pipefail {0} concurrency: group: "${{ github.ref }}" cancel-in-progress: ${{ github.event_name == 'pull_request' }} jobs: syntax: if: github.repository_owner == 'Homebrew' runs-on: ubuntu-latest steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: false cask: false test-bot: false - name: Cache Bundler RubyGems uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-syntax-${{ steps.set-up-homebrew.outputs.gems-hash }} restore-keys: ${{ runner.os }}-rubygems-syntax- - name: Install Bundler RubyGems run: brew install-bundler-gems --groups=style,typecheck - name: Install shellcheck and shfmt run: brew install shellcheck shfmt - name: Cache style cache uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ~/.cache/Homebrew/style key: syntax-style-cache-${{ github.sha }} restore-keys: syntax-style-cache- - run: brew style - run: brew typecheck - name: Check RuboCop filepaths working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew run: | public_apis="$(git grep -l "@api public" -- :^sorbet/ :^vendor/ | sort)" rubocop_docs="$(yq '.Style/Documentation.Include[]' .rubocop.yml | sort)" if [[ "${public_apis}" != "${rubocop_docs}" ]]; then echo "All public Homebrew APIs should be included in the \`Style/Documentation\` RuboCop." echo "Add/remove the following paths from \`Library/Homebrew/.rubocop.yml\` as appropriate:" echo "${public_apis} ${rubocop_docs}" | tr ' ' '\n' | sort | uniq -u exit 1 fi tap-syntax: name: tap syntax needs: syntax if: github.repository_owner == 'Homebrew' runs-on: macos-15 steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: true cask: true test-bot: true - name: Cache Bundler RubyGems uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-tap-syntax-${{ steps.set-up-homebrew.outputs.gems-hash }} restore-keys: ${{ runner.os }}-rubygems-tap-syntax- - name: Install Bundler RubyGems run: brew install-bundler-gems --groups=style - name: Cache style cache uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ~/.cache/Homebrew/style key: tap-syntax-style-cache-${{ github.sha }} restore-keys: tap-syntax-style-cache- - name: Run brew style on homebrew-core run: brew style homebrew/core - name: Set up all Homebrew taps run: | brew tap homebrew/command-not-found brew tap homebrew/portable-ruby # brew style doesn't like world writable directories sudo chmod -R g-w,o-w "$(brew --repo)/Library/Taps" - name: Run brew style on official taps run: | brew style homebrew/test-bot brew style homebrew/command-not-found \ homebrew/portable-ruby - name: Run brew style on homebrew/cask run: | brew style homebrew/cask formula-audit: name: formula audit needs: syntax if: github.repository_owner == 'Homebrew' && github.event_name != 'push' runs-on: ubuntu-latest container: image: ghcr.io/homebrew/brew:main steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: false cask: false test-bot: false - name: Run brew readall on homebrew/core run: brew readall --os=all --arch=all --aliases homebrew/core - name: Run brew audit --skip-style on homebrew/core run: brew audit --skip-style --except=version --tap=homebrew/core - name: Generate formula API run: brew generate-formula-api --dry-run cask-audit: name: cask audit needs: syntax if: github.repository_owner == 'Homebrew' && github.event_name != 'push' runs-on: macos-15 steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: true cask: true test-bot: false - name: Run brew readall on all casks run: brew readall --os=all --arch=all homebrew/cask - name: Run brew audit --skip-style on homebrew/cask run: | brew audit --skip-style --except=version --tap=homebrew/cask - name: Generate cask API run: brew generate-cask-api --dry-run vendored-gems: name: vendored gems needs: syntax runs-on: ubuntu-latest steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: false cask: false test-bot: false - name: Configure Git user uses: Homebrew/actions/git-user-config@main with: username: BrewTestBot # Can't cache this because we need to check that it doesn't fail the # "uncommitted RubyGems" step with a cold cache. - name: Install Bundler RubyGems run: brew install-bundler-gems --groups=all - name: Check for uncommitted RubyGems working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} run: git diff --stat --exit-code Library/Homebrew/vendor/bundle/ruby update-test: name: ${{ matrix.name }} runs-on: ${{ matrix.runs-on }} needs: syntax if: github.event_name != 'push' strategy: matrix: include: - name: update-test (Linux) runs-on: ubuntu-latest - name: update-test (macOS) runs-on: macos-latest steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: false cask: false test-bot: false - name: Run brew update-tests run: | brew update-test brew update-test --to-tag brew update-test --commit=HEAD tests: if: github.event_name != 'push' name: ${{ matrix.name }} needs: syntax runs-on: ${{ matrix.runs-on }} strategy: matrix: include: - name: tests (online) test-flags: --online --coverage runs-on: ubuntu-latest - name: tests (generic OS) test-flags: --generic --coverage runs-on: ubuntu-latest - name: tests (Linux) test-flags: --coverage runs-on: ubuntu-24.04 - name: tests (macOS) test-flags: --coverage runs-on: macos-15 steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: # We only test needs_homebrew_core tests on macOS because # homebrew/core is not available by default on GitHub-hosted Ubuntu # runners, and it's expensive to tap it. core: ${{ runner.os == 'macOS' }} cask: false test-bot: false - name: Cache Bundler RubyGems uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ matrix.runs-on }}-tests-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} restore-keys: ${{ matrix.runs-on }}-tests-rubygems- - run: brew config - name: Install Bundler RubyGems run: brew install-bundler-gems --groups=all - name: Create parallel test log directory run: mkdir tests - name: Cache parallel tests log uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: tests key: ${{ runner.os }}-${{ matrix.test-flags }}-parallel_runtime_rspec-${{ github.sha }} restore-keys: ${{ runner.os }}-${{ matrix.test-flags }}-parallel_runtime_rspec- - name: Install brew tests --online dependencies if: matrix.name == 'tests (online)' run: brew install subversion curl - name: Install brew tests macOS dependencies if: runner.os != 'Linux' run: | # Workaround GitHub Actions Python issues brew unlink python && brew link --overwrite python brew install subversion # brew tests doesn't like world writable directories - name: Cleanup permissions if: runner.os == 'Linux' run: sudo chmod -R g-w,o-w /home/linuxbrew/.linuxbrew/Homebrew - name: Run brew tests if: github.event_name == 'pull_request' || matrix.name != 'tests (online)' run: | # Retry multiple times to detect and submit flakiness to CodeCov (because rspec-retry is disabled). if [[ -n "${CODECOV_TOKEN-}" ]] then brew tests ${{ matrix.test-flags }} || brew tests ${{ matrix.test-flags }} else brew tests ${{ matrix.test-flags }} fi env: HOMEBREW_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} # These cannot be queried at the macOS level on GitHub Actions. HOMEBREW_LANGUAGES: en-GB CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Get RSpec JUnit XML filenames id: junit_xml working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} run: | mkdir -p Library/Homebrew/test/junit filenames=$(find Library/Homebrew/test/junit -name 'rspec*.xml' -print | tr '\n' ',') echo "filenames=${filenames%,}" >> "$GITHUB_OUTPUT" - uses: codecov/test-results-action@47f89e9acb64b76debcd5ea40642d25a4adced9f # v1.1.1 with: working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} files: ${{ steps.junit_xml.outputs.filenames }} disable_search: true token: ${{ secrets.CODECOV_TOKEN }} - uses: codecov/codecov-action@18283e04ce6e62d37312384ff67231eb8fd56d24 # v5.4.3 with: working-directory: ${{ steps.set-up-homebrew.outputs.repository-path }} files: Library/Homebrew/test/coverage/coverage.xml disable_search: true token: ${{ secrets.CODECOV_TOKEN }} test-bot: name: ${{ matrix.name }} needs: syntax if: github.repository_owner == 'Homebrew' && github.event_name != 'push' runs-on: ${{ matrix.runs-on }} container: ${{ matrix.container }} strategy: matrix: include: - name: test-bot (Linux arm64) runs-on: ubuntu-24.04-arm container: ghcr.io/homebrew/ubuntu24.04:latest - name: test-bot (Linux x86_64) runs-on: ubuntu-latest container: ghcr.io/homebrew/ubuntu22.04:main # Use Debian Old Stable for testing Homebrew's glibc support. - name: test-bot (Linux Homebrew glibc) runs-on: ubuntu-latest container: debian:oldstable - name: test-bot (macOS x86_64) runs-on: macos-13 - name: test-bot (macOS arm64) runs-on: macos-15 env: HOMEBREW_TEST_BOT_ANALYTICS: 1 HOMEBREW_ENFORCE_SBOM: 1 steps: - name: Install Homebrew and Homebrew's dependencies # All other images are built from our Homebrew Dockerfile. # This is the only one that needs to be set up manually. if: matrix.container == 'debian:oldstable' run: | # Slimmed down version from the Homebrew Dockerfile apt-get update apt-get install -y --no-install-recommends \ bzip2 \ ca-certificates \ curl \ file \ g++ \ git-core \ less \ locales \ make \ netbase \ patch \ procps \ sudo \ uuid-runtime \ tzdata # Install Homebrew mkdir -p /home/linuxbrew/.linuxbrew/bin # Don't do shallow clone or it's unshallowed by "Set up Homebrew" git clone https://github.com/Homebrew/brew.git /home/linuxbrew/.linuxbrew/Homebrew cd /home/linuxbrew/.linuxbrew/bin ln -s ../Homebrew/bin/brew brew echo "/home/linuxbrew/.linuxbrew/bin" >>"$GITHUB_PATH" - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: false cask: false test-bot: true - run: brew test-bot --only-cleanup-before - name: Setup environment variables run: | # Set enviroment variables to bypass `brew doctor` failures on Tier >=2 configurations if [[ "${MATRIX_NAME}" == "test-bot (Linux arm64)" ]]; then echo "HOMEBREW_ARM64_TESTING=1" >> "$GITHUB_ENV" elif [[ "${MATRIX_NAME}" == "test-bot (Linux Homebrew glibc)" ]]; then echo "HOMEBREW_GLIBC_TESTING=1" >> "$GITHUB_ENV" fi env: MATRIX_NAME: ${{ matrix.name }} - run: brew test-bot --only-setup - run: brew install gnu-tar - run: brew test-bot --only-formulae --only-json-tab --test-default-formula bundle-and-services: name: ${{ matrix.name }} needs: syntax if: github.repository_owner == 'Homebrew' && github.event_name != 'push' runs-on: ${{ matrix.runs-on }} strategy: matrix: include: - name: bundle and services (Linux) runs-on: ubuntu-latest - name: bundle and services (macOS) runs-on: macos-latest steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main with: core: false cask: false test-bot: false - run: brew test-bot --only-cleanup-before - run: brew test-bot --only-setup - name: Run brew bundle and brew services integration tests run: | cat <> Brewfile brew "git" brew "memcached", restart_service: true brew "postgresql@15", restart_service: true cask "1password" cask "1password-cli" cask "google-chrome" # VSCode cask is not available on Linux. vscode "github.copilot" if OS.mac? EOS brew bundle check && exit 1 brew bundle check --verbose && exit 1 brew bundle list brew bundle --verbose brew bundle --upgrade-formulae=memcached,postgresql@15 brew bundle upgrade brew bundle env | grep PATH | grep git brew bundle env --install brew bundle exec --services true brew bundle dump --force --describe brew services list brew services info memcached postgresql@15 brew services info --json memcached postgresql@15 brew services cleanup brew bundle cleanup --force analytics: name: ${{ matrix.name }} runs-on: ${{ matrix.runs-on }} strategy: matrix: include: - name: analytics (Linux) runs-on: ubuntu-latest - name: analytics (macOS) runs-on: macos-latest needs: syntax if: github.repository_owner == 'Homebrew' && github.event_name != 'push' steps: - name: Set up Homebrew id: set-up-homebrew uses: Homebrew/actions/setup-homebrew@main - name: Setup Python uses: actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065 # v5.6.0 with: python-version-file: ${{ steps.set-up-homebrew.outputs.repository-path }}/Library/Homebrew/formula-analytics/.python-version - name: Cache Homebrew Bundler RubyGems id: cache uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.2.3 with: path: ${{ steps.set-up-homebrew.outputs.gems-path }} key: ${{ runner.os }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} restore-keys: ${{ runner.os }}-rubygems- - name: Install Homebrew Bundler RubyGems if: steps.cache.outputs.cache-hit != 'true' run: brew install-bundler-gems - run: brew formula-analytics --setup - run: brew formula-analytics --install --json --days-ago=2 if: github.event.pull_request.head.repo.fork == false && (github.event_name == 'pull_request' && github.event.pull_request.user.login != 'dependabot[bot]') env: HOMEBREW_INFLUXDB_TOKEN: ${{ secrets.HOMEBREW_INFLUXDB_READ_TOKEN }} - run: brew generate-analytics-api if: github.event.pull_request.head.repo.fork == false && (github.event_name == 'pull_request' && github.event.pull_request.user.login != 'dependabot[bot]') env: HOMEBREW_INFLUXDB_TOKEN: ${{ secrets.HOMEBREW_INFLUXDB_READ_TOKEN }}