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.CSP property 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.created and 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.