Skip to content

Bazel

Configure build and test reporting with tuist bazel setup, then use tuist bazel test to fetch and apply Tuist's quarantine policy before each invocation.

Applying quarantine#

On a test's page, change its state to Muted, then run:

bash
tuist bazel test -- //app:tests

Arguments after -- are forwarded to bazel test. Use --path to select the project and working directory, and --bazel to select a different executable such as bazelisk. Ordinary bazel test still reports test results, but does not fetch or enforce Tuist quarantine policies.

Neither a named Bazel configuration nor retries are required for quarantine. The Flaky Tests, Quarantined Tests, and individual test pages support the same manual state changes as other build systems. Use Settings → Automations to configure flakiness thresholds, quarantine actions, and recovery actions.

Mute#

Use Mute as the primary quarantine workflow. Muted cases continue to run, their failures remain visible, and healthy sibling cases still affect the result. New executions provide evidence for recovery without excluding any cases from the target.

Tuist reads a temporary local build-event file and the referenced test.xml reports. It changes Bazel's test-failure exit to success only when complete reports attribute every reported failure in each failed target to a muted case.

Build errors, timeouts, interrupted runs, missing or malformed reports, incomplete event streams, and failures outside the muted set retain Bazel's failure status. The local event file is bounded to 64 mebibytes and individual reports to 5 mebibytes. Tuist needs its own local build-event file to verify muted failures and recover from stale skipped targets, so an explicit --build_event_json_file cannot be combined with active muted or skipped cases. Remote build-event reporting remains configured through tuist bazel setup.

The original Bazel invocation and reports retain their actual outcomes. Tuist's server records whether a case was quarantined when the invocation started, even if its state changes before the report is processed.

Use Skip only when you intend to exclude the entire Bazel target, including healthy cases inside that target. Marking a single case Skipped has this target-wide effect. Bazel's individual-case filter syntax depends on the test framework and is not universally supported. Tuist therefore uses native negative target patterns with test-suite expansion enabled. Explicit targets, wildcard selections, and tests inside test_suite rules keep Bazel's normal selection behavior.

The command fetches every quarantine page and deduplicates targets. If Bazel's complete event log identifies a missing skipped target before target configuration or execution, Tuist names it in a warning and retries without that stale exclusion. The original requested patterns and all other exclusions are preserved. Recovery is limited to five retries; other loading failures and incomplete evidence retain Bazel's error. Return stale cases to Enabled to remove their policies permanently. Pass target patterns directly; --target_pattern_file cannot be combined with quarantine exclusions. If no test targets remain, Bazel retains its normal “no tests found” exit status, which also catches an accidentally empty test selection.

The command prints every excluded target label so build logs show which targets, including their healthy cases, the quarantine policy excludes.

Running quarantined tests again#

To investigate or validate a fix, bypass both quarantine policies:

bash
tuist bazel test --no-quarantine -- --nocache_test_results //app:tests

Return the case to Enabled to include its target in subsequent normal runs. Skipped cases produce no new executions until explicitly included, so an automation based on successful new runs needs a validation run before it can recover them.

Quarantine retrieval errors stop the command before running tests. The command also rejects policies above 20,000 cases or target arguments above its bounded size; it never applies a partial policy. Use --no-quarantine when you intentionally want to run without the policy.

Optional configuration and retries#

If your repository defines a named ci configuration in .bazelrc, you can select it with --config=ci:

bash
tuist bazel test -- --config=ci //app:tests

This is a repository-defined configuration, not a Tuist prerequisite. Omit the option when your repository does not define it.

Retries are also optional and independent of quarantine. --flaky_test_attempts=3 enables up to three total Bazel attempts per test, not three additional retries. Teams can keep their existing retry configuration in .bazelrc or pass it explicitly:

bash
tuist bazel test -- --flaky_test_attempts=3 //app:tests

For repositories that define ci, the options can be combined:

bash
tuist bazel test -- --config=ci --flaky_test_attempts=3 //app:tests

Detecting flakiness#

Tuist uses the individual cases from Bazel's test.xml reports to detect inconsistent results across retries and across runs of the same commit and target selection in continuous integration. Local runs remain visible but do not automatically flag test cases as flaky. Provide commit and branch metadata when your build environment does not already publish it:

bash
tuist bazel test -- \
--build_metadata=CI=true \
--build_metadata=GIT_COMMIT="$GIT_COMMIT" \
--build_metadata=GIT_BRANCH="$GIT_BRANCH" //app:tests

Retries must actually run for Tuist to observe their results. A Bazel target's flaky summary alone does not identify which individual case was flaky. Reports that are unavailable or cannot be parsed cannot provide case-level evidence.

Cases are identified by target label, class name (falling back to the suite name), and test name. This keeps identically named methods in different classes separate. Older reports that used a different enclosing suite name retain their history under the old identity; their quarantine state must be applied to the new identity. When a failure matches an old suite-based mute instead of its current class identity, the command warns which policy needs to be reapplied and keeps the failure blocking. Policies are not transferred automatically because multiple classes may have shared the old identity.

See Bazel's test filters, command options, and build-event descriptions for the underlying behavior.