Publish from CI
Coverage reports, eval results, benchmark dashboards, release notes — if
your pipeline produces HTML, one curl puts it at a stable URL your team
can open, comment on, and diff against the previous run. No Pages setup, no
artifact-zip downloads.
The pattern
Store a scoped token (just reports:write) in your CI
secret store, then:
curl -fsS -X POST https://commareports.com/api/v1/reports \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html coverage/index.html \
'{title: "Coverage — \(env.GITHUB_SHA[0:7])", html: $html}')"
To keep one URL per report instead of one per run, create the report
once, save its id, and PATCH it on every run — each push appends a
revision, reviewers can diff any two, and bookmarks never go
stale:
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html coverage/index.html '{html: $html}')"
GitHub Actions
name: coverage-report
on:
push:
branches: [main]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci && npm run coverage
- name: Publish coverage to Comma
env:
COMMA_API_TOKEN: ${{ secrets.COMMA_API_TOKEN }}
REPORT_ID: ${{ vars.COMMA_COVERAGE_REPORT_ID }}
run: |
curl -fsS -X PATCH "https://commareports.com/api/v1/reports/$REPORT_ID" \
-H "Authorization: Bearer $COMMA_API_TOKEN" \
-H "Content-Type: application/json" \
-d "$(jq -n --rawfile html coverage/index.html '{html: $html}')"
The same two lines work in GitLab CI, CircleCI, Buildkite, or a cron on a box — it's plain HTTPS.
Per-provider guides
Each provider has its own way of failing to show you an HTML report, and its own syntax for publishing on red builds:
- GitHub Actions — artifact zips, the 1 MiB job-summary cap, and posting the link back on the PR
- GitLab CI — why GitLab.com won't render HTML artifacts inline, and skipping the Pages deploy
- Jenkins — the
DirectoryBrowserSupport.CSPproperty behind every unstyled HTML Publisher report - CircleCI — 30-day artifact retention and URLs that change every job
- Azure Pipelines — retention policies, the
markdown-only run summary, and mapping secret variables into
env: - Bitbucket Pipelines — the 14-day artifact clock and a Test Reports tab that only reads JUnit XML
By report type: Playwright, Cypress, pytest, coverage, Lighthouse, Allure, k6 load tests, JMeter, Newman / Postman, security scans, Terraform plans, dbt docs, and Great Expectations data docs.
Notes for pipelines
- HTML limit is 5 MB. Bigger payloads (screenshots, JS bundles) go in as assets — 25 MB per file.
- Rate limits are per token (60/min by default). One report per build is nowhere near it.
- Notify on push: add a webhook notification on
revision.createdand the new revision announces itself in Slack or Discord. - Access control: CI-published reports follow the same sharing model as everything else — keep them team-visible rather than public-by-default.
- Interactivity survives. Report HTML is stored verbatim and rendered with scripts enabled inside a sandboxed iframe, so a self-contained report keeps working — see interactive HTML reports. The exception is a report that fetches sibling data files at view time; publish those as a digest with the archive attached.