go-linter-configuration
Configure and troubleshoot golangci-lint for Go projects. Includes complete .golangci.yml examples, import resolution fixes, CI optimization, and linter selection workflows.
Installation
# BAD: install via go get (deprecated)
go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
# GOOD: install latest with go install
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
# Or install via package manager
# macOS: brew install golangci-lint
# Windows: choco install golangci-lint
# Linux: snap install golangci-lint --classic
Verify installation:
golangci-lint --version
# golangci-lint has version 1.61.0 built with go1.23.4
Complete Configuration Examples
Minimal (CI with Import Issues)
When CI fails with "undefined: package" errors despite local builds working:
# .golangci.yml
run:
timeout: 5m
tests: false # Skip test files (often have complex imports)
build-tags: [] # No build tags
skip-dirs: # Skip generated/vendor code
- vendor
- third_party
- testdata
linters:
disable-all: true # Start from scratch
enable:
- gofmt # Only check formatting (no type-checking)
- goimports # Check imports
linters-settings:
gofmt:
simplify: true # Use gofmt -s
issues:
exclude-use-default: false
max-issues-per-linter: 0 # Report all issues
max-same-issues: 0 # No deduplication
output:
formats:
- format: colored-line-number
sort-results: true
Standard (Local Development)
# .golangci.yml
run:
timeout: 5m
tests: true
build-tags:
- integration
skip-dirs:
- vendor
- third_party
modules-download-mode: readonly # Don't modify go.mod
linters:
enable:
- gofmt # Format checking
- goimports # Import organization
- govet # Go vet built-in
- errcheck # Unchecked errors
- staticcheck # Static analysis
- unused # Unused code
- gosimple # Simplifications
- ineffassign # Ineffective assignments
- typecheck # Type errors
- misspell # Spelling
- gocyclo # Cyclomatic complexity
- dupl # Code duplication
- gosec # Security issues
linters-settings:
govet:
enable-all: true
disable:
- shadow # Too noisy for most projects
errcheck:
check-type-assertions: true
check-blank: true
staticcheck:
checks: ["all"]
gocyclo:
min-complexity: 15 # Flag functions with complexity > 15
dupl:
threshold: 100 # Tokens threshold for duplication
gosec:
excludes:
- G104 # Unhandled errors (covered by errcheck)
misspell:
locale: US
issues:
exclude-rules:
# Exclude linters for test files
- path: _test\.go
linters:
- gocyclo
- dupl
# Exclude known false positives
- text: "weak cryptographic primitive"
linters:
- gosec
path: test/
max-issues-per-linter: 50
max-same-issues: 3
output:
formats:
- format: colored-line-number
print-issued-lines: true
print-linter-name: true
sort-results: true
Production (Strict)
# .golangci.yml
run:
timeout: 10m
tests: true
build-tags:
- integration
- e2e
linters:
enable-all: true
disable:
# Disable overly opinionated/noisy linters
- exhaustruct # Requires all struct fields
- varnamelen # Variable name length
- tagliatelle # Struct tag format
- ireturn # Interface return types
- wrapcheck # Wrapping errors
- nlreturn # Newline before return
- wsl # Whitespace linter (too strict)
linters-settings:
govet:
enable-all: true
errcheck:
check-type-assertions: true
check-blank: true
gocyclo:
min-complexity: 10 # Stricter than default
gocognit:
min-complexity: 15
nestif:
min-complexity: 4
funlen:
lines: 100 # Maximum function length
statements: 50
cyclop:
max-complexity: 10
lll:
line-length: 120 # Maximum line length
revive:
rules:
- name: exported
arguments:
- disableStutteringCheck
- name: var-naming
- name: error-return
- name: error-naming
- name: if-return
- name: increment-decrement
issues:
exclude-rules:
- path: _test\.go
linters:
- funlen
- gocyclo
- dupl
- gomnd
- path: cmd/
linters:
- gochecknoglobals # Allow globals in main
- source: "^//go:generate "
linters:
- lll
max-issues-per-linter: 0 # Report all
max-same-issues: 0
Troubleshooting Workflow
Problem: "undefined: package" in CI
# Symptom
golangci-lint run ./...
# Error: could not import example.com/user/project/internal/db (undefined: sql)
# Diagnosis: Check if imports resolve
go list -m all
go mod tidy
# Solution 1: Ensure dependencies downloaded
go mod download
golangci-lint cache clean
golangci-lint run ./...
# Solution 2: Use minimal linter set (see config above)
# Create .golangci.yml with disable-all: true and only gofmt
# Solution 3: Skip type-checking linters in CI
cat > .golangci.ci.yml <<'EOF'
linters:
disable:
- typecheck
- unused
- staticcheck
EOF
golangci-lint run --config .golangci.ci.yml ./...
Problem: Too Slow in CI
# BAD: run on entire codebase every time
golangci-lint run ./... # 5 minutes
# GOOD: run only on new code
git fetch origin main
golangci-lint run --new-from-rev=origin/main ./... # 30 seconds
Problem: Too Many False Positives
# .golangci.yml
issues:
exclude-rules:
# Ignore "magic number" in test files
- path: _test\.go
linters:
- gomnd
# Ignore long lines in generated code
- path: \.pb\.go$
linters:
- lll
# Ignore specific error messages
- text: "G404: Use of weak random number generator"
linters:
- gosec
path: test/
exclude-files:
- ".*\\.pb\\.go$" # Protocol buffers
- ".*mock.*\\.go$" # Mocks
Problem: Different Results Locally vs CI
# Cause: Different golangci-lint versions
golangci-lint --version # Local: v1.59.1
# CI: v1.61.0 (different rules)
# Solution: Pin version in CI using the official GitHub Action
# .github/workflows/lint.yml
- name: Install golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.61.0
# Also pin in Makefile using go install
.PHONY: lint
lint:
@which golangci-lint > /dev/null || \
go install github.com/golangci/golangci-lint/cmd/[email protected]
golangci-lint run ./...
GitHub Actions Integration
# .github/workflows/lint.yml
name: Lint
on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
jobs:
golangci-lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.26'
cache: true
# Download dependencies first
- name: Download dependencies
run: go mod download
# Use official golangci-lint action
- name: golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.61.0
args: --timeout=10m --config=.golangci.yml
only-new-issues: true # Only flag new issues in PRs
Linter Selection Guide
# Check which linters are enabled
golangci-lint linters
# Run specific linter only
golangci-lint run --disable-all --enable=errcheck ./...
# Run all except specific linters
golangci-lint run --disable=typecheck,unused ./...
Recommended progressive adoption:
# Week 1: Start with basics
linters:
enable:
- gofmt
- goimports
- govet
# Week 2: Add error checking
linters:
enable:
- gofmt
- goimports
- govet
- errcheck
- staticcheck
# Week 3: Add code quality
linters:
enable:
- gofmt
- goimports
- govet
- errcheck
- staticcheck
- unused
- gosimple
- ineffassign
- gocyclo
# Month 2: Enable most linters, disable noisy ones
linters:
enable-all: true
disable:
- exhaustruct
- varnamelen
- wsl