Merge pull request #193081 from Homebrew/bump-treesheets-11989416069 #161272
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: CI | |
on: | |
push: | |
branches: | |
- master | |
pull_request: | |
workflow_dispatch: | |
inputs: | |
casks: | |
description: List of casks to audit (comma-separated) | |
required: true | |
skip_install: | |
description: Skip installation of casks | |
required: false | |
default: true | |
type: boolean | |
new_cask: | |
description: Apply new cask audit | |
required: false | |
default: false | |
type: boolean | |
env: | |
HOMEBREW_DEVELOPER: 1 | |
HOMEBREW_NO_AUTO_UPDATE: 1 | |
HOMEBREW_NO_INSTALL_FROM_API: 1 | |
HOMEBREW_GITHUB_API_TOKEN: ${{ github.token }} | |
concurrency: | |
group: "${{ github.ref }}" | |
cancel-in-progress: ${{ github.event_name == 'pull_request' }} | |
permissions: | |
contents: read | |
jobs: | |
generate-matrix: | |
outputs: | |
matrix: ${{ steps.generate-matrix.outputs.matrix }} | |
runs-on: macos-latest | |
steps: | |
- name: Set up Homebrew | |
id: set-up-homebrew | |
uses: Homebrew/actions/setup-homebrew@master | |
with: | |
core: false | |
cask: true | |
test-bot: false | |
- name: Check out Pull Request | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
persist-credentials: false | |
- name: Generate CI matrix | |
id: generate-matrix | |
env: | |
INPUT_CASKS: ${{ github.event.inputs.casks }} | |
run: | | |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]] | |
then | |
# shellcheck disable=SC2086 # $INPUT_CASKS is a space-separated list of cask tokens | |
brew generate-cask-ci-matrix ${{ github.event.inputs.skip_install && '--skip-install' }} ${{ github.event.inputs.new_cask && '--new' }} --casks $INPUT_CASKS | |
elif [[ "${GITHUB_EVENT_NAME}" == "push" ]] | |
then | |
brew generate-cask-ci-matrix --syntax-only | |
else | |
brew generate-cask-ci-matrix --url "${{ github.event.pull_request.url }}" | |
fi | |
test: | |
name: ${{ matrix.name }} | |
needs: generate-matrix | |
runs-on: ${{ matrix.runner }} | |
strategy: | |
fail-fast: false | |
matrix: | |
include: ${{ fromJson(needs.generate-matrix.outputs.matrix) }} | |
steps: | |
- name: Set up Homebrew | |
id: set-up-homebrew | |
uses: Homebrew/actions/setup-homebrew@master | |
with: | |
core: false | |
cask: true | |
test-bot: true | |
- name: Enable debug mode | |
run: | | |
echo 'HOMEBREW_DEBUG=1' >> "${GITHUB_ENV}" | |
echo 'HOMEBREW_VERBOSE=1' >> "${GITHUB_ENV}" | |
if: runner.debug | |
- name: Check out Pull Request | |
uses: actions/checkout@v4 | |
with: | |
fetch-depth: 0 | |
persist-credentials: false | |
- name: Clean up CI machine | |
if: runner.os == 'macOS' | |
run: brew test-bot --cleanup --only-cleanup-before | |
- name: Cache Homebrew Gems | |
id: cache | |
uses: actions/cache@v4 | |
with: | |
path: ${{ steps.set-up-homebrew.outputs.gems-path }} | |
key: ${{ matrix.runner }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }} | |
restore-keys: ${{ matrix.runner }}-rubygems- | |
- name: Cache style cache | |
if: runner.os == 'macOS' | |
uses: actions/cache@v4 | |
with: | |
path: ~/Library/Caches/Homebrew/style | |
key: macos-style-cache-${{ github.sha }} | |
restore-keys: macos-style-cache- | |
- name: Run brew test-bot --only-tap-syntax | |
id: tap-syntax | |
run: brew test-bot --tap '${{ matrix.tap }}' --only-tap-syntax | |
if: always() && !matrix.cask | |
- name: Run brew fetch --cask ${{ matrix.cask.token }} | |
id: fetch | |
run: | | |
brew fetch --cask --retry --force ${{ join(matrix.fetch_args, ' ') }} '${{ matrix.cask.path }}' | |
timeout-minutes: 30 | |
if: > | |
always() && | |
contains(fromJSON('["success", "skipped"]'), steps.tap-syntax.outcome) && | |
matrix.cask | |
- name: Run brew audit --cask${{ (matrix.cask && ' ') || ' --tap ' }}${{ matrix.cask.token || matrix.tap }} | |
id: audit | |
run: | | |
brew audit --cask ${{ join(matrix.audit_args, ' ') }}${{ (matrix.cask && ' ') || ' --tap ' }}'${{ matrix.cask.token || matrix.tap }}' | |
timeout-minutes: 30 | |
if: > | |
always() && | |
contains(fromJSON('["success", "skipped"]'), steps.tap-syntax.outcome) && | |
(!matrix.cask || steps.fetch.outcome == 'success') && | |
!matrix.skip_audit | |
- name: Gather cask information | |
id: info | |
run: | | |
brew ruby <<'EOF' | |
require 'cask/cask_loader' | |
require 'cask/installer' | |
cask = Cask::CaskLoader.load('${{ matrix.cask.path }}') | |
manual_installer = cask.artifacts.any? do |artifact| | |
if defined?(artifact.manual_install) | |
artifact.manual_install | |
end | |
end | |
macos_requirement_satisfied = if macos_requirement = cask.depends_on.macos | |
macos_requirement.satisfied? | |
else | |
true | |
end | |
cask_conflicts = cask.conflicts_with&.dig(:cask).to_a.select { |c| Cask::CaskLoader.load(c).installed? } | |
formula_conflicts = cask.conflicts_with&.dig(:formula).to_a.select { |f| Formula[f].any_version_installed? } | |
installer = Cask::Installer.new(cask) | |
cask_and_formula_dependencies = installer.missing_cask_and_formula_dependencies | |
cask_dependencies = cask_and_formula_dependencies.select { |d| d.is_a?(Cask::Cask) }.map(&:full_name) | |
formula_dependencies = cask_and_formula_dependencies.select { |d| d.is_a?(Formula) }.map(&:full_name) | |
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |f| | |
f.puts "manual_installer=#{JSON.generate(manual_installer)}" | |
f.puts "macos_requirement_satisfied=#{JSON.generate(macos_requirement_satisfied)}" | |
f.puts "cask_conflicts=#{JSON.generate(cask_conflicts)}" | |
f.puts "cask_dependencies=#{JSON.generate(cask_dependencies)}" | |
f.puts "formula_conflicts=#{JSON.generate(formula_conflicts)}" | |
f.puts "formula_dependencies=#{JSON.generate(formula_dependencies)}" | |
end | |
EOF | |
if: always() && steps.fetch.outcome == 'success' && matrix.cask | |
- name: Uninstall conflicting formulae | |
run: | | |
brew uninstall --formula ${{ join(fromJSON(steps.info.outputs.formula_conflicts), ' ') }} | |
if: always() && steps.info.outcome == 'success' && join(fromJSON(steps.info.outputs.formula_conflicts)) != '' | |
timeout-minutes: 30 | |
- name: Uninstall conflicting casks | |
run: | | |
brew uninstall --cask ${{ join(fromJSON(steps.info.outputs.cask_conflicts), ' ') }} | |
if: always() && steps.info.outcome == 'success' && join(fromJSON(steps.info.outputs.cask_conflicts)) != '' | |
timeout-minutes: 30 | |
- name: Run brew uninstall --cask --force --zap ${{ matrix.cask.token }} | |
run: | | |
brew uninstall --cask --force --zap '${{ matrix.cask.path }}' | |
if: always() && steps.info.outcome == 'success' | |
timeout-minutes: 30 | |
- name: Take snapshot of installed and running apps and services | |
id: snapshot | |
run: | | |
brew ruby -r "$(brew --repository homebrew/cask)/cmd/lib/check.rb" <<'EOF' | |
File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |f| | |
f.puts "before=#{JSON.generate(Check.all)}" | |
end | |
EOF | |
if: always() && steps.info.outcome == 'success' | |
- name: Run brew install --cask ${{ matrix.cask.token }} | |
id: install | |
run: brew install --cask '${{ matrix.cask.path }}' | |
if: > | |
always() && steps.info.outcome == 'success' && | |
fromJSON(steps.info.outputs.macos_requirement_satisfied) && | |
!matrix.skip_install | |
timeout-minutes: 30 | |
- name: Run brew uninstall --cask ${{ matrix.cask.token }} | |
run: brew uninstall --cask '${{ matrix.cask.path }}' | |
if: always() && steps.install.outcome == 'success' && !fromJSON(steps.info.outputs.manual_installer) | |
timeout-minutes: 30 | |
- name: Uninstall cask dependencies | |
run: | | |
brew uninstall --cask ${{ join(fromJSON(steps.info.outputs.cask_dependencies), ' ') }} | |
if: always() && steps.install.outcome == 'success' && join(fromJSON(steps.info.outputs.cask_dependencies)) != '' | |
timeout-minutes: 30 | |
- name: Compare installed and running apps and services with snapshot | |
run: | | |
brew ruby -r "$(brew --repository homebrew/cask)/cmd/lib/check.rb" <<'EOF' | |
require "cask/cask_loader" | |
require "utils/github/actions" | |
before = JSON.parse(<<~'EOS').transform_keys(&:to_sym) | |
${{ steps.snapshot.outputs.before }} | |
EOS | |
after = Check.all | |
cask = Cask::CaskLoader.load('${{ matrix.cask.path }}') | |
errors = Check.errors(before, after, cask: cask) | |
errors.each do |error| | |
onoe error | |
puts GitHub::Actions::Annotation.new(:error, error, file: '${{ matrix.cask.path }}') | |
end | |
exit 1 if errors.any? | |
EOF | |
if: always() && steps.snapshot.outcome == 'success' | |
conclusion: | |
name: conclusion | |
needs: test | |
runs-on: ubuntu-latest | |
if: always() | |
steps: | |
- name: Result | |
run: ${{ needs.test.result == 'success' }} |