How Cypress Integrates with CI/CD Pipelines and Provides Exit Codes
Cypress automatically detects CI environments using environment variable inspection and returns deterministic exit codes—either a generic 1 or the exact count of failed tests when POSIX mode is enabled—to signal test outcomes to surrounding automation.
Cypress is engineered for seamless CI/CD integration, automatically recognizing popular automation platforms and reporting test results through standardized exit codes. The framework combines the ci-info package with custom detection logic in ci_provider.ts to identify the execution environment, then configures exit behavior based on pass/fail status. This architecture ensures that CI systems receive deterministic signals they can act upon without requiring manual configuration.
Detecting CI Environments Automatically
Cypress determines whether it is running inside a CI pipeline by checking for provider-specific environment variables. This detection runs once at startup, setting process.env.CI and enabling getIsCi() to return true for any subsequent operations.
The Provider Detection Logic
The detection mechanism is implemented in packages/server/lib/util/ci_provider.ts. Lines 31-75 define helper functions that identify more than 30 providers, including Azure Pipelines, AWS CodeBuild, Google Cloud Build, Jenkins, CircleCI, GitHub Actions, and Travis CI.
The CI_PROVIDERS map (lines 123-157) pairs each provider name with its identifying environment variable or detection function. For example, it checks for CIRCLECI, GITHUB_ACTIONS, or TRAVIS environment variables. The _detectProviderName() function (lines 59-75) walks this map and returns the first truthy match, providing Cypress with a canonical provider name used for telemetry and metadata extraction (such as pull-request IDs and build URLs).
// Simplified view of the detection logic in ci_provider.ts
const CI_PROVIDERS = {
'CircleCI': () => process.env.CIRCLECI,
'GitHub Actions': () => process.env.GITHUB_ACTIONS,
'Travis CI': () => process.env.TRAVIS,
// ... 30+ additional providers
}
function _detectProviderName() {
// Returns the first matching provider name
return Object.keys(CI_PROVIDERS).find(name => CI_PROVIDERS[name]()) || null
}
Exit Code Behavior and Configuration
When cypress run completes, the framework must convey the outcome to the CI system through process exit codes. The behavior follows a strict rule set implemented in packages/server/lib/cypress.ts.
Standard Exit Codes vs. POSIX Mode
By default, Cypress returns the following exit codes:
- 0: All tests passed
- 1: One or more tests failed (generic failure)
- Non-zero (e.g., 130): Run was aborted or canceled (SIGINT)
However, when POSIX exit codes are enabled via the --posix flag or the posixExitCodes CLI option (defined in cli/lib/cli.ts), Cypress returns the exact number of failed tests as the exit code instead of the generic 1. This allows pipelines to distinguish between "1 test failed" and "10 tests failed" without parsing stdout.
The implementation is verified in system-tests/test/posix_exit_codes_spec.ts, which asserts that the correct numeric code is emitted both when POSIX mode is enabled and when it is disabled.
# Run with POSIX exit codes to get exact failure count
npx cypress run --posix
echo $? # Will output the number of failed tests, or 0 if all passed
Handling Canceled Runs and Internal Errors
When a run is manually canceled, Cypress emits a specific non-zero exit code to distinguish it from test failures. In packages/server/lib/cypress.ts (lines 269-276), the runner prints a message and exits with a non-zero status:
// packages/server/lib/cypress.ts
if (runWasCanceled) {
console.log(require('chalk').magenta('\n Exiting with non-zero exit code because the run was canceled.'))
// All other exit codes are "number of tests that failed," so collapse
}
Internal errors or configuration problems typically return exit code 1, or a higher code if POSIX mode is enabled and the error maps to a specific count.
Consuming Exit Codes in CI Pipelines
Most CI platforms treat any non-zero exit code as a failure, automatically marking the step as failed. Because Cypress can report the exact number of failures in POSIX mode, pipelines can implement conditional logic based on the specific exit code value.
A typical GitHub Actions or CircleCI script pattern looks like this:
# Example for GitHub Actions with POSIX mode
npx cypress run --headless --posix
exit_code=$?
if (( exit_code > 0 )); then
echo "::error ::Cypress tests failed (exit code $exit_code)"
exit $exit_code
fi
This pattern works identically across Azure Pipelines, GitLab CI, and Travis CI because they all propagate the process exit status. When POSIX mode is disabled, the script simply checks if exit_code != 0 rather than inspecting the specific value.
Summary
-
Automatic Detection: Cypress identifies CI providers via
packages/server/lib/util/ci_provider.tsby scanning for unique environment variables using theCI_PROVIDERSmap and_detectProviderName(). -
Exit Code Semantics: Exit code
0indicates success, while non-zero codes indicate failure, cancellation, or errors. -
POSIX Mode: The
--posixflag (configured incli/lib/cli.ts) returns the exact count of failed tests rather than the generic1, enabling richer pipeline reporting. -
Cancellation Handling: Interrupted runs emit specific codes (e.g.,
130for SIGINT) as implemented inpackages/server/lib/cypress.ts. -
Pipeline Integration: CI systems automatically react to non-zero exit codes, allowing Cypress to control job success or failure without additional parsing logic.
Frequently Asked Questions
How does Cypress detect which CI provider is running?
Cypress uses the ci-info package combined with a custom detection layer in packages/server/lib/util/ci_provider.ts. The CI_PROVIDERS map (lines 123-157) pairs provider names with identifying environment variables such as CIRCLECI, GITHUB_ACTIONS, and TRAVIS. The _detectProviderName() function (lines 59-75) iterates through this map and returns the first matching provider, enabling environment-specific metadata extraction.
What exit code does Cypress return when tests fail?
By default, Cypress returns exit code 1 when any test fails. However, when POSIX mode is enabled via the --posix flag or posixExitCodes option in cli/lib/cli.ts, it returns the exact number of failed tests as the exit code. This behavior is validated in system-tests/test/posix_exit_codes_spec.ts.
How can I make my CI pipeline fail when Cypress tests fail?
Standard CI platforms automatically fail the job when the Cypress process exits with a non-zero code. You can capture the exit code in shell scripts using exit_code=$? and implement conditional logic to customize error messages or subsequent pipeline steps based on whether the value is greater than zero.
Does Cypress handle interrupted test runs differently?
Yes. When a run is canceled (for example, via SIGINT), Cypress exits with a specific non-zero code such as 130. This distinguishes manual cancellation from test failures, as implemented in packages/server/lib/cypress.ts (lines 269-276), allowing CI systems to differentiate between "failed tests" and "aborted execution."
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →