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.39.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 Buildkite 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

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.

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.

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

# 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

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 All jobs passed
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 β€” Pipeline slug ({slug} or {org}/{slug}) β€” required
--watch / --no-watch β€” Watch the build until completion
--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 after the build
--await-test-results β€” Wait for Test Engine summaries after build completion
--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 Enable debug output for API calls