DeepSource Autofix and Test Coverage Enforcement: A Practical Guide

DeepSource Autofix and Test Coverage Enforcement: A Practical Guide

DeepSource combines static analysis with automated code fixes — it doesn't just report issues, it opens PRs to fix them. This guide covers DeepSource setup, autofix configuration, coverage enforcement, and integrating quality gates into your CI pipeline.


What Makes DeepSource Different

Most code quality tools report problems and stop. DeepSource goes further:

  • Autofix PRs — for many issues, DeepSource opens a pull request with the fix already written
  • Transformers — automated code style and formatting fixes (Black, isort, gofmt, Prettier)
  • Coverage enforcement — sets a minimum coverage threshold and fails PRs that drop below it
  • Native GitHub/GitLab integration — no separate CI step; analysis runs on DeepSource's infrastructure

This changes the workflow from "find issues → manually fix" to "find issues → accept fix PR."


Setup

Connect to GitHub

  1. Go to deepsource.io and sign in with GitHub
  2. Select your repository to analyze
  3. DeepSource auto-detects your tech stack and suggests analyzers

Create .deepsource.toml

All DeepSource configuration lives in .deepsource.toml at the repo root:

version = 1

[[analyzers]]
name = "python"
enabled = true

  [analyzers.meta]
  runtime_version = "3.x.x"
  max_line_length = 120

[[analyzers]]
name = "test-coverage"
enabled = true

  [analyzers.meta]
  should_fail_with_low_coverage = true
  coverage_threshold = 80

[[analyzers]]
name = "secrets"
enabled = true

[[transformers]]
name = "black"
enabled = true

[[transformers]]
name = "isort"
enabled = true

For JavaScript/TypeScript:

version = 1

[[analyzers]]
name = "javascript"
enabled = true

  [analyzers.meta]
  environment = ["browser", "node"]

[[analyzers]]
name = "typescript"
enabled = true

[[analyzers]]
name = "test-coverage"
enabled = true

  [analyzers.meta]
  should_fail_with_low_coverage = true
  coverage_threshold = 75

[[transformers]]
name = "prettier"
enabled = true

[[transformers]]
name = "eslint"
enabled = true

For Go:

version = 1

[[analyzers]]
name = "go"
enabled = true

[[analyzers]]
name = "test-coverage"
enabled = true

  [analyzers.meta]
  should_fail_with_low_coverage = true
  coverage_threshold = 70

[[transformers]]
name = "gofmt"
enabled = true

Autofix in Practice

When DeepSource finds a fixable issue, it opens a PR from a deepsource-fix branch. The PR description explains the issue and shows the diff.

What DeepSource Can Autofix

Python:

  • E501 — line too long (reformatted by Black)
  • isort violations — import ordering
  • W292 — no newline at end of file
  • F401 — unused imports
  • E711 — comparison to None with == (should be is)
  • Security: B105 — hardcoded password string
  • Antipatterns: mutable default arguments, incorrect super() usage

JavaScript/TypeScript:

  • ESLint fixable rules (no-var, prefer-const, arrow-body-style, semicolons)
  • Prettier formatting violations
  • no-unused-vars (can auto-remove in some cases)
  • Import ordering

Java:

  • Unused imports
  • String comparisons with == instead of .equals()
  • Empty catch blocks (adds comment)

Reviewing Autofix PRs

Autofix PRs should be reviewed, not blindly merged. Common gotchas:

  • Black reformats long strings in ways that lose intentional alignment
  • isort changes import order in files with conditional imports that depend on order
  • Removing "unused" imports that are actually used for side effects

Configure which autofixes run by enabling/disabling individual transformers in .deepsource.toml.


Test Coverage Enforcement

Uploading Coverage Reports

DeepSource requires you to upload coverage in your CI pipeline:

Python (pytest):

# .github/workflows/test.yml
- name: Run tests with coverage
  run: |
    pip install pytest pytest-cov
    pytest --cov=src --cov-report xml:coverage.xml

- name: Upload coverage to DeepSource
  uses: deepsourcelabs/test-coverage-action@master
  with:
    key: python
    coverage-file: coverage.xml
    dsn: ${{ secrets.DEEPSOURCE_DSN }}

JavaScript (Jest):

- name: Run tests with coverage
  run: |
    npm run test -- --coverage --coverageReporters=lcov

- name: Upload coverage to DeepSource
  uses: deepsourcelabs/test-coverage-action@master
  with:
    key: javascript
    coverage-file: coverage/lcov.info
    dsn: ${{ secrets.DEEPSOURCE_DSN }}

Go:

- name: Run tests with coverage
  run: go test ./... -coverprofile=coverage.out

- name: Convert to lcov format
  run: |
    go install github.com/jandelgado/gcov2lcov@latest
    gcov2lcov -infile=coverage.out -outfile=coverage.lcov

- name: Upload coverage to DeepSource
  uses: deepsourcelabs/test-coverage-action@master
  with:
    key: go
    coverage-file: coverage.lcov
    dsn: ${{ secrets.DEEPSOURCE_DSN }}

DSN (Data Source Name)

Find your DSN in DeepSource Dashboard → Settings → Reporting → Test Coverage:

DEEPSOURCE_DSN=https://yourtoken@deepsource.io

Add this as a GitHub repository secret.


Configuring Coverage Thresholds

In .deepsource.toml, the coverage analyzer configuration:

[[analyzers]]
name = "test-coverage"
enabled = true

  [analyzers.meta]
  # Fail PRs that drop overall coverage below this %
  should_fail_with_low_coverage = true
  coverage_threshold = 80
  
  # Allow coverage to drop by this amount on PRs (avoid blocking every PR)
  max_decrease_threshold = 2

With should_fail_with_low_coverage = true, DeepSource reports a failing check on GitHub PRs when:

  • Overall coverage drops below coverage_threshold
  • Coverage on the PR's changed files is below threshold

Per-File Coverage Visibility

DeepSource shows coverage per file in the PR diff. This makes it easy to see exactly which new lines aren't covered, rather than just an overall percentage.


Analyzer Configuration Deep Dive

Python Analyzer

[[analyzers]]
name = "python"
enabled = true

  [analyzers.meta]
  runtime_version = "3.x.x"
  max_line_length = 120
  
  # Skip specific files
  skip_doc_coverage = false  # Set true to not require docstrings
  
  # Enable specific checks
  type_stubs = ["boto3-stubs", "types-requests"]

Disabling Specific Issues

Add inline comments to suppress specific issues:

import os  # noqa: F401 - used by downstream modules via __init__.py

password = "dev-only-not-prod"  # skipcq: PY-A0001 - local dev config only

Or configure globally in .deepsource.toml:

[[analyzers]]
name = "python"
enabled = true

  [analyzers.meta]
  skip_issues = ["PY-W0069", "PYL-W0611"]

Running DeepSource Locally

The deepsource CLI lets you run analysis locally before pushing:

# Install
curl https://deepsource.io/cli <span class="hljs-pipe">| sh

<span class="hljs-comment"># Analyze current directory
deepsource analyze --directory . --analyzer python

<span class="hljs-comment"># Upload coverage from local test run
deepsource report --analyzer test-coverage --key python --value-file coverage.xml

Comparing DeepSource to Alternatives

Feature DeepSource SonarQube CodeClimate
Autofix PRs
Auto-transformers ✅ (Black, Prettier, gofmt)
Self-hosting ✅ (Enterprise)
Secrets detection
Custom rules Limited ✅ (Java plugins) Limited
Pricing (OSS) Free Free (SonarCloud) Free

DeepSource's autofix is its standout feature. For teams drowning in "your code has 847 issues" reports, automated fixes convert backlog into merged PRs.


Full CI Workflow

name: CI with DeepSource
on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: pip install -r requirements.txt pytest pytest-cov
      
      - name: Run tests
        run: |
          pytest \
            --cov=src \
            --cov-report=xml:coverage.xml \
            tests/
      
      - name: Upload coverage to DeepSource
        uses: deepsourcelabs/test-coverage-action@master
        if: always()  # Upload even if tests fail
        with:
          key: python
          coverage-file: coverage.xml
          dsn: ${{ secrets.DEEPSOURCE_DSN }}

Note: Always upload coverage even when tests fail (if: always()). This lets DeepSource show which tests failed and what coverage those failures affect.


Summary

DeepSource's autofix workflow is its primary differentiator:

  1. Static analysis finds issues — bugs, security vulnerabilities, code smells
  2. Autofix opens a PR — you review and merge, not manually implement
  3. Transformers format code — Black, isort, Prettier run on push
  4. Coverage gate blocks merges — PRs that drop below threshold fail CI

For teams with significant technical debt, the autofix feature converts abstract "we should fix this" into concrete "here's the PR, approve it."

The coverage enforcement is straightforward but effective: define a threshold in .deepsource.toml, upload coverage in CI, and let DeepSource block merges that regress coverage below the line.

Read more