Synchronize CI configuration from homebrew/cask
.
#328
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 | |
run: | | |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]] | |
then | |
brew ruby -- "$(brew --repository homebrew/cask)/cmd/lib/generate-matrix.rb" ${{ github.event.inputs.skip_install && '--skip-install' }} ${{ github.event.inputs.new_cask && '--new' }} --casks=${{ github.event.inputs.casks }} | |
elif [[ "${GITHUB_EVENT_NAME}" == "push" ]] | |
then | |
brew ruby -- "$(brew --repository homebrew/cask)/cmd/lib/generate-matrix.rb" --syntax-only | |
else | |
brew ruby -- "$(brew --repository homebrew/cask)/cmd/lib/generate-matrix.rb" --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 | |
run: | | |
if [ "${{ matrix.runner }}" == 'macos-12' ] && ! brew uninstall --cask julia && ! rm -r /Applications/Julia-*.app; then | |
echo '::warning::Removing Julia is no longer necessary.' | |
fi | |
if ! rm /usr/local/share/man/man1/al.1 || \ | |
! sudo rm /etc/paths.d/mono-commands || \ | |
! sudo rm -r /Library/Frameworks/Mono.framework || \ | |
! sudo pkgutil --forget com.xamarin.mono-MDK.pkg; then | |
echo '::warning::Uninstalling Mono is no longer necessary.' | |
fi | |
if ! rm /usr/local/bin/dotnet; then | |
echo "::warning::Removing \`dotnet\` symlink is no longer necessary." | |
fi | |
if ! rm /usr/local/bin/pod; then | |
echo "::warning::Removing \`cocoapods\` symlink is no longer necessary." | |
fi | |
if ! rm /usr/local/bin/chromedriver; then | |
echo "::warning::Removing \`chromedriver\` symlink is no longer necessary." | |
fi | |
brew unlink python && brew link --overwrite python | |
if: runner.os == 'macOS' | |
- 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 }}') | |
was_installed = cask.installed? | |
manual_installer = cask.artifacts.any? { |artifact| | |
artifact.is_a?(Cask::Artifact::Installer::ManualInstaller) | |
} | |
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 "was_installed=#{JSON.generate(was_installed)}" | |
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 --zap ${{ matrix.cask.token }} | |
run: | | |
brew uninstall --cask --zap '${{ matrix.cask.path }}' | |
if: always() && steps.info.outcome == 'success' && fromJSON(steps.info.outputs.was_installed) | |
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' }} |