reviewdog/action-golangci-lint: Go lint findings as pull request review comments
Run golangci-lint with reviewdog
At a glance
- What is it?
- A GitHub Action that runs golangci-lint and routes its output through reviewdog, so lint failures land as annotations or review comments on the pull request diff. It is a thin wrapper, and its value and its limits both come from that.
- Who is it for?
- Adopt it if your team already runs golangci-lint and wants findings attached to the diff instead of buried in a CI log, and if you are willing to pin golangci_lint_version rather than follow the default latest. Do not adopt it if you need findings outside a pull request context, or if you want the action to control which linters run; that is golangci-lint's job and the action only passes flags through.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 1 day ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 29, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The problem: golangci-lint output is a wall of text, not a review
golangci-lint aggregates many Go linters behind one command. That aggregation is the point, and it is also why its output is hard to act on in CI. A failing run prints every finding to the job log, and the reviewer has to match file and line numbers in the log against the diff in another browser tab. The README frames the action's purpose narrowly: it runs golangci-lint with reviewdog on pull requests "to improve code review experience." Nothing more ambitious than that.
The audience is Go teams that already accept golangci-lint as their linter and want its findings placed where review happens. If your project has no golangci-lint configuration and no opinion about which linters to enable, this action does not supply that opinion. It supplies transport.
How the action works: golangci-lint in line-number format, piped to reviewdog
The README documents the invocation precisely: golangci-lint runs as `golangci-lint run --out-format=line-number <golangci_lint_flags>`. The line-number output format is what makes the rest possible, because reviewdog needs parseable positions to attach a finding to a file and line. The action then hands those findings to reviewdog with the reporter, level and filter settings you supply as inputs.
The inputs map onto reviewdog's own command-line flags, and the README says so directly: `level` is "same as `-level` flag of reviewdog", `reporter` is the same as `-reporter`, and `filter_mode` the same as its filtering mode. `filter_mode` defaults to `added`, which means only findings on lines added in the pull request are reported. That default is the reason the checkout step in every README example passes `fetch-depth: 0`; without full history and diff context, reviewdog has nothing to filter against.
Two inputs control the exit code, and they are not the same thing. `fail_level` decides whether reviewdog exits 1 when it finds at least one issue at or above a given severity, with possible values `none`, `any`, `info`, `warning` and `error`, defaulting to `none`. `fail_on_error` is marked deprecated in the README and replaced by `fail_level`. Because the default is `none`, a freshly added workflow reports findings without failing the job. That is a deliberate default for adoption, and a trap if you assume a green check means clean code.
The action is written in TypeScript and bundled to `dist/`, with `@actions/core`, `@actions/exec`, `@actions/cache` and `@actions/tool-cache` among its dependencies. That dependency list tells you what the wrapper actually does at runtime: download tools, cache them, execute them, report results.
Installing reviewdog/action-golangci-lint and getting a first run
There is no package to install locally. The action is consumed from a workflow file. The README's minimum example is a checkout step followed by the action itself, and it is worth copying exactly, including `fetch-depth: 0`.
name: reviewdog
on: [pull_request]
jobs:
golangci-lint:
name: runner / golangci-lint
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: golangci-lint
uses: reviewdog/action-golangci-lint@3dfdce20f5ca12d264c214abb993dbb40834da90 # v2.7.2After the job runs, you should see reviewdog output in the job log and findings attached to the pull request according to the reporter you chose. The README's minimum example sets no inputs at all, so it uses the defaults: `filter_mode` of `added`, `fail_level` of `none`, and the latest versions of Go, reviewdog and golangci-lint.
If you migrated from v1, the README says most workflows need no change beyond switching the version reference. The one thing to remove is any step that sets up Go or caches Go modules, because v2 does both itself. The README shows those steps commented out with the note "no need with v2", and lists the three cache paths: `~/.cache/golangci-lint`, `~/.cache/go-build` and `~/go/pkg/mod`. Caching is on by default through the `cache` input.
To pass linter flags, use `golangci_lint_flags`. The README's all-in-one example enables everything at once.
- name: golangci-lint
uses: reviewdog/action-golangci-lint@3dfdce20f5ca12d264c214abb993dbb40834da90 # v2.7.2
with:
golangci_lint_flags: "--enable-all --exclude-use-default=false"That configuration is aggressive and will surface findings from linters most projects have never enabled. It is a reasonable way to see what is there, not a reasonable steady state for an existing codebase.
Version drift is the real operational risk
By default the action installs the latest version of reviewdog and the latest version of golangci-lint. Both are inputs you can pin: `golangci_lint_version` and `reviewdog_version`. Leaving them unpinned means a new golangci-lint release can add linters, change defaults or alter output, and your pull requests change behaviour without any commit in your repository. The README does not discuss this trade-off; it simply lists the inputs and their defaults.
Go version selection has the same shape. `go_version` installs a specific version, and by default the latest Go 1.x is installed. `go_version_file` accepts a path to a `go.mod` file or a file containing only a Go version, which lets the toolchain follow the module declaration. When both are provided, the README states that `go_version` wins. That precedence rule is easy to miss and produces a silently ignored file reference.
There is also a note in the advanced example that deserves attention: golangci-lint does not report multiple errors on the same line from different linters, and reports only one of them. The README marks it as a NOTE and leaves it there. It means the number of annotations you see is not the number of findings that exist, which matters if anyone treats the annotation count as a quality metric.
When this action is the wrong tool
The action is pull request shaped. Its reporter options are `github-pr-check` and `github-pr-review`, and its default filter mode is `added`. Run it on a push to a long-lived branch and the filtering behaviour is not what the defaults assume. If you want lint results on every commit, on a schedule, or as a required status on a branch with no diff context, a plain golangci-lint step in the workflow is simpler and has fewer moving parts.
A second boundary is configuration ownership. The action passes `golangci_lint_flags` through and points at golangci-lint's own configuration file for everything else. If your team wants the linter set defined in one place, that place is `.golangci.yml` in your repository, not the workflow inputs. Teams that put linter selection in `golangci_lint_flags` end up with lint policy spread across workflow files.
A third case: repositories with large diffs and strict severity gates. Because `fail_level` defaults to `none`, the action reports without blocking until you change it, and once you set it to `error`, findings at that level fail the job. Whether that is desirable depends on whether your golangci-lint configuration has been tuned to produce a clean baseline. Without that baseline, turning on `fail_level` converts a review aid into a merge blocker.
What it is not: a replacement for golangci-lint or for go vet
The action contains no linting logic. Every check it reports comes from golangci-lint, and golangci-lint in turn runs a set of Go linters. The question of whether golangci-lint includes go vet is answered by golangci-lint's own documentation, not by this repository; the README here says nothing about which linters run. Likewise, choosing the best Go linter is a question about golangci-lint's linter list and your codebase, and this action is indifferent to the answer.
The meaningful alternative is not another reviewdog action but a direct golangci-lint invocation in the workflow, with the official golangci-lint GitHub Action or a downloaded binary. The difference in approach is where the reporting layer sits. A direct invocation prints to the log and fails the job according to golangci-lint's own exit behaviour. This action inserts reviewdog between the linter and the developer, which buys diff filtering, annotation and review-comment reporters, and severity-based exit codes, at the cost of an extra tool to version and an extra set of inputs to understand. If your reviewers already read CI logs comfortably, the wrapper adds surface without adding much.
Licence and the cost of keeping it current
The repository is MIT licensed, and the action depends on golangci-lint and reviewdog, which are separate projects with their own licences. MIT on the wrapper says nothing about the tools it downloads at runtime. If licence review matters to your organisation, the wrapper is the easy part; check the licences of golangci-lint and reviewdog directly.
Upgrade cost is mostly a version-pinning exercise. The action's last push was on 2026-03-15, and v2.10.0 was released the same day, following v2.9.0 on 2026-03-14 and v2.8.0 on 2025-03-25. The gap between v2.8.0 and v2.9.0 shows the release cadence is not uniform, so a workflow that tracks the latest tag can jump several months of changes at once. Pinning the action by commit SHA, as every README example does, plus pinning `golangci_lint_version`, gives you a reproducible combination. The README does not document a rollback procedure, so the practical rollback is reverting the SHA and the version input together in the workflow file.
Editorial conclusion
Adopt it if your team already runs golangci-lint and wants findings attached to the diff instead of buried in a CI log, and if you are willing to pin golangci_lint_version rather than follow the default latest. Do not adopt it if you need findings outside a pull request context, or if you want the action to control which linters run; that is golangci-lint's job and the action only passes flags through. Before rolling it out, verify two things in your own repository: that your checkout step uses fetch-depth: 0, since the default filter_mode of added depends on diff context, and that the golangci_lint_version you pin actually accepts the flags you put in golangci_lint_flags, because the action forwards them without validation.
Frequently asked questions
Does golangci-lint include go vet?
The README for this action does not say which linters golangci-lint runs or whether go vet is among them. That is documented by golangci-lint itself, which this action invokes with `golangci-lint run --out-format=line-number`.
What is the best Golang linter for use with reviewdog/action-golangci-lint?
This action does not choose linters. It forwards `golangci_lint_flags` to golangci-lint and otherwise defers to golangci-lint's configuration file, so linter selection is a golangci-lint question rather than an action question.
What does "CI" mean in GitHub Actions?
The README does not define CI. It shows the action running inside a workflow under `.github/workflows/reviewdog.yml` triggered by `on: [pull_request]`, which is the context this action is built for.
How can I lint Go code with reviewdog/action-golangci-lint?
Add a workflow triggered on `pull_request` with a checkout step using `fetch-depth: 0`, then a step that uses the action. The README's minimum example sets no inputs and relies on the defaults, so findings are filtered to added lines and the job does not fail.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/reviewdog-action-golangci-lint)