Report Run Checks as GitHub pull request statuses
Run Checks evaluate recorded runs in Cypress Cloud against the thresholds you define: failed tests, flaky tests, accessibility scores, UI Coverage, and more. To turn those results into a merge gate, fetch them in CI with getCheckResults() and post one commit status per applicable check.
Install @cypress/extract-cloud-results as described in the Results API guide, which also lists the supported CI providers. Run the status script in the same CI job that recorded the run, after cypress run --record. For the full getCheckResults() response shape and options, see Using check results in CI.
This is separate from the Cypress GitHub integration, which reports per-run-group or per-spec test pass/fail. Run Check statuses report specific thresholds that you configure, including Cypress Accessibility and UI Coverage assertions if relevant.
This GitHub Actions workflow records a run, then posts one commit status per applicable Run Check to the pull request. A failed check shows as a failing status on the pull request, and you can make the statuses required in your branch protection rules.
name: Cypress
on: pull_request
permissions:
contents: read
statuses: write
jobs:
cypress:
runs-on: ubuntu-latest
env:
CYPRESS_PROJECT_ID: abc123
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
steps:
- uses: actions/checkout@v4
- run: npm ci
# Failing tests fail this step, but the checks still need to be reported.
- run: npx cypress run --record
continue-on-error: true
- run: npm install --force https://cdn.cypress.io/extract-cloud-results/v1/extract-cloud-results.tgz
- run: node scripts/post-check-statuses.js
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
const fs = require('fs')
const { getCheckResults } = require('@cypress/extract-cloud-results')
const STATES = { passed: 'success', failed: 'failure' }
// On pull_request events, GITHUB_SHA is a merge commit that GitHub creates.
// Statuses only show on the pull request when they're posted to its head commit.
const event = JSON.parse(fs.readFileSync(process.env.GITHUB_EVENT_PATH, 'utf8'))
const sha = event.pull_request?.head.sha ?? process.env.GITHUB_SHA
const main = async () => {
const results = await getCheckResults()
const checks = (results.checks ?? []).filter(
(check) => check.status !== 'not-applicable'
)
for (const check of checks) {
const missed = check.assertionResults
.filter((assertion) => assertion.status !== 'passed')
.map(({ type, value, status }) => `${type} ${value} (${status})`)
const response = await fetch(
`https://api.github.com/repos/${process.env.GITHUB_REPOSITORY}/statuses/${sha}`,
{
method: 'POST',
headers: {
authorization: `Bearer ${process.env.GITHUB_TOKEN}`,
accept: 'application/vnd.github+json',
},
body: JSON.stringify({
context: `Cypress check: ${check.name}`,
state: STATES[check.status] ?? 'error',
description: (missed.length
? `Missed: ${missed.join('; ')}`
: 'All assertions passed'
).slice(0, 140),
target_url: `${results.runUrl}/properties`,
}),
}
)
if (!response.ok) {
throw new Error(`GitHub responded ${response.status}`)
}
}
}
main().catch((error) => {
console.error(error.message)
process.exit(1)
})
The script skips not-applicable checks, so a check scoped to release/.* branches only appears on pull requests from those branches. GitHub limits a status description to 140 characters, so the script truncates it and links each status to the run for details.

See also​
- Run Checks - define assertions and scope them to branches and tags
- GitHub integration - built-in commit statuses for test results
- Flaky Test Management - flake detection that
run.flakyTests.maxreads