Publish an HTML report from Bitbucket Pipelines
Bitbucket Pipelines is happy to build the report and then gives it nowhere
to live. artifacts: keeps the files for 14 days and serves them as a
download. The Test Reports tab renders results, but only from
JUnit-format XML — a coverage page, a Lighthouse run, a benchmark
dashboard or an eval scoreboard has no tab to appear in. And Bitbucket
Cloud has no Pages-style static hosting to deploy to, so the usual fallback
isn't there either.
What teams do instead: download the zip, or screenshot it into the PR.
One step, one URL
Store a scoped token (reports:write is enough) as a
secured repository variable, create the Comma report once, and keep its
id in a plain repository variable:
pipelines:
default:
- step:
name: Test and publish report
image: node:22
script:
- npm ci && npm run coverage
- >-
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 coverage/index.html
--arg title "Coverage — ${BITBUCKET_COMMIT:0:7}"
'{title: $title, html: $html}')"
after-script:
- echo "Report: https://commareports.com/p/$COMMA_REPORT_ID"
Two details earn their keep:
PATCH, notPOST. Each run appends a revision at the same URL — one bookmark per report, and any two runs can be diffed.- Publish on red too. Put the call in
after-script(which runs even when the step fails) if the report is the thing you most want when the build breaks.after-scriptalso hasBITBUCKET_EXIT_CODEavailable, so the revision title can carry the outcome.
jq isn't in every image — apt-get install -y jq or pick an image that
has it. In a minimal image, generating the JSON body with python3 -c is
the shorter path.
What you get that a 14-day artifact can't give you
- The link outlives the sprint. Revisions stay. There is no expiry clock ticking under the URL you pasted in the PR.
- Comments anchored to the report. A reviewer highlights the failing row and pins a thread to it, and the thread survives the next twelve runs — see commenting on HTML.
- Diffs between runs. "What changed since the last green build?" is a revision diff, not a memory exercise.
- Readers without repository access. A Comma report can be team-visible, domain-gated, or shared with a named reviewer who has no Bitbucket seat — see the sharing model.
- Announcements. A webhook on
revision.createdposts each run into Slack or Discord.
Limits worth knowing before you wire it up
- HTML body: 5 MB. Screenshots, trace zips and JS bundles go in as assets — 25 MB per file, 250 MB per report.
- Scripts run, sandboxed. Report HTML is stored verbatim and rendered
in an iframe with
sandbox="allow-scripts"and noallow-same-origin, so an interactive report keeps working. What needs care is a report that loads sibling files at view time (Allure, JMeter's dashboard) — upload them as assets alongside the HTML and relative references are rewritten. See Playwright or Cypress for the shape. - Rate limits are per token, 60/minute by default. One publish per build is nowhere near it.
Try it
Comma is free — unlimited reports, unlimited commenters, unlimited revision history. Add the step to one pipeline and send a link that still works next quarter.
Related
- Publish from CI — the general pipeline pattern
- GitHub Actions · GitLab CI · Jenkins · CircleCI
- Azure Pipelines — retention policies and the markdown-only summary