Skip to content

golangci/golangci-lint-action

Use this GitHub action with your project
Add this Action to an existing workflow or create a new one
View on Marketplace

Repository files navigation

golangci-lint-action

Build Status

It's the official GitHub Action for golangci-lint from its authors.

The action runs golangci-lint and reports issues from linters.

GitHub Annotations

Logs

Supporting Us

GitHub Sponsors Open Collective backers and sponsors Linter Authors

golangci-lint is a free and open-source project built by volunteers.

If you value it, consider supporting us; we appreciate it! ❤️

How to use

We recommend running this action in a job separate from other jobs (go test, etc.) because different jobs run in parallel.

Add a .github/workflows/golangci-lint.yml file with the following contents:

Simple Example
name: golangci-lint
on:
  push:
    branches:
      - main
      - master
  pull_request:

permissions:
  contents: read
  # Optional: allow read access to pull requests. Use with `only-new-issues` option.
  # pull-requests: read

jobs:
  golangci:
    name: lint
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: stable
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v8
        with:
          version: v2.1
Multiple OS Example
name: golangci-lint
on:
  push:
    branches:
      - main
      - master
  pull_request:

permissions:
  contents: read
  # Optional: allow read access to pull requests. Use with `only-new-issues` option.
  # pull-requests: read

jobs:
  golangci:
    strategy:
      matrix:
        go: [stable]
        os: [ubuntu-latest, macos-latest, windows-latest]
    name: lint
    runs-on: ${{ matrix.os }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: ${{ matrix.go }}
      - name: golangci-lint
        uses: golangci/golangci-lint-action@v8
        with:
          version: v2.1

You will also likely need to add the following .gitattributes file to ensure that line endings for Windows builds are properly formatted:

*.go text eol=lf
Go Workspace Example
name: golangci-lint

on:
  pull_request:
  push:
    branches:
      - main
      - master

env:
  GO_VERSION: stable
  GOLANGCI_LINT_VERSION: v2.1

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

  golangci-lint:
    needs: detect-modules
    runs-on: ubuntu-latest
    strategy:
      matrix:
        modules: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: ${{ env.GO_VERSION }}
      - name: golangci-lint ${{ matrix.modules }}
        uses: golangci/golangci-lint-action@v8
        with:
          version: ${{ env.GOLANGCI_LINT_VERSION }}
          working-directory: ${{ matrix.modules }}
Go Workspace Example (Multiple OS)
# ./.github/workflows/golangci-lint.yml
name: golangci-lint (multi OS)

on:
  pull_request:
  push:
    branches:
      - main
      - master

jobs:
  golangci-lint:
    strategy:
      matrix:
        go-version: [ stable, oldstable ]
        os: [ubuntu-latest, macos-latest, windows-latest]
    uses: ./.github/workflows/.golangci-lint-reusable.yml
    with:
      os: ${{ matrix.os }}
      go-version: ${{ matrix.go-version }}
      golangci-lint-version: v2.1
# ./.github/workflows/.golangci-lint-reusable.yml
name: golangci-lint-reusable

on:
  workflow_call:
    inputs:
      os:
        description: 'OS'
        required: true
        type: string
      go-version:
        description: 'Go version'
        required: true
        type: string
        default: stable
      golangci-lint-version:
        description: 'Golangci-lint version'
        type: string
        default: 'v2.1'

jobs:
  detect-modules:
    runs-on: ${{ inputs.os }}
    outputs:
      modules: ${{ steps.set-modules.outputs.modules }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: ${{ inputs.go-version }}
      - id: set-modules
        shell: bash # required for Windows to be able to use $GITHUB_OUTPUT https://github.com/actions/runner/issues/2224
        run: echo "modules=$(go list -m -json | jq -s '.' | jq -c '[.[].Dir]')" >> $GITHUB_OUTPUT

  golangci-lint:
    needs: detect-modules
    runs-on: ${{ inputs.os }}
    strategy:
      matrix:
        modules: ${{ fromJSON(needs.detect-modules.outputs.modules) }}
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-go@v5
        with:
          go-version: ${{ inputs.go-version }}
      - name: golangci-lint ${{ matrix.modules }}
        uses: golangci/golangci-lint-action@v8
        with:
          version: ${{ inputs.golangci-lint-version }}
          working-directory: ${{ matrix.modules }}

You will also likely need to add the following .gitattributes file to ensure that line endings for Windows builds are properly formatted:

*.go text eol=lf

Compatibility

  • v8.0.0 works with golangci-lint version >= v2.1.0
  • v7.0.0 supports golangci-lint v2 only.
  • v6.0.0+ removes annotations option, removes the default output format (github-actions).
  • v5.0.0+ removes skip-pkg-cache and skip-build-cache because the cache related to Go itself is already handled by actions/setup-go.
  • v4.0.0+ requires an explicit actions/setup-go installation step before using this action: uses: actions/setup-go@v5. The skip-go-installation option has been removed.
  • v2.0.0+ works with golangci-lint version >= v1.28.3
  • v1.2.2 is deprecated because we forgot to change the minimum version of golangci-lint to v1.28.3 (issue)
  • v1.2.1 works with golangci-lint version >= v1.14.0 (issue)

Options

version

(optional)

The version of golangci-lint to use.

When install-mode is:

  • binary (default): the value can be v2.3, v2.3.4, or latest to use the latest version.
  • goinstall: the value can be v2.3.4, latest, or the hash of a commit.
  • none: the value is ignored.
Example
uses: golangci/golangci-lint-action@v8
with:
  version: v2.1
  # ...

install-mode

(optional)

The mode to install golangci-lint: it can be binary, goinstall, or none.

The default value is binary.

Example
uses: golangci/golangci-lint-action@v8
with:
  install-mode: "goinstall"
  # ...

github-token

(optional)

When using the only-new-issues option, the GitHub API is used, so a token is required.

By default, it uses the github.token from the action.

Example
uses: golangci/golangci-lint-action@v8
with:
  github-token: xxx
  # ...

verify

(optional)

This option is true by default.

If the GitHub Action detects a configuration file, validation will be performed unless this option is set to false. If there is no configuration file, validation is skipped.

The JSON Schema used to validate the configuration depends on the version of golangci-lint you are using.

Example
uses: golangci/golangci-lint-action@v8
with:
  verify: false
  # ...

only-new-issues

(optional)

Show only new issues.

The default value is false.

  • pull_request and pull_request_target: the action gets the diff of the PR content from the GitHub API and uses it with --new-from-patch.
  • push: the action gets the diff of the push content (the difference between commits before and after the push) from the GitHub API and uses it with --new-from-patch.
  • merge_group: the action gets the diff by using the --new-from-rev option (relies on git). You should add the option fetch-depth: 0 to the actions/checkout step.
Example
uses: golangci/golangci-lint-action@v8
with:
  only-new-issues: true
  # ...

working-directory

(optional)

The golangci-lint working directory, useful for monorepos. The default is the project root.

Example
uses: golangci/golangci-lint-action@v8
with:
  working-directory: somedir
  # ...

args

(optional)

golangci-lint command line arguments.

Note

By default, the .golangci.yml file should be at the root of the repository. The location of the configuration file can be changed by using --config=.

Important

Adding a = between the flag name and its value is important because the action parses the arguments on spaces.

Example
uses: golangci/golangci-lint-action@v8
with:
  # In some rare cases,
  # you may need to use `${{ github.workspace }}` as the base directory to reference your configuration file.
  args: --config=/my/path/.golangci.yml --issues-exit-code=0
  # ...

problem-matchers

(optional)

Forces the usage of the embedded problem matchers.

By default, the problem matcher of Go (actions/setup-go) already handles the default golangci-lint output (text).

Works only with the text format (the golangci-lint default).

https://golangci-lint.run/usage/configuration/#output-configuration

The default value is false.

Example
uses: golangci/golangci-lint-action@v8
with:
  problem-matchers: true
  # ...

skip-cache

(optional)

If set to true, all caching functionality will be completely disabled. This takes precedence over all other caching options.

The default value is false.

Example
uses: golangci/golangci-lint-action@v8
with:
  skip-cache: true
  # ...

skip-save-cache

(optional)

If set to true, caches will not be saved, but they may still be restored, requiring skip-cache: false.

The default value is false.

Example
uses: golangci/golangci-lint-action@v8
with:
  skip-save-cache: true
  # ...

cache-invalidation-interval

(optional)

Periodically invalidate a cache every cache-invalidation-interval days to ensure that outdated data is removed and fresh data is loaded.

The default value is 7.

If the number is <= 0, the cache will always be invalidated (not recommended).

Example
uses: golangci/golangci-lint-action@v8
with:
  cache-invalidation-interval: 15
  # ...

Annotations

Currently, GitHub parses the action's output and creates annotations.

The restrictions of annotations are as follows:

  1. Currently, they don't support Markdown formatting (see the feature request).
  2. They aren't shown in the list of comments. If you would like to have comments, please up-vote the issue.
  3. The number of annotations is limited.

Permissions required:

permissions:
  # Required: allow read access to the content for analysis.
  contents: read
  # Optional: allow read access to pull requests. Use with `only-new-issues` option.
  pull-requests: read

For annotations to work, use the default format output (text) and either use actions/setup-go in the job or enable the internal problem matchers.

Performance

The action was implemented with performance in mind:

  1. We cache data from golangci-lint analysis between builds by using @actions/cache.
  2. We don't use Docker because image pulling is slow.
  3. We do as much as we can in parallel, e.g., we download the cache and the golangci-lint binary in parallel.

For example, in the golangci-lint repository, running this action without the cache takes 50s, but with the cache it takes 14s:

  • in parallel:
    • 4s to restore 50 MB of cache
    • 1s to find and install golangci-lint
  • 1s to run golangci-lint (it takes 35s without cache)

Internals

We use a JavaScript-based action. We don't use a Docker-based action because:

  1. Pulling Docker images is currently slow.
  2. It is easier to use caching from @actions/cache.

We support different platforms, such as ubuntu, macos, and windows with x32 and x64 architectures.

Inside our action, we perform three steps:

  1. Set up the environment in parallel:
    • Restore the cache from previous analyses.
    • Fetch the action config and find the latest golangci-lint patch version for the required version (users of this action can specify only the minor version of golangci-lint). After that, install golangci-lint using @actions/tool-cache.
  2. Run golangci-lint with the arguments args specified by the user.
  3. Save the cache for later builds.

Caching internals

  1. We save and restore the following directory: ~/.cache/golangci-lint.
  2. The primary caching key looks like golangci-lint.cache-{runner_os}-{working_directory}-{interval_number}-{go.mod_hash}. The interval number ensures that we periodically invalidate our cache (every 7 days). The go.mod hash ensures that we invalidate the cache early — as soon as dependencies have changed.
  3. We use restore keys: golangci-lint.cache-{runner_os}-{working_directory}-{interval_number}-. GitHub matches keys by prefix if there is no exact match for the primary cache.

This scheme is basic and needs improvements. Pull requests and ideas are welcome.