Skip to main content
Version: v4 (current)

Preflight Tests

Preflight tests are fast, fail-fast validation gates that run before expensive build and test stages, catching configuration, environment, and integrity issues before they waste CI resources.

Overview

A build that fails because a config file is malformed, a required tool is missing, or a script has a syntax error has wasted the full cost of runner startup, dependency resolution, and potentially Unity import. Preflight tests eliminate that class of failure by running cheap validations up front — before a Unity license is acquired, before a build is dispatched, before anything expensive begins.

Preflight tests differ from test suites in two key ways:

  • Fail-fast. The first failing check aborts the entire preflight stage. Unlike test suites, which collect all failures before reporting, preflight aborts immediately. One bad config file stops the run.
  • No Unity required. Preflight checks are pure validation: YAML parsing, filesystem checks, network probes, script linting. No Unity license, no GPU, no build output.

The result is a gate that completes in under five minutes and reliably prevents the most common class of avoidable CI failures.

Pipeline Position

Preflight runs after checkout and before any Unity work:

If preflight fails, nothing else runs. Pre-build hooks, builds, and tests are all skipped. This is intentional: there is no value in dispatching a build job when the configuration driving it is invalid.

Quick Start

Enable preflight in your orchestrator configuration with a single field:

# .game-ci/orchestrator.yml
preflight:
suite: .game-ci/preflight-suite.yml

Define the suite:

# .game-ci/preflight-suite.yml
checks:
- runner-health
- config-validation
- build-profiles
- script-integrity

That is enough to catch the most common avoidable failures. Add checks incrementally as you identify recurring failure modes.

Built-in Checks

Orchestrator ships with 13 built-in checks covering four categories.

Config

Validate configuration files before the pipeline commits to using them.

IDNameDescription
pipeline-contractPipeline Contract ValidationValidates CI workflow YAML structure and required fields
build-profilesBuild Profile ValidationValidates build profile configs are well-formed
submodule-profilesSubmodule Profile ValidationValidates submodule profile configs and dependencies
config-validationConfig ValidationValidates .game-ci configuration files
framework-suite-configFramework & Suite ConfigValidates framework and test suite YAML configs

Environment

Confirm the runner can support the build before the build starts.

IDNameDescription
runner-healthRunner Health CheckChecks disk space, required tools, and network connectivity
lfs-healthLFS Health CheckValidates Git LFS config, connectivity, and hydration

Integrity

Verify that the repository and its supporting scripts are internally consistent.

IDNameDescription
preunityjob-dry-runPreUnityJob Dry RunRuns the PreUnityJob script in dry-run mode
script-integrityScript & Manifest IntegrityChecks scripts for syntax errors and manifests for consistency
health-test-discoveryHealth Test DiscoveryDiscovers health test classes and validates wiring

Compilation

Catch C# problems without invoking Unity.

IDNameDescription
csharp-heuristicsC# Heuristics (Changed)Lightweight C# analysis on changed files only
csharp-heuristics-fullC# Heuristics (Full Repo)Full-repo C# heuristic analysis, scoped to relevant changes
cross-profile-compileCross-Profile CompileVerifies the project compiles across all active profiles

Check Categories

Each category has a different cost profile:

CategoryTypical durationUnity requiredWhen to run
Config< 5sNoAlways
Environment5–30sNoAlways on persistent runners
Integrity10–60sNoAlways
Compilation30–60sNoOn changed C# or profile changes

Order your suite to run cheaper checks first. A config check that fails in two seconds prevents the compilation check from running unnecessarily.

Configuration

Using Built-in Checks

Reference built-in checks by ID string:

# .game-ci/preflight-suite.yml
checks:
- runner-health
- config-validation
- build-profiles
- submodule-profiles
- script-integrity
- csharp-heuristics

Custom Checks

Define checks inline when you need project-specific validation:

checks:
- runner-health
- id: verify-artifact-mount
name: Artifact Mount Check
category: environment
run: |
if (-not (Test-Path "D:\BuildArtifacts")) {
Write-Error "Artifact mount D:\BuildArtifacts not present"
exit 1
}
timeout: 15
- config-validation

Custom check fields:

FieldDescriptionDefault
idUnique identifier for this checkRequired
nameHuman-readable label shown in CI outputid
categoryconfig, environment, integrity, or compilationintegrity
runShell script to execute (PowerShell on Windows, bash elsewhere)Required
timeoutMaximum seconds before the check is aborted and fails60

A check passes when its run script exits 0. Any non-zero exit code is a failure, and preflight aborts.

Importing Community Checks

Import check packages published by the community:

imports:
- source: game-ci/preflight-checks-unity@1.2.0
checks:
- unity-license-server
- package-cache-integrity

checks:
- runner-health
- unity-license-server
- package-cache-integrity
- config-validation

Imported check packages are resolved at pipeline startup. The source field accepts any package reference in the format {owner}/{repo}@{tag}.

Scoping Checks

Run a check only when relevant files have changed using scope.paths. This prevents expensive checks from running on every push:

checks:
- runner-health
- id: csharp-heuristics
scope:
paths:
- Assets/**/*.cs
- Assets/**/*.asmdef
- id: cross-profile-compile
scope:
paths:
- config/submodule-profiles/**
- Assets/**/*.cs
runCondition: changed

runCondition accepts:

ValueBehaviour
alwaysRun on every execution (default)
changedRun only when scoped paths have changed since the last successful run
neverDisable the check without removing it from the suite

Use never to temporarily disable a check during active investigation without losing its configuration.

CLI Usage

Run preflight locally before pushing:

game-ci preflight

Target a specific suite file:

game-ci preflight --suite .game-ci/preflight-suite.yml

List all available built-in checks:

game-ci preflight --list

Run a single check by ID:

game-ci preflight --check runner-health

The CLI uses the same execution engine as CI. A check that passes locally passes in CI under the same conditions.

Continuous Improvement Cycle

A preflight suite that never changes is not doing its job. The value of preflight comes from encoding the failure modes you have actually seen:

  1. A build fails for a preventable reason. A missing tool, a malformed config, a broken script.
  2. Write a check for it. Either use a built-in check that covers the case, or write a custom check.
  3. Add it to the suite. The failure mode is now caught before it reaches the build stage.
  4. Review each sprint. Remove checks that no longer apply. A check for a dependency you removed six months ago creates noise, not signal.

The goal is a suite where every check has caught a real failure at least once. Checks that have never fired and address conditions that cannot arise should be removed.

Design Principles

  • Fast. Each check completes in under 60 seconds. The full suite completes in under 5 minutes. If a check regularly exceeds these bounds, it belongs in the test suite, not in preflight.
  • Fail-fast. Preflight aborts on the first failure. This is deliberate — a failing environment or broken config invalidates the results of subsequent checks.
  • Cheap. No Unity license, no GPU, no build output. Preflight runs on any machine that can run a shell script.
  • Extensible. Built-in checks, custom inline checks, and community check packages all compose in the same suite file.
  • Scoped. Change-detection gating ensures checks run only when their relevant files have changed, keeping the suite fast even as it grows.

When to Use Preflight vs. Other Gates

Validation typeWhere it belongs
Config file structure, required fieldsPreflight (config-validation, pipeline-contract)
Environment readiness (disk, tools, network)Preflight (runner-health, lfs-health)
Script syntax, manifest consistencyPreflight (script-integrity)
Lightweight C# heuristics on changed filesPreflight (csharp-heuristics)
Unity EditMode / PlayMode testsTest suite
Full compilation across all platformsBuild stage or preflight (cross-profile-compile)
End-to-end product validationTest suite (built-client runs)
Profiling, performance benchmarksPost-build validation stage

If a check requires Unity to be running, it is a test suite item. If it requires a built artifact, it is a post-build validation item. Everything else that can be validated cheaply and quickly belongs in preflight.

Inputs Reference

InputDescriptionDefault
preflightSuitePath to preflight suite YAML file''
preflightEnabledEnable the preflight stage'true' when suite is set
preflightFailFastAbort on first check failure'true'
preflightTimeoutMaximum total preflight duration in seconds300