332 lines
14 KiB
YAML
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
|