Merge pull request #20218 from Homebrew/migrate-semeru-jdk
[Homebrew/homebrew-cask-versions.git] / .github / workflows / ci.yml
blobc262022f16685d0d0c89ed5dc626f4d0c91bb9a2
1 name: CI
3 on:
4   pull_request:
5   workflow_dispatch:
6     inputs:
7       casks:
8         description: List of casks to audit (comma-separated)
9         required: true
10       skip_install:
11         description: Skip installation of casks
12         required: false
13         default: true
14         type: boolean
15       new_cask:
16         description: Apply new cask audit
17         required: false
18         default: false
19         type: boolean
21 env:
22   HOMEBREW_DEVELOPER: 1
23   HOMEBREW_NO_AUTO_UPDATE: 1
24   HOMEBREW_NO_INSTALL_FROM_API: 1
25   HOMEBREW_GITHUB_API_TOKEN: ${{ github.token }}
27 concurrency:
28   group: "${{ github.ref }}"
29   cancel-in-progress: ${{ github.event_name == 'pull_request' }}
31 permissions:
32   contents: read
34 jobs:
35   generate-matrix:
36     outputs:
37       matrix: ${{ steps.generate-matrix.outputs.matrix }}
38     runs-on: macos-latest
39     steps:
40       - name: Set up Homebrew
41         id: set-up-homebrew
42         uses: Homebrew/actions/setup-homebrew@master
43         with:
44           core: false
45           cask: true
46           test-bot: false
48       - name: Check out Pull Request
49         uses: actions/checkout@v4
50         with:
51           fetch-depth: 0
52           persist-credentials: false
54       - name: Generate CI matrix
55         id: generate-matrix
56         run: |
57           if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]
58           then
59             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 }}
60           else
61             brew ruby -- "$(brew --repository homebrew/cask)/cmd/lib/generate-matrix.rb" --url="${{ github.event.pull_request.url }}"
62           fi
64   test:
65     name: ${{ matrix.name }}
66     needs: generate-matrix
67     runs-on: ${{ matrix.runner }}
68     strategy:
69       fail-fast: false
70       matrix:
71         include: ${{ fromJson(needs.generate-matrix.outputs.matrix) }}
72     steps:
73       - name: Set up Homebrew
74         id: set-up-homebrew
75         uses: Homebrew/actions/setup-homebrew@master
76         with:
77           core: false
78           cask: true
79           test-bot: true
81       - name: Enable debug mode
82         run: |
83           echo 'HOMEBREW_DEBUG=1' >> "${GITHUB_ENV}"
84           echo 'HOMEBREW_VERBOSE=1' >> "${GITHUB_ENV}"
85         if: runner.debug
87       - name: Check out Pull Request
88         uses: actions/checkout@v4
89         with:
90           fetch-depth: 0
91           persist-credentials: false
93       - name: Clean up CI machine
94         run: |
95           if [ "${{ matrix.runner }}" == 'macos-12' ] && ! brew uninstall --cask julia && ! rm -r /Applications/Julia-*.app; then
96             echo '::warning::Removing Julia is no longer necessary.'
97           fi
99           if ! rm /usr/local/bin/dotnet; then
100             echo '::warning::Removing `dotnet` symlink is no longer necessary.'
101           fi
103           if ! rm /usr/local/bin/pod; then
104             echo '::warning::Removing `cocoapods` symlink is no longer necessary.'
105           fi
107           if ! rm /usr/local/bin/chromedriver; then
108             echo '::warning::Removing `chromedriver` symlink is no longer necessary.'
109           fi
111           brew unlink python && brew link --overwrite python
112         if: runner.os == 'macOS'
114       - name: Cache Homebrew Gems
115         id: cache
116         uses: actions/cache@v4
117         with:
118           path: ${{ steps.set-up-homebrew.outputs.gems-path }}
119           key: ${{ matrix.runner }}-rubygems-${{ steps.set-up-homebrew.outputs.gems-hash }}
120           restore-keys: ${{ matrix.runner }}-rubygems-
122       - name: Run brew test-bot --only-tap-syntax
123         id: tap-syntax
124         run: brew test-bot --tap '${{ matrix.tap }}' --only-tap-syntax
125         if: always() && !matrix.cask
127       - name: Run brew fetch --cask ${{ matrix.cask.token }}
128         id: fetch
129         run: |
130           brew fetch --cask --retry --force ${{ join(matrix.fetch_args, ' ') }} '${{ matrix.cask.path }}'
131         timeout-minutes: 30
132         if: >
133           always() &&
134           contains(fromJSON('["success", "skipped"]'), steps.tap-syntax.outcome) &&
135           matrix.cask
137       - name: Run brew audit --cask${{ (matrix.cask && ' ') || ' --tap ' }}${{ matrix.cask.token || matrix.tap }}
138         id: audit
139         run: |
140           brew audit --cask ${{ join(matrix.audit_args, ' ') }}${{ (matrix.cask && ' ') || ' --tap ' }}'${{ matrix.cask.token || matrix.tap }}'
141         timeout-minutes: 30
142         if: >
143           always() &&
144           contains(fromJSON('["success", "skipped"]'), steps.tap-syntax.outcome) &&
145           (!matrix.cask || steps.fetch.outcome == 'success') &&
146           !matrix.skip_audit
148       - name: Gather cask information
149         id: info
150         run: |
151           brew tap homebrew/cask-versions
152           brew ruby <<'EOF'
153             require 'cask/cask_loader'
154             require 'cask/installer'
156             cask = Cask::CaskLoader.load('${{ matrix.cask.path }}')
158             was_installed = cask.installed?
159             manual_installer = cask.artifacts.any? { |artifact|
160               artifact.is_a?(Cask::Artifact::Installer::ManualInstaller)
161             }
163             macos_requirement_satisfied = if macos_requirement = cask.depends_on.macos
164               macos_requirement.satisfied?
165             else
166               true
167             end
169             cask_conflicts = cask.conflicts_with&.dig(:cask).to_a.select { |c| Cask::CaskLoader.load(c).installed? }
170             formula_conflicts = cask.conflicts_with&.dig(:formula).to_a.select { |f| Formula[f].any_version_installed? }
172             installer = Cask::Installer.new(cask)
173             cask_and_formula_dependencies = installer.missing_cask_and_formula_dependencies
175             cask_dependencies = cask_and_formula_dependencies.select { |d| d.is_a?(Cask::Cask) }.map(&:full_name)
176             formula_dependencies = cask_and_formula_dependencies.select { |d| d.is_a?(Formula) }.map(&:full_name)
178             File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |f|
179               f.puts "was_installed=#{JSON.generate(was_installed)}"
180               f.puts "manual_installer=#{JSON.generate(manual_installer)}"
181               f.puts "macos_requirement_satisfied=#{JSON.generate(macos_requirement_satisfied)}"
182               f.puts "cask_conflicts=#{JSON.generate(cask_conflicts)}"
183               f.puts "cask_dependencies=#{JSON.generate(cask_dependencies)}"
184               f.puts "formula_conflicts=#{JSON.generate(formula_conflicts)}"
185               f.puts "formula_dependencies=#{JSON.generate(formula_dependencies)}"
186             end
187           EOF
188         if: always() && steps.fetch.outcome == 'success' && matrix.cask
190       - name: Uninstall conflicting formulae
191         run: |
192           brew uninstall --formula ${{ join(fromJSON(steps.info.outputs.formula_conflicts), ' ') }}
193         if: always() && steps.info.outcome == 'success' && join(fromJSON(steps.info.outputs.formula_conflicts)) != ''
194         timeout-minutes: 30
196       - name: Uninstall conflicting casks
197         run: |
198           brew uninstall --cask ${{ join(fromJSON(steps.info.outputs.cask_conflicts), ' ') }}
199         if: always() && steps.info.outcome == 'success' && join(fromJSON(steps.info.outputs.cask_conflicts)) != ''
200         timeout-minutes: 30
202       - name: Run brew uninstall --cask --zap ${{ matrix.cask.token }}
203         run: |
204           brew uninstall --cask --zap '${{ matrix.cask.path }}'
205         if: always() && steps.info.outcome == 'success' && fromJSON(steps.info.outputs.was_installed)
206         timeout-minutes: 30
208       - name: Take snapshot of installed and running apps and services
209         id: snapshot
210         run: |
211           brew ruby -r "$(brew --repository homebrew/cask)/cmd/lib/check.rb" <<'EOF'
212             File.open(ENV.fetch("GITHUB_OUTPUT"), "a") do |f|
213               f.puts "before=#{JSON.generate(Check.all)}"
214             end
215           EOF
216         if: always() && steps.info.outcome == 'success'
218       - name: Run brew install --cask ${{ matrix.cask.token }}
219         id: install
220         run: brew install --cask '${{ matrix.cask.path }}'
221         if: >
222           always() && steps.info.outcome == 'success' &&
223           fromJSON(steps.info.outputs.macos_requirement_satisfied) &&
224           !matrix.skip_install
225         timeout-minutes: 30
227       - name: Run brew uninstall --cask ${{ matrix.cask.token }}
228         run: brew uninstall --cask '${{ matrix.cask.path }}'
229         if: always() && steps.install.outcome == 'success' && !fromJSON(steps.info.outputs.manual_installer)
230         timeout-minutes: 30
232       - name: Uninstall formula dependencies
233         run: |
234           brew uninstall --formula ${{ join(fromJSON(steps.info.outputs.formula_dependencies), ' ') }}
235         if: always() && steps.install.outcome == 'success' && join(fromJSON(steps.info.outputs.formula_dependencies)) != ''
236         timeout-minutes: 30
238       - name: Uninstall cask dependencies
239         run: |
240           brew uninstall --cask ${{ join(fromJSON(steps.info.outputs.cask_dependencies), ' ') }}
241         if: always() && steps.install.outcome == 'success' && join(fromJSON(steps.info.outputs.cask_dependencies)) != ''
242         timeout-minutes: 30
244       - name: Compare installed and running apps and services with snapshot
245         run: |
246           brew ruby -r "$(brew --repository homebrew/cask)/cmd/lib/check.rb" <<'EOF'
247             require "cask/cask_loader"
248             require "utils/github/actions"
250             before = JSON.parse(<<~'EOS').transform_keys(&:to_sym)
251               ${{ steps.snapshot.outputs.before }}
252             EOS
253             after = Check.all
255             cask = Cask::CaskLoader.load('${{ matrix.cask.path }}')
256             errors = Check.errors(before, after, cask: cask)
258             errors.each do |error|
259               onoe error
260               puts GitHub::Actions::Annotation.new(:error, error, file: '${{ matrix.cask.path }}')
261             end
263             exit 1 if errors.any?
264           EOF
265         if: always() && steps.snapshot.outcome == 'success'
267   conclusion:
268     name: conclusion
269     needs: test
270     runs-on: ubuntu-latest
271     if: always()
272     steps:
273       - name: Result
274         run: ${{ needs.test.result == 'success' }}