Why CI failures need more than a log dump
A CI failure tells you something broke. A log dump tells you what the code did. Neither tells you what the user would have seen — the visual state, the console errors, the network requests that failed. For end-to-end test failures especially, this gap is where hours of debugging time disappear.
The goal is to make failures self-describing: when a CI job fails, the context needed to reproduce and fix the bug should be attached to a GitHub Issue automatically, waiting for the developer when they open their notifications.
Pattern 1: Report on test step failure
For Playwright or Cypress end-to-end tests, capture a report on failure and POST it to Site Reviewer's API. The webhook payload format is the same as the browser extension — description, URL, console log, network requests, screenshot.
# .github/workflows/e2e.yml
name: E2E Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run E2E tests
id: e2e
run: npx playwright test
continue-on-error: true
- name: Report failure to Site Reviewer
if: steps.e2e.outcome == 'failure'
run: |
curl -s -X POST https://api.sitereviewer.app/api/v1/reports \
-H "X-Api-Key: ${{ secrets.SITE_REVIEWER_API_KEY }}" \
-H "Content-Type: application/json" \
-d '{
"id": "${{ github.run_id }}-${{ github.run_attempt }}",
"type": "bug",
"severity": "blocking",
"description": "E2E test suite failed on ${{ github.ref_name }}",
"context": {
"url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}",
"title": "GitHub Actions — ${{ github.workflow }}",
"timestamp": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"
},
"consoleLog": [],
"networkRequests": []
}'
- name: Fail job if tests failed
if: steps.e2e.outcome == 'failure'
run: exit 1
Pattern 2: Attach Playwright test artifacts
Playwright generates screenshots and traces on failure. Upload them to Site Reviewer's screenshot endpoint for visual context in the issue.
- name: Upload failure screenshot
if: steps.e2e.outcome == 'failure'
run: |
# Find the first failure screenshot
SCREENSHOT=$(find test-results -name "*.png" | head -1)
if [ -n "$SCREENSHOT" ]; then
SCREENSHOT_B64=$(base64 -w 0 "$SCREENSHOT")
# Include in the report payload as the screenshot field
# (Site Reviewer accepts base64 data URLs)
echo "data:image/png;base64,$SCREENSHOT_B64" > /tmp/screenshot.txt
fi
Pattern 3: Nightly regression report
For nightly runs against staging, submit a summary report that includes pass/fail counts as the description and links to the GitHub Actions run for drill-down.
# .github/workflows/nightly.yml
on:
schedule:
- cron: '0 2 * * *' # 2am UTC nightly
jobs:
nightly-regression:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run test suite
id: tests
run: npm test -- --reporter=json > test-results.json
continue-on-error: true
- name: Parse results
id: parse
run: |
PASSED=$(jq '.numPassedTests' test-results.json)
FAILED=$(jq '.numFailedTests' test-results.json)
echo "passed=$PASSED" >> $GITHUB_OUTPUT
echo "failed=$FAILED" >> $GITHUB_OUTPUT
- name: Create Site Reviewer report if failures
if: steps.parse.outputs.failed != '0'
run: |
curl -s -X POST https://api.sitereviewer.app/api/v1/reports \
-H "X-Api-Key: ${{ secrets.SITE_REVIEWER_API_KEY }}" \
-H "Content-Type: application/json" \
-d "{
\"id\": \"nightly-${{ github.run_id }}\",
\"type\": \"bug\",
\"severity\": \"blocking\",
\"description\": \"Nightly regression: ${{ steps.parse.outputs.failed }} tests failed, ${{ steps.parse.outputs.passed }} passed\",
\"context\": {
\"url\": \"${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}\",
\"title\": \"Nightly Regression — $(date +%Y-%m-%d)\",
\"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"
},
\"consoleLog\": [],
\"networkRequests\": []
}"
Routing CI failures to the right place
Once reports land in Site Reviewer, you can route them automatically:
- GitHub Issues — connect the GitHub App integration to create an issue in the relevant repo automatically
- Slack — configure the Slack integration to ping the on-call channel for blocking severity reports
- Linear / Jira — create tickets in your issue tracker automatically with full context attached
Because CI-generated reports use the same schema as extension-submitted reports, they flow through the same integration pipeline. No special handling needed.
The environment variable you need
The only required setup is adding your Site Reviewer API key as a GitHub Actions secret. In your repository settings:
Settings → Secrets and variables → Actions → New repository secret → name it SITE_REVIEWER_API_KEY.
Get your API key from app.sitereviewer.app/settings.
"CI failures should create a ticket, not a mystery. The person fixing the bug shouldn't have to re-run the test suite to figure out what happened."
Start integrating today
Site Reviewer is free for up to 100 reports/month. Create your workspace → and add the GitHub Actions steps above in five minutes.