README
¶
Operationalising golangci-lint
Presentation slides and material for talk presented at Golang Meetup Bangalore on 31st August 2024.
Ideally the slides should have been accessible at https://go-talks.appspot.com/github.com/sudo-suhas/operationalising-golangci-lint/2024-08-golang-bangalore-meetup.slide. However, due to https://github.com/golang/go/issues/58906, the functionality is currently broken.
As an alternative, the slides have been hosted via Google App Engine and can be accessed here.
Video
Configuration sheet
golangci-lint-linters-v1.57.1 sheet
This sheet helps to manage the configuration for golangci-lint. It can also help with generating
the 2 configuration files.
⚠️ The configuration was last updated for version v1.57.1 of golangci-lint.
available-linters
This sheet lists the linters that were available in version v1.57.1 with the following columns:
- Linter: The name of the linter with a link to the source or website for it. If applicable, there is also a link to the golangci-lint’s documentation for configuring the linter. For example, https://staticcheck.io/ and https://golangci-lint.run/usage/linters/#staticcheck.
- Description: A short description of the linter.
- Local: This field indicates whether the linter is enabled or disable in the local config file
variant -
.golangci.toml. - Prod: This field indicates whether the linter is enabled or disable in the prod config file
variant -
.golangci-prod.toml. - Comments: Self explanatory. The comments try to justify why a particular linter is enabled or disabled.
- Settings: If the linter is enabled, the value in this field is used to configure the linter.
The settings are in TOML format. These settings are hand-coded with 2 exceptions. In case of
gocriticandrevive, the settings are generated from their respective sheets (explained below).
Why do we need 2 variants?
In my experience, having these 2 variants works well:
.golangci.toml- local development and testing.golangci-prod.toml- continuous integration (CI)
The difference is that the prod version includes some more intensive checks that may take longer to run, but are important for ensuring code quality and security. Some examples of heavy linters that we would want to enable only in CI: unused, gocognit, gocritic, gosec.
gocritic@v0.11.2
This sheet lists the checks that were available in version v0.11.2 of gocritic that was packaged
into golangci-lint@v1.57.1. The sheet is similar to the available-linters sheet and is used to
manage the 100+ checks provided by gocritic. The checks that are enabled along with their optional
settings are used to build the complete settings for gocritic in golangci-lint’s configuration.
📒 The function BUILD_GOCRITIC_SETTINGS is defined in the Apps Script project
linked to the sheet.
revive@v1.3.7
This sheet lists the checks that were available in version v1.3.7 of revive that was packaged
into golangci-lint@v1.57.1. The sheet is similar to the available-linters sheet and is used to
manage the 77 checks provided by revive. The checks that are enabled along with their optional
settings are used to build the complete settings for revive in golangci-lint’s configuration.
📒 The function BUILD_REVIVE_SETTINGS is defined in the Apps Script project
linked to the sheet.
sections
This has the static header and footer for the generation of golangci-lint configuration.
.golangci.toml, .golangci-prod.toml
This has the configuration for golangci-lint generated by stitching together the following:
- Header from the
sectionssheet. - Linters enabled for 'local' / 'prod' in
available-linterssheet - Linter settings for enabled linters in
available-linterssheet - Footer from the
sectionssheet.
The configuration is updated in a few seconds whenever any change is made in the sheet by the Apps Script project linked to the sheet.
⚠️ When the configuration is copied from the sheet, it is wrapped in double quotes. So we need to
replace "" with " and remove the quotes at the beginning and the end.
Why TOML?
YAML: probably not so great after all
Integrating golangci-lint into an existing project
When we are creating a new project, it is straightforward to integrate golangci-lint into it.
However, if we are integrating golangci-lint into a pre-existing project where either the linter
was not integrated or the configuration was minimal, we need a strategy to iteratively fix the large
number of issues that would be reported by the linter with the new/updated configuration. Fixing all
the issues in a single effort can be inhibitively expensive. golangci-lint provides a mechanism
for doing so by reporting issues only for new and modified lines in the commit.
See golangci-lint FAQ.
Project tooling
Most repositories utilise multiple tools for formatting, code/doc generation and testing. Some examples:
golangci-lint: github.com/golangci/golangci-lintgofumpt: github.com/mvdan/gofumptmockery: github.com/vektra/mockery
The version of these tools needs to be kept consistent between local and CI. Sometimes the version of these tools are different across projects and different developers might have different local versions of these tools.
We can leverage Go modules to manage the necessary tooling by doing the following:
- Add a
go.modfile undertoolsdirectory:go mod init github.com/netskope/spm-{{repo}}/tools - Add a
tools.gofile in the same folder with a build constraint so that it does not get included in the build://go:build tools package tools import ( _ "github.com/golangci/golangci-lint/cmd/golangci-lint" // add more imports as needed ) - Run
go mod tidyto sync thetools/go.modwith the tools that are declared intools.go.
Using the tools/go.mod, we can build the tool binaries. Task can be used for building the
binaries on demand and for managing the commonly used commands. Taskfile is a more modern
replacement for Makefiles and also has features for re-building the tools based on checksum of
module files. Taskfile example: Taskfile.yml
Developer workflow (with Task)
Installing task
To install task on macOS with Homebrew, following command needs to be executed:
brew install go-task
For other operating systems please refer task installation page.
Install tools locally
Users can install and cache tools locally for formatting, linter checks etc. by using the below command:
task install-tools
User can run task --list to list all configured commands for the current repository.
Formatting code
Formatting issues can be reported by github.com/mvdan/gofumpt
and github.com/daixiang0/gci. We just need to run task fmt to fix all formatting issues.
It is also possible to only fix the order of import statements by running task imports.
➜ task fmt
task: Task "install-gci" is up to date
task: Task "install-gofumpt" is up to date
task: [imports] .tools/gci write ./ --section standard --section default --section "Prefix(github.com/netskope,github.com/netSkope)" --skip-generated --skip-vendor
task: [fmt] .tools/gofumpt -l -w -extra .
By integrating the tools to the IDE, we can enable format on save so that files are always formatted correctly.
IDE configuration
Goland
- Install the Go Linter plugin for GoLand.
- Under Tools | Go Linter, For the "Path to golangci-lint", select the
golangci-lintbinary present in the.toolsdirectory inside the project (runtask install-toolsif binary is not present under.tools).
Go Linter should automatically pick .golangci.toml as the configuration file. It is recommended to
use this with the IDE plugin to avoid running some of the heavy linters like unused during
development. task lint can be used to lint the source files against .golangci-prod.toml.
- Open IDE preferences with
⌘+,. - Navigate to the 'File Watchers' settings: Tools | File Watchers.
- Click the 'Import' icon (
) and select the file
.idea/watchers-cfg.xml. To show hidden folders in the Finder app on macOS, press⌘+⇧+..
These file watchers will run gci and gofumpt for updating import lines and formatting the code
idiomatically on saving the file.
- Under Code Style | Go:
- Select the "Imports" tab and set "Sorting type" to "None".
- Under the "Other" tab:
- Deselect "On Reformat Code action"
- Select "Add a leading space to comments". under "Except for comments starting with:" add
nolintandgo:. - Under Tools | Actions on Save, deselect "Reformat Code".
False Positives
False positives are inevitable but golangci-lint provides flexible mechanisms for working around
false positives. We can either use //nolint directives to ignore a specific error or we can update
the .golangci-lint*.toml and add an exclude-rule.
See https://golangci-lint.run/usage/false-positives/ for more details.
When adding or updating exclude-rules, remember to update both .golangci.toml and
.golangci-prod.toml.
Directories
¶
| Path | Synopsis |
|---|---|
|
demo-code
|
|
|
printf
command
|
|
|
rowserrv1
command
|
|
|
rowserrv2
command
|
|
|
urlqueryv1
command
|
|
|
urlqueryv2
command
|
