# Share an HTML Report From Travis CI — There Is No Artifacts Tab

Canonical: https://commareports.com/ci/travis-ci-html-report
Published: 2026-09-02

> Travis CI has no artifact viewer at all: your HTML report exists for the length of the job and then the container is gone. One curl in after_script gives it a permanent URL.

# 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:

- **`cat` it 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](/docs/api-tokens) 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:

```yaml
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](/docs/sharing), decided per report.
- **Comments anchored to the content.** A reviewer highlights the uncovered
  branch and pins a thread to it — see
  [commenting on HTML](/comment-on-html).
- **No infrastructure.** No bucket, no policy, no CORS, nothing to inherit.

## Limits

- **HTML body: 5 MB.** Larger files go in as [assets](/docs/api) 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](/share-allure-report).
- **Rate limit: 60 requests/minute per token.**

## Try it

Comma is free — unlimited reports, unlimited commenters, unlimited revision
history.

**[Create your first report →](https://commareports.com/)**

### Related

- [Publish from CI](/docs/ci) — the general pipeline pattern
- [S3 static hosting alternatives](/alternatives/s3-static-hosting-alternatives)
  — the bucket you were about to create
- [Share a pytest report](/share-pytest-report) ·
  [Share a coverage report](/share-coverage-report)
