Preflight

Experimental feature

The Preflight feature is currently in experimental stage. Its behavior is subject to change without notice. To provide feedback, please contact Buildkite's Support team at support@buildkite.com.

Preflight is a subcommand of the Buildkite CLI (bk preflight) that runs your uncommitted local changes against Buildkite Pipelines and monitors failures as they happen. It is designed for use with a coding agent, triggering a build against the changes in your working tree and surfacing failures for the agent to iterate against.

The Preflight (bk preflight) command:

  • Snapshots your uncommitted changes (staged, unstaged, and untracked files) as a temporary commit on a new branch, without touching your working tree. Files matched by .gitignore are excluded.
  • Pushes that commit to a branch prefixed with bk/preflight/ on the repository's origin remote, then triggers a build on your chosen Buildkite pipeline.
  • Monitors failures in your terminal in real time and exits as soon as the build starts failing.
  • Cleans up the temporary branch automatically when the build finishes.

Before you begin

You'll need:

  • The Buildkite CLI version 3.40.0 or later.
  • A configured API access token with the read_builds, write_builds, and read_pipelines scopes. The read_suites scope is also required to use Preflight with Test Engine.
  • Git commit and push access to the repository.

Install or upgrade the Buildkite CLI

To check your current Buildkite CLI version, run:

bk version

To upgrade using Homebrew:

brew upgrade buildkite/buildkite/bk@3

To upgrade using mise:

mise use -g github:buildkite/cli@latest

Install the Preflight skill

To install the Preflight skill into your coding agent:

bk skill add buildkite-preflight

Using the skill requires Buildkite CLI version 3.40.0 or later.

Run a Preflight build

To run a build with Preflight enabled:

bk preflight --pipeline my-org/my-pipeline --watch

The --pipeline flag accepts either {org-slug}/{pipeline-slug} or just {pipeline-slug} if your Buildkite organization is already set in your bk config.

You can omit --pipeline when configuration or repository discovery resolves the pipeline. Pass it explicitly in automation to avoid ambiguous selection. Watching is enabled by default; --no-watch starts the build and exits without waiting or automatically cleaning up its branch.

In --watch mode, Preflight exits with code 0 if all jobs pass, 10 when the build first enters the failing state (the default), or 9 if the build completes with failures. See exit codes for the full list.

Preflight works well with promise job failure. If a running job declares a promised hard failure, Buildkite Pipelines can move the build to failing before the job exits, and Preflight can start reporting the failure earlier while the job continues uploading logs and test results. Reading promised failures from running jobs requires Buildkite CLI version 3.50.0 or later.

The following examples show common variations:

# Start the build and exit immediately (don't wait)
bk preflight --pipeline my-org/my-pipeline --no-watch

# Skip confirmation prompts
bk preflight --pipeline my-org/my-pipeline --watch --yes

# Use plain text output in non-interactive environments
bk preflight --pipeline my-org/my-pipeline --watch --text

# Use JSONL output when another tool needs structured events
bk preflight --pipeline my-org/my-pipeline --watch --json

# Wait up to 30s for Test Engine results after build completion
bk preflight --pipeline my-org/my-pipeline --watch --await-test-results

# Set a custom wait for Test Engine results
bk preflight --pipeline my-org/my-pipeline --watch --await-test-results=10s

# Don't cancel the build or remove the branch on exit
bk preflight --pipeline my-org/my-pipeline --watch --no-cleanup

# Wait for the build to reach a terminal state instead of exiting on the failing state
bk preflight --pipeline my-org/my-pipeline --watch --exit-on build-terminal

Unless you pass --no-cleanup, an early exit while watching can cancel the build before deleting the remote branch. Use --no-cleanup when you want the build to continue after Preflight exits.

Clean up retained branches

For branches left by --no-watch, --no-cleanup, or an interrupted run, preview cleanup before deleting them:

bk preflight cleanup --pipeline my-org/my-pipeline --preflight-uuid UUID --dry-run
bk preflight cleanup --pipeline my-org/my-pipeline --preflight-uuid UUID

Cleanup checks build status only in the selected pipeline. It leaves branches with in-progress builds found in that pipeline untouched, but branches with no build found are eligible for deletion. Without --preflight-uuid, all bk/preflight/* branches on origin are candidates, including branches used by other pipelines.

In repositories shared by multiple pipelines, use --preflight-uuid UUID with the matching --pipeline and inspect --dry-run output before deleting. The UUID limits branch selection; it does not independently verify which pipeline owns the branch. See the Preflight command reference for available flags.

Build summary

On exit, Preflight prints a summary of the jobs that failed. When integrated with Buildkite Test Engine, the summary also includes test results. This integration requires the read_suites scope on your API access token.

A test with at least one passed execution is treated as passed, and a test with only failed executions is treated as failed. Tests that pass on retry are not counted as failures. Tests with only pending, skipped, or unknown executions are excluded from the summary.

Preflight reports up to 10 test failures in the terminal output, and up to 100 test failures in JSON events.

Customizing pipelines for Preflight

Preflight sets the following environment variables on the build, so you can customize pipeline behavior for preflight runs:

  • PREFLIGHT: Set to true.
  • PREFLIGHT_SOURCE_COMMIT: The HEAD commit when Preflight was run.
  • PREFLIGHT_SOURCE_BRANCH: The current branch when Preflight was run.

Use these with conditionals and dynamic pipelines to run a subset of a pipeline or otherwise modify its behavior under Preflight.

To skip linting on builds triggered by Preflight:

pipeline.yml
steps:
  - command: ./scripts/lint.sh
    label: lints
    if: build.env("PREFLIGHT") != "true"

To run a test suite with --fast-fail when Preflight is in use:

pipeline.yml
steps:
  - label: "πŸ§ͺ Tests"
    command: |
      if [ "$PREFLIGHT" = "true" ]; then
        ./scripts/test.sh --fast-fail
      else
        ./scripts/test.sh
      fi

Exit codes

Exit code Meaning
0 Watched build passed, or the build was created successfully with --no-watch
1 Generic error
9 Build completed with failures
10 Build incomplete β€” failures already detected
11 Build incomplete β€” still running or blocked
12 Unknown build state
130 Aborted by user (Ctrl+C)

Flags

Flag Default Description
--pipeline, -p Inferred Pipeline slug ({slug} or {org}/{slug}); specify explicitly when inference is ambiguous
--watch / --no-watch true Watch the build until completion or the selected exit condition
--interval 2 Polling interval in seconds
--exit-on build-failing Condition that triggers exit. build-failing exits when the build enters the failing state; build-terminal exits when the build reaches a terminal state.
--no-cleanup false Keep the remote preflight branch and leave the build running on early exit
--await-test-results[=DURATION] Disabled Wait for Test Engine summaries after build completion; without a duration, wait up to 30 seconds
--text false Use plain text output
--json false Emit one JSON object per event (JSONL)
--yes, -y false Skip confirmation prompts
--no-input false Disable all interactive prompts
--quiet, -q false Suppress progress output
--debug false Log HTTP API traffic to stderr; bodies can contain sensitive data