Migrating from Codecov or Coveralls¶
Both services and gocov measure the same thing and hand it to the same places: a status on the commit, a comment on the pull request, a badge in the README. So a migration is mostly a translation — the status thresholds you have become a coverage gate, the flags or parallel jobs become parts, the upload step is swapped for gocov's — and it can be done with the old tool still running.
What changes underneath: no upload secret (an identity token from your CI replaces it), the same product on GitHub, GitLab and Bitbucket Cloud, and a self-hostable single binary if you would rather run it yourself. What does not carry over: history. gocov starts measuring from the first upload, so run both for a while before you switch the badge and the required checks.
The order that works¶
- Sign in, claim the workspace and connect it to the forge. Nothing changes in your repos yet.
- Add the gocov upload step next to the existing one. From the next push both report; compare the numbers on a few pull requests. Paths that do not line up show as an empty diff coverage — Why coverage changed has the fixes.
- Set the gate from your old thresholds (tables below), on the workspace's settings page as the default for new repos and on each repo you have already uploaded from.
- Switch the required check in branch protection to gocov's, swap the badge, remove the old step, the old configuration file and the old token.
From Codecov¶
The upload step¶
codecov/codecov-action and gocov/gocov-action take the same shape. The differences are the inputs:
| Codecov input | gocov | Notes |
|---|---|---|
token |
(none) or token |
Grant the job id-token: write and no secret is needed; a GOCOV_TOKEN secret is the alternative — see tokens. |
use_oidc: true |
permissions: id-token: write |
The permission alone turns it on; there is no input. |
files |
files |
Same comma-separated list; globs allowed. |
directory, disable_search |
— | gocov never searches for reports: name them in files. |
flags |
part |
One part per upload; a Codecov upload with two flags becomes two uploads, one per part. |
name |
— | Uploads are listed by commit, part and time. |
fail_ci_if_error (default false) |
fail-on-error (default true) |
gocov fails the step by default when the upload fails; set false for the Codecov behaviour. |
slug |
— | Detected from the CI environment. |
verbose |
— | The step's log already says what was uploaded and what the server answered. |
Before and after, for a repository whose tests write coverage.xml:
# before
- uses: codecov/codecov-action@v5
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage.xml
flags: unit
fail_ci_if_error: true
# after
permissions:
contents: read
id-token: write
steps:
- uses: gocov/gocov-action@v1
with:
files: coverage.xml
part: unit
The format (Cobertura here) is detected from the file's content; every format Codecov accepts from a mainstream test runner is listed in Languages & formats. GitLab CI and Bitbucket Pipelines users swap the Codecov CLI call for the gocov component or the gocov pipe in the same way.
codecov.yml¶
gocov has no configuration file; the settings below live on the workspace and repository settings pages, and a few are
CLI or action inputs. The table maps each codecov.yml key to where it went.
codecov.yml |
gocov |
|---|---|
coverage.status.project.default.target: 80% |
Gate → Min coverage 80. |
coverage.status.project.default.target: auto + threshold: 2 |
Gate → Max coverage drop 2. Codecov compares with the base commit; gocov compares with the latest gate-passing upload on the default branch, so a drop cannot be laundered by re-running CI or ratcheted down push by push. |
coverage.status.patch.default.target: 80% |
Gate → Min diff coverage 80. |
coverage.status.patch.default.target: auto |
No direct equivalent. Set Min diff coverage to the total you expect of new code, or leave it empty: diff coverage is still reported on every PR. |
informational: true |
Leave the corresponding gate field empty. The number is still reported; it just cannot fail the status. |
threshold on patch |
— (diff coverage has a minimum, not a drop tolerance). |
if_ci_failed, only_pulls, branches, paths on a status |
— . The gate applies to every upload; the diff rule only has something to measure on PR uploads. |
flags, flag_management |
Parts: part: <name> on each upload. Parts merge as they arrive; there is no carryforward, so a commit's report is the parts it received. |
ignore |
Repository settings → Ignored files, or the ignore input on the upload — ignoring files. Same .gitignore-style globs. |
fixes |
The CLI's -path-prefix; Go modules are detected from go.mod. See Why coverage changed. |
comment.layout, comment.behavior |
The PR comment has one layout — total, delta, diff coverage, the uncovered changed lines — and always updates in place. |
comment.require_changes |
— (the comment is posted on every PR upload). |
github_checks.annotations |
On by default: the check run annotates uncovered changed lines once the workspace is connected. |
codecov.require_ci_to_pass, notify.after_n_builds |
— . gocov reports as soon as an upload lands; see the note on parts and the gate below. |
coverage.range, coverage.precision, coverage.round |
— . The badge is red below 50%, yellow to 75%, green above; percentages carry one decimal. |
codecov.max_report_age |
— (uploads are accepted for any commit gocov can see). |
One behavioural difference to plan for: with several parts, Codecov waits for after_n_builds before deciding;
gocov evaluates the gate against the merged report as parts arrive, so it can fail transiently until the last
part lands and then correct itself. Sequence the required check after all coverage jobs, as parts
describes.
Required checks and the badge¶
In branch protection, replace codecov/project and codecov/patch with gocov (the commit status) or
gocov coverage (the check run) — one status carries both rules. Then the badge:
<!-- before -->
[](https://codecov.io/gh/myorg/myrepo)
<!-- after -->
[](https://app.gocov.dev/repos/myorg/myrepo?ref=badge)
Finally: delete codecov.yml, the CODECOV_TOKEN secret, and uninstall the Codecov app from the organization.
From Coveralls¶
The upload step¶
| Coveralls input | gocov | Notes |
|---|---|---|
github-token |
(none) | gocov never needs a GitHub token in the job: identity comes from id-token: write (or a GOCOV_TOKEN secret), and statuses are posted through the workspace's connection. |
file, files (space-separated) |
files (comma-separated) |
Globs allowed. |
format |
— | Detected from the file's content. |
flag-name |
part |
|
parallel: true, parallel-finished: true |
— | Parts merge as they arrive; there is no finish job. |
carryforward |
— | No carryforward: a commit's report is the parts it received. |
base-path |
— | Report paths have to match the repository's; see Why coverage changed. |
fail-on-error (default true) |
fail-on-error (default true) |
Same. |
allow-empty |
— | An empty report is an error. |
compare-ref |
— | A PR is compared against its base through the forge's diff; the default branch against the previous upload. |
coveralls-endpoint |
server |
Only when self-hosting gocov. |
A matrix with a finish job collapses to the matrix alone:
# before
test:
strategy: { matrix: { os: [ubuntu, macos] } }
steps:
- uses: coverallsapp/github-action@v2
with:
parallel: true
flag-name: ${{ matrix.os }}
finish:
needs: test
steps:
- uses: coverallsapp/github-action@v2
with:
parallel-finished: true
carryforward: "ubuntu,macos"
# after
permissions:
contents: read
id-token: write
test:
strategy: { matrix: { os: [ubuntu, macos] } }
steps:
- uses: gocov/gocov-action@v1
with:
files: coverage.out
part: ${{ matrix.os }}
Thresholds, checks and the badge¶
Coveralls' repository settings map onto the gate: Coverage threshold for failure is Min coverage, Coverage decrease threshold for failure is Max coverage drop. Coveralls has no per-PR-lines rule; Min diff coverage is the one you gain.
Replace the coverage/coveralls required check with gocov or gocov coverage, then the badge:
<!-- before -->
[](https://coveralls.io/github/myorg/myrepo?branch=main)
<!-- after -->
[](https://app.gocov.dev/repos/myorg/myrepo?ref=badge)
Finally: delete .coveralls.yml if you have one, the COVERALLS_REPO_TOKEN secret, and the Coveralls webhook or app.
Tokens¶
Neither the Codecov token nor the Coveralls repo token has an equivalent that you paste. On GitHub Actions, Bitbucket
Pipelines and GitLab CI the job proves its own identity with a short-lived OIDC token the forge mints, and gocov
verifies it through the workspace's connection — nothing to create, share or rotate. The per-CI pages show the one
line that turns it on. A CI that cannot mint one uses the workspace's upload token as a GOCOV_TOKEN variable
instead; owners find it on the setup page.
What you will not find¶
- History import. gocov starts at the first upload. The delta on the first PR compares with the first default-branch upload it has, so let the default branch upload once before you judge the numbers.
- Carryforward. A commit's merged report is the parts that were uploaded for it.
- Per-status branch, path and flag filters. One gate per repository, evaluated on the whole report.
- A configuration file. Gate, ignore patterns and connections are settings on the workspace and repository pages, visible to the whole team; per-upload choices are inputs on the upload step.
Self-hosting changes none of the above — set server on the upload step (or GOCOV_SERVER in CI) to your instance;
Self-hosting covers the rest.