Skip to content
CloudOps
Newsletter
All prompts
AI for GitLab CI/CD Difficulty: Intermediate ClaudeChatGPT

GitLab CI/CD Code Coverage Integration Prompt

Configure code coverage parsing, MR coverage diff, trend graphs, badge integration, multi-language combined coverage.

Target user
Engineers integrating test coverage into GitLab CI
Difficulty
Intermediate
Tools
Claude, ChatGPT

The prompt

You are a senior engineer who has set up code coverage reporting across many language stacks in GitLab CI/CD. You know how to parse output, configure the per-job coverage regex, generate Cobertura/JaCoCo XML for the MR diff feature, and avoid the multi-job coverage merge trap.

I will provide:
- The test framework and coverage tool (pytest+coverage.py, jest, go test -cover, JaCoCo, etc.)
- The current pipeline / coverage config
- The goal: enable coverage / fix display / require minimum / combine multi-job

Your job:

1. **Two layers of coverage in GitLab**:
   - **Coverage % parsed from job log** (the "old way" — still works) — set `coverage:` regex on the job
   - **Coverage diff in MR** (the "new way") — requires `artifacts:reports:coverage_report` in Cobertura format
2. **For coverage regex**:
   - The regex matches the line in the test output that contains the coverage percentage
   - Examples:
     - **pytest-cov**: `/^TOTAL.+?(\d+\%)$/`
     - **simplecov (Ruby)**: `/\(\d+\.\d+\%\) covered/`
     - **jest**: `/All files[^|]*\|[^|]*\s+([\d\.]+)/`
     - **go test -cover**: `/total:\s+\(statements\)\s+(\d+\.\d+%)/`
     - **JaCoCo (Maven)**: `/Total.*?([0-9]{1,3})%/`
   - Validate at Project → CI/CD → coverage regex tester
3. **For MR coverage diff (Cobertura XML)**:
   - Most coverage tools can output Cobertura format
   - **pytest-cov**: `pytest --cov-report=xml:coverage.xml`
   - **jest**: `jest --coverage --coverageReporters=cobertura`
   - **go**: `go test -coverprofile=cover.out && gocov-xml < cover.out > coverage.xml`
   - **JaCoCo**: `mvn jacoco:report` then convert with `cover2cover.py`
   - In CI: `artifacts: reports: coverage_report: { coverage_format: cobertura, path: coverage.xml }`
4. **For multi-job coverage**:
   - If you split tests into parallel jobs, each job reports its own coverage
   - GitLab averages them (since 14.2) when each job has `coverage:` set
   - For combined report: download all artifacts in a final job, merge (use `coverage combine` for pytest, etc.), output single Cobertura
5. **For coverage gating**:
   - GitLab doesn't natively enforce minimum coverage
   - Workarounds:
     - Job exits non-zero if coverage below threshold (`coverage report --fail-under=80`)
     - Custom diff calculation between MR and target branch
     - Approval rules on `coverage_decrease` (some projects use bot to comment)
6. **For badge**:
   - Project → Settings → General → Badges
   - URL: `https://gitlab.example.com/%{project_path}/badges/%{default_branch}/coverage.svg`
   - Image to display in README
7. **For trend graphs**:
   - Project → Analytics → Repository → Code Coverage
   - Shows coverage over time per default branch
8. **For paths exclusion**:
   - Each tool has its own exclude config:
     - pytest: `--cov-config=.coveragerc` with `omit` list
     - jest: `coveragePathIgnorePatterns` in config
     - go: build tags

Mark DESTRUCTIVE: dropping coverage threshold under pressure (lowering bar), excluding code from coverage to "improve" the number (misleading metric).

---

Test framework + coverage tool: [DESCRIBE]
Current pipeline:
```yaml
[PASTE relevant job]
```
Symptom (if debugging): [coverage not displayed / wrong %, MR diff missing, multi-job confusion]
Goal: [enable / fix / require threshold / combine]

Why this prompt works

Coverage configuration is tool-specific and the regex syntax is a frequent stumbling block. The MR diff feature requires Cobertura format which not every tool emits natively. This prompt walks the recipes per language.

How to use it

  1. Set coverage regex first for the number-in-UI.
  2. Add Cobertura artifact for MR diff.
  3. For multi-job, plan combining upfront.
  4. For threshold gating, fail the job if below.

Useful commands

# Test regex (matches what's expected from test output)
# Project → Settings → CI/CD → expand "General pipelines" → "Test coverage parsing"

# Convert formats
# Go coverage to Cobertura
go install github.com/AlekSi/gocov-xml@latest
go install github.com/axw/gocov/gocov@latest
go test -coverprofile=cover.out ./...
gocov convert cover.out | gocov-xml > coverage.xml

# JaCoCo to Cobertura
pip install jacoco-converter
jacoco-converter target/site/jacoco/jacoco.xml > coverage.xml
# Or use: https://gitlab.com/haynes/jacoco2cobertura

Patterns

Python pytest

test-python:
  image: python:3.12
  script:
    - pip install -e . pytest pytest-cov
    - pytest --cov=mypackage --cov-report=term --cov-report=xml:coverage.xml --cov-fail-under=80
  coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
  artifacts:
    when: always
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
      junit: report.xml
    expire_in: 1 week

Node.js Jest

test-node:
  image: node:20
  script:
    - npm ci
    - npm test -- --coverage --coverageReporters=text-summary --coverageReporters=cobertura
  coverage: '/All files\s*\|\s*([\d\.]+)/'
  artifacts:
    when: always
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage/cobertura-coverage.xml
    expire_in: 1 week

Go

test-go:
  image: golang:1.23
  script:
    - go install github.com/axw/gocov/gocov@latest
    - go install github.com/AlekSi/gocov-xml@latest
    - go test -coverprofile=cover.out ./...
    - go tool cover -func=cover.out
    - gocov convert cover.out | gocov-xml > coverage.xml
  coverage: '/total:\s+\(statements\)\s+(\d+\.\d+%)/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml
    expire_in: 1 week

Multi-job parallel + combined

test:
  parallel: 4
  script:
    - pytest --cov=mypackage --cov-report=xml:coverage-${CI_NODE_INDEX}.xml \
        --tests-per-worker auto
  artifacts:
    paths: ["coverage-*.xml"]
    expire_in: 1 hour

combine-coverage:
  needs: [test]
  script:
    - pip install coverage
    - coverage combine
    - coverage xml -o coverage.xml
    - coverage report
  coverage: '/(?i)total.*? (\d+\%)/'
  artifacts:
    reports:
      coverage_report:
        coverage_format: cobertura
        path: coverage.xml

Coverage gate

coverage-gate:
  needs: [test]
  script:
    - pip install coverage
    - coverage combine || true
    - coverage report --fail-under=80
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"

Common findings this catches

  • Coverage shows N/A in UI → regex not matching test output; test in CI/CD settings.
  • MR doesn’t show coverage diffcoverage_report artifact missing or wrong format.
  • Coverage % wrong → regex matches a sub-total, not the total.
  • Multi-job parallel coverage = avg of partials → expected if not combined; combine for accurate %.
  • Excluded paths skew coverage.coveragerc omit list too broad.
  • Coverage drops on every MR → flaky tests dropping out; quarantine flakies before fixing coverage.
  • JaCoCo XML format not understood by GitLab — must convert to Cobertura.

When to escalate

  • Coverage trending downward despite team focus — process issue, not pipeline.
  • Cobertura conversion edge cases (e.g., parallel package coverage in Java) — engage tooling team.
  • Coverage gating blocking critical fix — temporary lower OR explicit waiver path.

Related prompts

Newsletter

Free: the DevOps AI Incident-Triage Cheat Sheet

Subscribe and we’ll send you the one-page cheat sheet — plus weekly AI prompts, automation ideas, and tool reviews for infrastructure engineers. One email a week. No spam, unsubscribe anytime.

  • AI Incident-Triage Cheat Sheet (PDF)
  • Access to 1,603 DevOps AI prompts
  • One practical workflow email per week