Skip to content

coverage.py's HTML report, as a link on every PR

coverage html turns your test run into a browsable site with per-line hit highlighting. Instead of letting it rot in an artifacts zip, publish it — reviewers open a link and see exactly which lines your diff missed.

The artifact dead-end

The console summary tells you coverage dropped 2%. The HTML report tells you which lines. Guess which one ends up zipped in the artifacts tab where nobody opens it.

Reviewers deserve the annotated source — yellow partial branches, red misses — one click from the PR, not a download-and-extract ritual.

Two lines to a live link

Generate the report like you already do, then hand the folder to the action:

Generate the report

coverage run -m pytest && coverage html

.github/workflows/ci.yml

- uses: bitgate/ship-page-action@v1
  if: always()
  with:
    path: htmlcov/

Omit path entirely and the action auto-detects htmlcov/ on its own. The URL lands in the job summary — add comment: true and it lands on the pull request too.

What you get

  • Per-file annotated source renders exactly as coverage.py wrote it — served from the root of its own subdomain
  • Works with pytest-cov too: --cov-report=html produces the same htmlcov/ layout
  • Omit path entirely — the action auto-detects htmlcov/

Questions

Does it work with pytest-cov?

Yes — pytest --cov=. --cov-report=html writes htmlcov/, the exact layout the action looks for.

Can I keep one stable coverage URL per branch?

Yes: name: cov-${{ github.ref_name }} plus an sp_ API key redeploys the same URL on every push to that branch.

Is my source code exposed?

The report contains annotated source, so treat the URL like a secret: slugs are unguessable and there's no listing. For sensitive repos, use expiry — anonymous drops self-destruct after 7 days.

Try it on your next run

Anonymous deploys need no account and no key — paste the step, get a link. When you want stable URLs that redeploy in place, an API key and a name are all it takes.