# golangci-lint-action: GitHub Actions setup and constraints

> golangci-lint-action is the authors' official GitHub Action for running golangci-lint in Go workflows. It supports pinned linter versions, multiple operating systems, workspaces, and GitHub annotations, but delegates lint behavior to golangci-lint configuration.

**golangci/golangci-lint-action** — Official GitHub Action for golangci-lint from its authors

- Repository: https://github.com/golangci/golangci-lint-action
- Website: https://github.com/marketplace/actions/golangci-lint
- Stars: 1,452 · Forks: 197
- Language: TypeScript
- License: MIT
- Published: 2026-10-16 · Updated: 2026-10-16 · Language: en
- Canonical page: https://hysenlabs.com/projects/golangci-golangci-lint-action

## The CI integration gap addressed by golangci-lint-action

golangci-lint-action connects an existing golangci-lint setup to GitHub Actions. It is intended for Go repositories that want static analysis findings to appear in workflow runs and GitHub annotations instead of maintaining a separate script that downloads the executable, selects a version, runs it, and translates output. The README identifies this repository as the official action from golangci-lint's authors, but it does not present the project as a replacement for golangci-lint itself.

That distinction matters when choosing a tool. golangci-lint determines the enabled linters, configuration, issue set, and exit behavior. The action supplies the GitHub Actions integration around that command. Repository-specific files such as `.golangci.yml` therefore remain central to adoption. A workflow can run successfully while changing the selected golangci-lint version exposes new findings or changing the linter configuration changes the result.

The README recommends placing the action in a job separate from `go test` and similar work. GitHub runs jobs in parallel, so a dedicated lint job keeps its status visible as its own check and avoids embedding linting inside an unrelated test step. The action reports issues found by linters and includes examples of annotations and colored log output. The README does not quantify reporting accuracy or document an alternative report format for this action.

## Version selection and the v9 GitHub Actions boundary

Workflow examples use `golangci/golangci-lint-action@v9` and set `version: v2.13`. This separates two independently selected versions: the major version of the GitHub Action and the golangci-lint release that the workflow requests. The compatibility notes state that `v8.0.0` works with golangci-lint `>= v2.1.0` and that `v7.0.0` supports golangci-lint v2 only. The README also states that `v9.0.0` requires the Node.js `node24` runtime.

The latest release listed for the action is `v9.3.0`, published on 2026-06-29. The repository was not archived, and its last push was on 2026-10-01. Those facts support describing the project as recently changed, but they do not establish compatibility with every future GitHub Actions runner image or Go release.

Older action behavior has also changed across major releases. According to the compatibility notes, `v6.0.0+` removed the `annotations` option and the default `github-actions` output format. Version `v5.0.0+` removed `skip-pkg-cache` and `skip-build-cache` because Go caching is handled by `actions/setup-go`. Version `v4.0.0+` requires an explicit `actions/setup-go` step, and `skip-go-installation` has been removed. These are concrete upgrade constraints for maintainers copying older workflows.

## A first lint job using actions/setup-go

Create `.github/workflows/golangci-lint.yml` with the simple workflow shown below. It checks pushes to `main` and `master`, handles pull requests, grants read access to repository contents, checks out the code, installs stable Go, and invokes the action with golangci-lint `v2.13`.

```yaml
name: golangci-lint
on:
  push:
    branches:
      - main
      - master
  pull_request:
permissions:
  contents: read
jobs:
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-go@v6
        with:
          go-version: stable
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v9
        with:
          version: v2.13
```

After saving the file, push it to a covered branch or open a pull request. The workflow run should contain a `lint` job, and any reported linter issues should appear in the action output or GitHub annotations. The README states that `permissions: contents: read` is sufficient for this basic example. It also identifies optional pull request read access for use with `only-new-issues`, although the simple example does not enable that option.

The complete internal installation sequence is not documented in the README. Based on the repository layout, this is a TypeScript project with source under `src/`, action metadata in `action.yml`, and bundled JavaScript under `dist/`. The package dependencies include command execution, HTTP access, GitHub APIs, caching, and tool caching. That layout supports the documented purpose, but it does not establish every internal fallback or network path.

## Windows, multiple operating systems, and line-ending control

The README's multiple-OS example expands one lint check across `ubuntu-latest`, `macos-latest`, and `windows-latest`. All three matrix entries use `go-version: stable`, so this example tests operating-system behavior without varying the Go release.

```yaml
name: golangci-lint
on:
  push:
    branches:
      - main
      - master
  pull_request:
permissions:
  contents: read
jobs:
  golangci:
    strategy:
      matrix:
        go: [stable]
        os: [ubuntu-latest, macos-latest, windows-latest]
    name: lint
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-go@v6
        with:
          go-version: ${{ matrix.go }}
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v9
        with:
          version: v2.13
```

The README says Windows builds will likely need `.gitattributes` to ensure that Go files retain the expected line endings. The exact file content is:

```.gitattributes
*.go text eol=lf
```

This is a repository-level constraint, not merely a runner setting. The action can run across several operating systems, but checkout normalization still depends on repository configuration. A multi-platform matrix also creates three lint jobs in this example. The README presents that expansion as an option; it does not claim that every repository needs to lint every listed operating system.

The matrix can also vary Go versions, but the simple example deliberately keeps one value. Choosing several Go versions would test more compatibility combinations, although the README does not prescribe a minimum or maximum matrix.

## Go workspaces and module-scoped working directories

For a Go workspace containing multiple modules, the README does not treat the repository root as the only target. Its example has a `detect-modules` job run after checkout and Go setup. A shell step executes `go list -m -json`, combines the returned objects with `jq`, extracts each `Dir`, and writes a JSON array to `$GITHUB_OUTPUT`.

```yaml
env:
  GO_VERSION: stable
  GOLANGCI_LINT_VERSION: v2.13
jobs:
  detect-modules:
    runs-on: ubuntu-latest
    outputs:
      modules: ${{ steps.set-modules.outputs.modules }}
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-go@v6
        with:
          go-version: ${{ env.GO_VERSION }}
      - id: set-modules
        run: echo "modules=$(go list -m -json | jq -s '.' | jq -c '[.[].Dir]')" >> $GITHUB_OUTPUT
```

The lint job consumes that output as a matrix and passes each module path through `working-directory`:

```yaml
jobs:
  golangci-lint:
    needs: detect-modules
    runs-on: ubuntu-latest
    strategy:
      matrix:
        modules: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-go@v6
        with:
          go-version: ${{ env.GO_VERSION }}
      - name: golangci-lint ${{ matrix.modules }}
        uses: golangci/golangci-lint-action@v9
        with:
          version: ${{ env.GOLANGCI_LINT_VERSION }}
          working-directory: ${{ matrix.modules }}
```

This differs from checking out each module into a separate repository. One checkout is reused, module directories are discovered from Go's module metadata, and each lint invocation receives its own matrix entry. The README does not discuss filtering nested modules or behavior when `go list -m -json` returns a directory that should be excluded, so repository owners must inspect their workspace layout before adopting this exact pattern.

## Misuse cases, reporting limits, and direct golangci-lint execution

The clearest wrong-tool case is a repository that has not chosen golangci-lint rules or configuration. Adding this action does not create a complete static-analysis policy. It runs golangci-lint and reports what that program finds, so maintainers still need a suitable configuration and an agreed response to findings. The README does not document a default configuration supplied by the action or guarantee any particular linter set.

The action is also unsuitable when a build deliberately needs `skip-go-installation`, because the compatibility notes say that option was removed and `actions/setup-go` is now required. Copying an old workflow containing `skip-pkg-cache`, `skip-build-cache`, or the removed `annotations` option would carry obsolete configuration rather than a valid v9 setup.

A real alternative is to install `golangci-lint` directly in a normal `run` step and execute it as an explicit CI command. That approach gives the workflow author direct control over the executable path, command arguments, shell, and installation steps. The trade-off is that the author must own version selection, download or cache behavior, tool availability, and output handling. golangci-lint-action packages those concerns for GitHub Actions and documents GitHub annotations, but the README does not provide a full list of command-line pass-through options or explain every internal cache decision.

For local development, direct golangci-lint execution remains the simpler alternative because it runs in the developer's existing Go environment. The action is most relevant when lint results need to become a distinct GitHub Actions check.

## Maintenance, bundled runtime, and MIT upgrade work

The repository's last push was on 2026-10-01, and its release history includes `v9.3.0` from 2026-06-29. It is not archived. Those are maintenance signals, not a guarantee that workflow configuration will remain compatible with future runner images or external action releases.

The source is TypeScript, while `package.json` declares Node.js `>=24.0.0`. The `build` script runs TypeScript compilation and creates bundled action files under `dist/`. The repository includes both `src/` and `dist/`, which is consistent with developing the action in TypeScript while shipping generated JavaScript to the runner. The package uses `actions/checkout@v6` and `actions/setup-go@v6` in its documented examples, so adopting the action also means accepting ongoing maintenance of those surrounding workflow dependencies.

The project uses the MIT license. That license permits use, modification, distribution, and sublicensing subject to its notice and disclaimer conditions. This is not legal advice, and repository owners should read the included `LICENSE` file when redistributing modified action code. The README does not document a rollback procedure, a support policy for older golangci-lint versions, or migration automation from an earlier action major release. Those are concrete checks when upgrading an established CI workflow.

## Conclusion

Adopt golangci-lint-action when the repository already uses golangci-lint and wants its checks integrated into GitHub Actions. Do not treat it as a standalone linter configuration. Verify the selected Go version, golangci-lint version, working-directory value, permissions, and operating-system matrix first, because those inputs define what the workflow actually checks.

## FAQ

### How do I install golangci-lint-action in a GitHub Actions workflow?

Add a workflow file that runs `actions/checkout@v6` and `actions/setup-go@v6`, then uses `golangci/golangci-lint-action@v9` with a `version` such as `v2.13`. The v9 action requires the Node.js `node24` runtime.

### Does golangci-lint-action require actions/setup-go?

Yes. The compatibility notes state that `v4.0.0+` requires an explicit `actions/setup-go` installation step, and the examples use `actions/setup-go@v6`. The `skip-go-installation` option has been removed.

### How can I lint multiple Go modules with golangci-lint-action?

Use `go list -m -json` to discover module directories, expose their `Dir` values as a JSON array, and create a matrix from that output. Pass each directory to the action through `working-directory`.

### What should I change when running golangci-lint-action on Windows?

The README says Windows builds will likely need a `.gitattributes` entry containing `*.go text eol=lf`. Its multiple-OS examples also use `windows-latest` in the runner matrix.

## Sources

- [golangci/golangci-lint-action on GitHub](https://github.com/golangci/golangci-lint-action)
- [License: MIT](https://github.com/golangci/golangci-lint-action/blob/main/LICENSE)
- [Project website](https://github.com/marketplace/actions/golangci-lint)
- [README](https://github.com/golangci/golangci-lint-action/blob/main/README.md)
- [Releases](https://github.com/golangci/golangci-lint-action/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/golangci-golangci-lint-action
