Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,17 @@ jobs:
if: always()

test:
name: test (${{ matrix.os }}, Bazel ${{ matrix.bazel || 'pinned' }})
needs: pre-commit
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# Empty selects .bazelversion in both the checkout and consumer workspaces.
bazel: ["7.7.2", "8.8.2", ""]
runs-on: ${{ matrix.os }}
env:
USE_BAZEL_VERSION: ${{ matrix.bazel }}
steps:
- uses: actions/checkout@v7
- name: Configure report capture
Expand All @@ -65,7 +70,7 @@ jobs:
cache-save: ${{ github.ref == 'refs/heads/main' }}
bazelisk-cache: true
# Store build cache per workflow.
disk-cache: ${{ github.workflow }}
disk-cache: ${{ github.workflow }}-${{ matrix.bazel || 'pinned' }}
# Share repository cache between workflows.
repository-cache: true
- name: Built & Test
Expand Down Expand Up @@ -103,7 +108,7 @@ jobs:
if: always()
uses: actions/upload-artifact@v7
with:
name: test-results-${{ matrix.os }}
name: test-results-${{ matrix.os }}-bazel-${{ matrix.bazel || 'pinned' }}
path: ${{ runner.temp }}/test-results
if-no-files-found: warn
retention-days: 7
Expand Down
6 changes: 4 additions & 2 deletions bashtest/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,10 @@ bashtest(

The script can invoke `"${GENERATOR}" --output "${TEST_TMPDIR}/result"`.
Tools must be executable Bazel targets; their runtime runfiles are included.
This uses the test execution group, including `exec_group_compatible_with` on Bazel 8 and newer, so
cross-compilation builds helpers for the platform running the test. `data`
This uses the test execution group, so cross-compilation builds helpers for the
platform running the test. Set `exec_group_compatible_with = {"test": [...]}` on
Bazel 9 and newer; on Bazel 7 and 8, use `exec_compatible_with = [...]`, which also
constrains the test group on those versions. `data`
continues to use the target configuration. Do not put the same target in both
`data` and `tools`. Multiple names may refer to the same tool.

Expand Down
12 changes: 7 additions & 5 deletions bashtest/tests/tools_consumer_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,13 @@ bashtest(
tags = ["manual"],
)
EOF
# Bazel 7 predates per-group constraints; still verify execution-platform tools.
if [[ "$(bazel --version)" == 'bazel 7.'* ]]; then
sed 's/exec_group_compatible_with = {"test": \[":test_role"\]}/exec_compatible_with = [":test_role"]/' BUILD.bazel > BUILD.tmp
mv BUILD.tmp BUILD.bazel
fi
# Before Bazel 9, default execution constraints also apply to the test group.
case "$(bazel --version)" in
'bazel 7.'*|'bazel 8.'*)
sed 's/exec_group_compatible_with = {"test": \[":test_role"\]}/exec_compatible_with = [":test_role"]/' BUILD.bazel > BUILD.tmp
mv BUILD.tmp BUILD.bazel
;;
esac
cat > consumer.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
Expand Down
29 changes: 21 additions & 8 deletions bashtest/tests/windows_runtime_test.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,36 @@ foreach ($name in @('bash', 'grep', 'sed', 'diff')) {
# collects a failed command's outputs too.
$reports = if ($env:BASHTEST_CI_REPORTS) { $env:BASHTEST_CI_REPORTS } else { Join-Path $env:TEMP ([guid]::NewGuid().ToString()) }
New-Item -ItemType Directory -Path $reports -Force | Out-Null
& $bazel test //... "--build_event_json_file=$reports/root-default.json"
# Resolve the downloaded source, not an execroot link pruned by later builds.
$outputBase = & $bazel info output_base
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$bashFile = & $bazel cquery '@bashtest_windows_runtime//:usr/bin/bash.exe' --output=files
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$bash = Join-Path $outputBase $bashFile
$version = & $bazel --version
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$testShell = @()
if ($version -match '^bazel 7\.') {
# Native Bazel 7 sh_binary fixtures do not resolve rules_shell toolchains.
# Point their embedded interpreter at our downloaded runtime, never WSL.
$testShell = @("--shell_executable=$bash")
}
& $bazel test //... @testShell "--build_event_json_file=$reports/root-default.json"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& $bazel run //tools:ci_reports -- collect --events "$reports/root-default.json" --output "$reports/root-default"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& $bazel test --enable_runfiles=no //... "--build_event_json_file=$reports/root-manifest.json"
& $bazel test --enable_runfiles=no //... @testShell "--build_event_json_file=$reports/root-manifest.json"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
& $bazel run //tools:ci_reports -- collect --events "$reports/root-manifest.json" --output "$reports/root-manifest"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# No shell override: bashtest itself must bootstrap its declared runtime.
& $bazel run --enable_runfiles=no //bashtest/tests:runtime_test "--build_event_json_file=$reports/root-runtime.json"
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
# Drive the existing external-consumer tests with the downloaded interpreter.
# Resolve the downloaded source, not an execroot link pruned by later builds.
$outputBase = & $bazel info output_base
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$bashFile = & $bazel cquery '@bashtest_windows_runtime//:usr/bin/bash.exe' --output=files
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
$bash = Join-Path $outputBase $bashFile
if ($version -match '^bazel 7\.') {
# New consumer servers need the same configuration for native helper tools.
$env:BAZEL_SH = $bash
}
$env:PATH = "$(Split-Path $bash);$(Split-Path $bazel);$env:PATH"
& $bash bashtest/tests/external_consumer_test.sh
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
Expand Down
22 changes: 18 additions & 4 deletions docs/infrastructure.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ Main and numeric release tags run push validation; branches run in pull-request
including forks, without duplicating the complete matrix on each push. Only superseded PR runs
are cancelled. The final gate retains release-site tests, pre-commit, and every test-matrix cell.

Every pull request and main/tag run tests Linux, macOS, and Windows with Bazel 7.7.2,
8.8.2, and the version in `.bazelversion`. The empty version entry in the matrix
lets Bazelisk read that file, avoiding a second copy of the default version.
Job-level `USE_BAZEL_VERSION` applies to root tests, report tooling, and external
consumer workspaces. Every cell runs both root runfiles modes and the consumer
checks; Windows additionally verifies the managed runtime with Unix tools removed
from PATH. Native Bazel 7 `sh_binary` fixtures need separate shell configuration:
the Windows driver points those helper launchers at the downloaded Bash. The
standalone bashtest runtime invocation still runs without a shell override or
`BAZEL_SH`. Job names, disk caches, and artifacts distinguish each version.

The pinned setup-bazel action retains native platform-specific cache paths, including Windows
where supported. In the Test workflow, PRs and release tags restore caches; only main saves them.
The separate upstream Release build disables cache mounting because it cannot restrict writes
Expand All @@ -27,7 +38,7 @@ may briefly lag post-job cache uploads; verify later runs before claiming saving

### Test diagnostics

Every test-matrix job retains a `test-results-OS` Actions artifact for seven days,
Every test-matrix job retains a `test-results-OS-bazel-VERSION` Actions artifact for seven days,
including on failure. The workflow and consumer scripts execute ordinary Bazel
commands with `--build_event_json_file`. Collection runs separately through the
Bazel-built `//tools:ci_reports` tool, which copies the `test.log` and `test.xml`
Expand All @@ -39,8 +50,10 @@ results in `bazel-testlogs` are not collected. Missing local outputs and malform
build events are reported as collection errors; remote-only output URIs are not
downloaded by this local-CI collector.

An always-run step writes per-invocation JUnit counts and limited failure/error
excerpts to the job summary. It first collects pending root results, including
An always-run step separates regular test counts, intentional failure checks, and
builds in the job summary. Counts use the final attempt of each run/shard; earlier
retries remain in the artifact. Intentional fixture diagnostics are collapsed and
excluded from ordinary failure/error counts. It first collects pending root results, including
commands that failed before an explicit collection call. Consumer cleanup does the
same before deleting its workspace and retains negative-analysis command logs.
Build/test status comes from the build events' finished event;
Expand All @@ -50,7 +63,8 @@ the program's exit status. Event files
under an `expected/` directory identify intentional failure commands. Console
output remains in the native Actions step logs. It counts actual cases rather than trusting aggregate
XML attributes, labels intentionally failing fixture commands, and fails on
unexpected command outcomes, collection errors, or malformed JUnit XML. The
unexpected command outcomes, final-attempt Bazel/JUnit failures or errors,
collection errors, or malformed JUnit XML. The
consumer scripts still verify each expected failure's precise behavior and print
captured negative-test logs if those checks fail. A missing report is shown as
such, leaving the original Bazel command status authoritative.
Expand Down
13 changes: 11 additions & 2 deletions tools/infrastructure_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,24 @@ def test_prs_restore_but_only_main_writes_caches(self):
self.assertIn("cache-save: ${{ github.ref == 'refs/heads/main' }}", workflow)
self.assertIn("cancel-in-progress: ${{ github.event_name == 'pull_request' }}", workflow)
self.assertIn('repository-cache: true', workflow)
self.assertIn('disk-cache: ${{ github.workflow }}', workflow)
self.assertIn("disk-cache: ${{ github.workflow }}-${{ matrix.bazel || 'pinned' }}", workflow)
self.assertIn('needs: [release-site-tests, pre-commit, test]', workflow)
self.assertIn('os: [ubuntu-latest, macos-latest, windows-latest]', workflow)

def test_every_platform_tests_supported_bazel_versions(self):
workflow = (Path(__file__).parents[1] / '.github/workflows/main.yml').read_text()
self.assertIn('bazel: ["7.7.2", "8.8.2", ""]', workflow)
# Job-level selection must also reach external consumer workspaces and
# Windows commands, rather than being limited to a single root test step.
self.assertIn(' env:\n USE_BAZEL_VERSION: ${{ matrix.bazel }}', workflow)
self.assertIn("name: test (${{ matrix.os }}, Bazel ${{ matrix.bazel || 'pinned' }})", workflow)
self.assertIn("name: test-results-${{ matrix.os }}-bazel-${{ matrix.bazel || 'pinned' }}", workflow)

def test_test_diagnostics_survive_failed_steps(self):
workflow = (Path(__file__).parents[1] / '.github/workflows/main.yml').read_text()
self.assertIn('name: Summarize JUnit results\n if: always()', workflow)
self.assertIn('name: Retain test diagnostics\n if: always()', workflow)
self.assertIn('name: test-results-${{ matrix.os }}', workflow)
self.assertIn("name: test-results-${{ matrix.os }}-bazel-${{ matrix.bazel || 'pinned' }}", workflow)
self.assertIn('retention-days: 7', workflow)
self.assertIn('bazel test //... --build_event_json_file=', workflow)
self.assertIn('bazel test --enable_runfiles=no //... --build_event_json_file=', workflow)
Expand Down
Loading