Skip to Content
GuidesCoverage Ingestion

Coverage Ingestion

CodeStax reads your test-coverage reports to compute the Coverage A–E rating, emit coverage-gap findings, and drive the new_coverage_min quality gate. Four formats are supported out of the box.

Supported Formats

FormatFile patternLanguage/framework examples
LCOVlcov.info, coverage.lcovJS/TS (Jest, Vitest), C/C++ (gcov), Python (coverage.py --lcov)
Cobertura XMLcoverage.xml, cobertura.xml, coverage-cobertura.xmlPython (coverage.py), PHP (PHPUnit), Ruby (SimpleCov)
JaCoCo XMLjacoco.xml, build/reports/jacoco/test/jacocoTestReport.xmlJava (Maven, Gradle), Kotlin, Scala
Clover XMLclover.xmlPHP (PHPUnit), JS (Karma, Jest legacy)

Two Ways to Ingest

1. Auto-detect (default)

Every scan walks the cloned repo (bounded depth 6) looking for known filenames. Any matches are parsed automatically. No action needed if your CI writes reports to standard paths AND commits them to the scanned branch.

But most teams don’t commit coverage artifacts to the repo. For those, use upload.

2. Upload from CI

Push the coverage report to CodeStax from your CI pipeline after tests run:

# Trigger a scan, get the scan_id back SCAN_ID=$(curl -sf -X POST \ -H "Authorization: Bearer $CODESTAX_JWT" \ "https://codestax.co/api/scans/trigger/$REPO_ID" | jq -r .id) # Wait for scan completion, then upload coverage curl -X POST "https://codestax.co/api/quality/coverage/upload" \ -H "Authorization: Bearer $CODESTAX_JWT" \ -F "scan_id=$SCAN_ID" \ -F "report_format=lcov" \ -F "file=@coverage/lcov.info"

Max file size: 25 MB.

CI Recipes

GitHub Actions (LCOV from Jest)

- name: Run tests with coverage run: npm test -- --coverage - name: Upload coverage to CodeStax if: steps.trigger.outputs.scan_id != '' run: | curl -X POST "https://codestax.co/api/quality/coverage/upload" \ -H "Authorization: Bearer ${{ secrets.CODESTAX_JWT }}" \ -F "scan_id=${{ steps.trigger.outputs.scan_id }}" \ -F "file=@coverage/lcov.info"

GitLab CI (Cobertura from pytest)

test: script: - pytest --cov --cov-report=xml artifacts: reports: coverage_report: coverage_format: cobertura path: coverage.xml after_script: - | curl -X POST "https://codestax.co/api/quality/coverage/upload" \ -H "Authorization: Bearer $CODESTAX_JWT" \ -F "scan_id=$SCAN_ID" \ -F "file=@coverage.xml"

Jenkins (JaCoCo from Gradle)

stage('Upload coverage to CodeStax') { steps { sh ''' curl -X POST "https://codestax.co/api/quality/coverage/upload" \ -H "Authorization: Bearer $CODESTAX_JWT" \ -F "scan_id=$SCAN_ID" \ -F "file=@build/reports/jacoco/test/jacocoTestReport.xml" ''' } }

What You Get

Once ingested, coverage data drives:

  • Coverage rating (A–E) — shipped with every scan in GET /quality/ratings/:scan_id
  • Coverage % dashboard widget on scan detail
  • Per-file coverage table — sortable, color-coded (below 60% red, below 80% yellow)
  • Coverage-gap findings — files below threshold emitted as type: "coverage_gap" with severity
  • new_coverage_min quality gate — configurable threshold in org policy

Thresholds

Default thresholds for coverage-gap findings:

  • Files < 60% coverage → high severity
  • Files ≥ 60% but < configured quality_coverage_low_threshold (default 60) → not emitted unless customized
  • Files < 30% → critical

Configurable per-org via scanner config:

  • quality_coverage_low_threshold (default 60)
  • quality_coverage_critical_threshold (default 30)

Tiny files (< 20 lines) are skipped to reduce noise.

Auto-Detect Path Matching

The auto-detector scans these paths in your cloned repo (up to 6 dirs deep from root):

  • lcov.info / coverage.lcov / lcov-report.info
  • coverage.xml / cobertura.xml / cobertura-coverage.xml / coverage-cobertura.xml
  • jacoco.xml / jacocoTestReport.xml
  • clover.xml

Skipped dirs: node_modules, .git, __pycache__, .venv, venv, env, dist, .next, .nuxt, .cache.

Querying Coverage

# Get aggregate + per-file curl -H "Authorization: Bearer $JWT" \ https://codestax.co/api/quality/coverage/12345 | jq # Output shape: # { # "scan_id": 12345, # "file_count": 847, # "lines_total": 42100, # "lines_covered": 38700, # "coverage_pct": 91.92, # "files": [ { file_path, lines_total, lines_covered, ... } ] # }

Common Issues

SymptomCauseFix
Coverage widget empty after scanNo reports found + no uploadAdd upload step to CI, or commit reports
”Format detection failed”Unknown extensionPass explicit report_format=<lcov|cobertura|jacoco|clover>
File > 25 MB rejectedReport too bigSplit by package, or upload multiple times per subdir
File paths don’t match sourceAbsolute paths in reportUse --cov-report=xml:<path> flags to write relative paths