homebrew-core/.github/workflows/tests.yml

332 lines
14 KiB
YAML

name: CI
on:
push:
branches:
- master
pull_request:
env:
HOMEBREW_DEVELOPER: 1
HOMEBREW_GITHUB_ACTIONS: 1
HOMEBREW_NO_AUTO_UPDATE: 1
concurrency:
group: "tests-${{ github.ref }}"
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
tap_syntax:
if: github.repository == 'Homebrew/homebrew-core'
runs-on: ubuntu-22.04
container:
image: ghcr.io/homebrew/ubuntu22.04:master
env:
HOMEBREW_SIMULATE_MACOS_ON_LINUX: 1
outputs:
testing_formulae: ${{ steps.formulae-detect.outputs.testing_formulae }}
added_formulae: ${{ steps.formulae-detect.outputs.added_formulae }}
deleted_formulae: ${{ steps.formulae-detect.outputs.deleted_formulae }}
steps:
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- run: brew test-bot --only-tap-syntax
- run: brew test-bot --only-formulae-detect
if: github.event_name == 'pull_request'
id: formulae-detect
setup_tests:
permissions:
pull-requests: read
if: github.event_name == 'pull_request' && github.repository == 'Homebrew/homebrew-core'
runs-on: ubuntu-22.04
needs: tap_syntax
outputs:
syntax-only: ${{ steps.check-labels.outputs.syntax-only }}
linux-runner: ${{ steps.check-labels.outputs.linux-runner }}
logs-dir: ${{ steps.check-labels.outputs.logs-dir }}
fail-fast: ${{ steps.check-labels.outputs.fail-fast }}
test-dependents: ${{ steps.check-labels.outputs.test-dependents }}
timeout-minutes: ${{ steps.check-labels.outputs.timeout-minutes }}
test-bot-formulae-args: ${{ steps.check-labels.outputs.test-bot-formulae-args }}
test-bot-dependents-args: ${{ steps.check-labels.outputs.test-bot-dependents-args }}
steps:
- name: Check for CI labels
id: check-labels
uses: actions/github-script@v6
env:
TESTING_FORMULAE: ${{needs.tap_syntax.outputs.testing_formulae}}
ADDED_FORMULAE: ${{needs.tap_syntax.outputs.added_formulae}}
DELETED_FORMULAE: ${{needs.tap_syntax.outputs.deleted_formulae}}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const { data: { labels: labels } } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number
})
const label_names = labels.map(label => label.name)
if (label_names.includes('CI-syntax-only')) {
console.log('CI-syntax-only label found. Skipping tests job.')
core.setOutput('syntax-only', 'true')
} else {
console.log('No CI-syntax-only label found. Running tests job.')
core.setOutput('syntax-only', 'false')
}
if (label_names.includes('CI-linux-self-hosted')) {
core.setOutput('linux-runner', 'linux-self-hosted-1')
} else {
core.setOutput('linux-runner', 'ubuntu-22.04')
core.setOutput('logs-dir', '/github/home/.cache/Homebrew/Logs')
}
if (label_names.includes('CI-no-fail-fast')) {
console.log('CI-no-fail-fast label found. Continuing tests despite failing matrix builds.')
core.setOutput('fail-fast', 'false')
} else {
console.log('No CI-no-fail-fast label found. Stopping tests on first failing matrix build.')
core.setOutput('fail-fast', 'true')
}
if (label_names.includes('CI-skip-dependents')) {
console.log('CI-skip-dependents label found. Skipping brew test-bot --only-formulae-dependents.')
core.setOutput('test-dependents', 'false')
} else {
console.log('No CI-skip-dependents label found. Running brew test-bot --only-formulae-dependents.')
core.setOutput('test-dependents', 'true')
}
const maximum_long_pr_count = 2
if (label_names.includes('CI-long-timeout')) {
const labelCountQuery = `query($owner:String!, $name:String!, $label:String!) {
repository(owner:$owner, name:$name) {
pullRequests(last: 100, states: OPEN, labels: [$label]) {
totalCount
}
}
}`;
var long_pr_count;
try {
const response = await github.graphql(
labelCountQuery, {
owner: context.repo.owner,
name: context.repo.repo,
label: 'CI-long-timeout'
}
)
long_pr_count = response.repository.pullRequests.totalCount
} catch (error) {
// The GitHub API query errored, so fail open and assume 0 long PRs.
long_pr_count = 0
core.warning('CI-long-timeout label count query failed. Assuming no long PRs.')
}
if (long_pr_count > maximum_long_pr_count) {
core.setFailed(`Too many pull requests (${long_pr_count}) with the long-timeout label!`)
core.error(`Only ${maximum_long_pr_count} pull requests at a time can use this label.`)
core.error('Remove the long-timeout label from this or other PRs (once their CI has completed).')
}
console.log('CI-long-timeout label found. Setting long GitHub Actions timeout.')
core.setOutput('timeout-minutes', '4320')
} else {
console.log('No CI-long-timeout label found. Setting short GitHub Actions timeout.')
core.setOutput('timeout-minutes', '90')
if (label_names.includes('long build')) {
core.setFailed('PR requires the CI-long-timeout label but it is not set!')
core.error('If the longer timeout is not required, remove the "long build" label.')
core.error('Otherwise, add the "CI-long-timeout" label.')
core.error(`No more than ${maximum_long_pr_count} PRs at a time may use "CI-long-timeout".`)
}
}
const test_bot_formulae_args = ["--only-formulae", "--junit", "--only-json-tab", "--skip-dependents"]
test_bot_formulae_args.push(`--testing-formulae="${process.env.TESTING_FORMULAE}"`)
test_bot_formulae_args.push(`--added-formulae="${process.env.ADDED_FORMULAE}"`)
test_bot_formulae_args.push(`--deleted-formulae="${process.env.DELETED_FORMULAE}"`)
const test_bot_dependents_args = ["--only-formulae-dependents", "--junit"]
test_bot_dependents_args.push(`--testing-formulae="${process.env.TESTING_FORMULAE}"`)
if (label_names.includes('CI-test-bot-fail-fast')) {
console.log('CI-test-bot-fail-fast label found. Passing --fail-fast to brew test-bot.')
test_bot_formulae_args.push('--fail-fast')
test_bot_dependents_args.push('--fail-fast')
} else {
console.log('No CI-test-bot-fail-fast label found. Not passing --fail-fast to brew test-bot.')
}
if (label_names.includes('CI-build-dependents-from-source')) {
console.log('CI-build-dependents-from-source label found. Passing --build-dependents-from-source to brew test-bot.')
test_bot_dependents_args.push('--build-dependents-from-source')
} else {
console.log('No CI-build-dependents-from-source label found. Not passing --build-dependents-from-source to brew test-bot.')
}
if (label_names.includes('CI-skip-recursive-dependents')) {
console.log('CI-skip-recursive-dependents label found. Passing --skip-recursive-dependents to brew test-bot.')
test_bot_dependents_args.push('--skip-recursive-dependents')
} else {
console.log('No CI-skip-recursive-dependents label found. Not passing --skip-recursive-dependents to brew test-bot.')
}
if (label_names.includes('CI-skip-livecheck')) {
console.log('CI-skip-livecheck label found. Passing --skip-livecheck to brew test-bot.')
test_bot_formulae_args.push('--skip-livecheck')
} else {
console.log('No CI-skip-livecheck label found. Not passing --skip-livecheck to brew test-bot.')
}
core.setOutput('test-bot-formulae-args', test_bot_formulae_args.join(" "))
core.setOutput('test-bot-dependents-args', test_bot_dependents_args.join(" "))
tests:
needs: setup_tests
if: ${{github.event_name == 'pull_request' && fromJson(needs.setup_tests.outputs.syntax-only) == false}}
strategy:
matrix:
include:
- runner: "13-arm64-${{github.run_id}}-${{github.run_attempt}}"
- runner: "13-${{github.run_id}}-${{github.run_attempt}}"
- runner: "12-arm64"
- runner: "12-${{github.run_id}}-${{github.run_attempt}}"
- runner: "11-arm64"
- runner: "11-${{github.run_id}}-${{github.run_attempt}}"
- runner: ${{needs.setup_tests.outputs.linux-runner}}
container:
image: ghcr.io/homebrew/ubuntu22.04:master
options: --user=linuxbrew -e GITHUB_ACTIONS_HOMEBREW_SELF_HOSTED
workdir: /github/home
timeout: 4320
fail-fast: ${{fromJson(needs.setup_tests.outputs.fail-fast)}}
runs-on: ${{matrix.runner}}
container: ${{matrix.container}}
timeout-minutes: ${{ matrix.timeout || fromJson(needs.setup_tests.outputs.timeout-minutes) }}
defaults:
run:
shell: /bin/bash -e {0}
working-directory: ${{matrix.workdir || github.workspace}}
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
HOMEBREW_GITHUB_API_TOKEN: ${{secrets.GITHUB_TOKEN}}
steps:
- name: Set environment variables
if: runner.os == 'macOS'
run: |
echo 'PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin' >> $GITHUB_ENV
- name: Set up Homebrew
id: set-up-homebrew
uses: Homebrew/actions/setup-homebrew@master
- run: brew test-bot --only-cleanup-before
- run: brew test-bot --only-setup
- name: Run brew test-bot ${{ needs.setup_tests.outputs.test-bot-formulae-args }}
id: brew-test-bot-formulae
run: |
mkdir bottles
cd bottles
brew test-bot ${{ needs.setup_tests.outputs.test-bot-formulae-args }}
- name: Failures summary for brew test-bot --only-formulae
if: always()
uses: Homebrew/actions/failures-summary-and-bottle-result@master
with:
workdir: ${{matrix.workdir || github.workspace}}
result_path: bottles/steps_output.txt
step_name: 'Build summary on ${{ matrix.runner }}'
- name: Output brew linkage result
if: always()
uses: Homebrew/actions/failures-summary-and-bottle-result@master
with:
workdir: ${{matrix.workdir || github.workspace}}
result_path: bottles/linkage_output.txt
step_name: '`brew linkage` output on ${{ matrix.runner }}'
collapse: 'true'
- name: Output brew bottle result
if: always()
uses: Homebrew/actions/failures-summary-and-bottle-result@master
with:
workdir: ${{matrix.workdir || github.workspace}}
result_path: bottles/bottle_output.txt
step_name: '`brew bottle` output on ${{ matrix.runner }}'
collapse: 'true'
- name: Run brew test-bot ${{ needs.setup_tests.outputs.test-bot-dependents-args }} --skipped-or-failed-formulae=${{ steps.brew-test-bot-formulae.outputs.skipped_or_failed_formulae }}
if: ${{(success() || failure()) && fromJson(needs.setup_tests.outputs.test-dependents)}}
run: |
cd bottles
brew test-bot ${{ needs.setup_tests.outputs.test-bot-dependents-args }} --skipped-or-failed-formulae=${{ steps.brew-test-bot-formulae.outputs.skipped_or_failed_formulae }}
- name: Failures summary for brew test-bot ${{ needs.setup_tests.outputs.test-bot-dependents-args }} --skipped-or-failed-formulae=${{ steps.brew-test-bot-formulae.outputs.skipped_or_failed_formulae }}
if: ${{always() && fromJson(needs.setup_tests.outputs.test-dependents) == true}}
uses: Homebrew/actions/failures-summary-and-bottle-result@master
with:
workdir: ${{matrix.workdir || github.workspace}}
result_path: bottles/steps_output.txt
step_name: 'Dependent summary on ${{ matrix.runner }}'
collapse: 'true'
- name: Upload logs
if: always()
uses: actions/upload-artifact@main
with:
name: logs-${{ matrix.runner }}
path: ${{ runner.os == 'Linux' && needs.setup_tests.outputs.logs-dir || format('{0}/bottles/logs', matrix.workdir || github.workspace) }}
- name: Delete logs and home
if: always()
run: |
rm -rvf bottles/logs
rm -rvf bottles/home
- name: Count bottles
id: bottles
if: always()
run: |
cd bottles
count=$(ls *.json | wc -l | xargs echo -n)
echo "$count bottles"
echo "::set-output name=count::$count"
failures=$(ls failed/*.json | wc -l | xargs echo -n)
echo "$failures failed bottles"
echo "::set-output name=failures::$failures"
- name: Upload failed bottles
if: always() && steps.bottles.outputs.failures > 0
uses: actions/upload-artifact@main
with:
name: bottles-${{ matrix.runner }}
path: ${{matrix.workdir || github.workspace}}/bottles/failed
# Must be run before the `Upload bottles` step so that failed
# bottles are not included in the `bottles` artifact.
- name: Delete failed bottles
if: always()
run: rm -rvf bottles/failed
- name: Upload bottles
if: always() && steps.bottles.outputs.count > 0
uses: actions/upload-artifact@main
with:
name: bottles
path: ${{matrix.workdir || github.workspace}}/bottles
- name: Post cleanup
if: always()
run: |
brew test-bot --only-cleanup-after
rm -rvf bottles