Share an HTML report from Travis CI
Every other CI provider gives you something to complain about. Travis gives
you nothing to complain about, because there is nothing there: no artifacts
tab, no report viewer, no retention window to argue with. Your job writes
coverage/index.html, the job finishes, the container is destroyed, and the
report never existed as far as anyone outside that log is concerned.
The two workarounds are both bad:
catit into the log. Now the report is 4,000 lines of markup in a scrolling text pane. Nobody reads it. The failing test gets screenshotted into Slack, which is where this always ends.- Push it to your own S3 bucket. Now you own a bucket policy, a lifecycle rule, a public-access decision and a CORS config, in order to show someone a test report. The bucket outlives whoever set it up.
One call in after_script
Mint a scoped token with reports:write and nothing else,
add it as a repository environment variable with the display value off, create
the report once, and PATCH that id from every build:
language: python
python: "3.12"
install:
- pip install -r requirements.txt
script:
- pytest --cov --cov-report=html
after_script:
- |
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$COMMA_REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html htmlcov/index.html \
--arg title "Coverage — ${TRAVIS_BRANCH} @ ${TRAVIS_COMMIT:0:7}" \
'{title: $title, html: $html}')"
echo "Report → https://commareports.com/p/$COMMA_REPORT_ID"
after_script, not after_success — that is the whole trick. after_success
skips exactly the builds whose report someone wanted to read. after_script
runs either way and its own exit status does not change the build result, so a
transient publish failure cannot turn a green build red.
For a pull-request build, note that Travis does not expose secure environment variables to builds from forks. That is a security property worth keeping: the fork build simply skips the publish, and the branch build after the merge publishes the real thing.
What you get instead of a bucket
- A permanent URL. Each build appends a revision at the same address and any two revisions can be diffed — no lifecycle rule, no retention clock.
- It renders. The HTML is stored verbatim and served inside a sandboxed iframe with scripts enabled, so a coverage tree or a chart still works.
- Readers who are not in your Travis org. Visibility is private, team, domain-gated, or link, decided per report.
- Comments anchored to the content. A reviewer highlights the uncovered branch and pins a thread to it — see commenting on HTML.
- No infrastructure. No bucket, no policy, no CORS, nothing to inherit.
Limits
- HTML body: 5 MB. Larger files go in as assets at 25 MB per file, 250 MB per report.
- A report that loads sibling data files at view time can't, from the sandbox — publish a static digest with the archive attached, as in sharing an Allure report.
- Rate limit: 60 requests/minute per token.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history.
Related
- Publish from CI — the general pipeline pattern
- S3 static hosting alternatives — the bucket you were about to create
- Share a pytest report · Share a coverage report