golangci-lint: Агрегатор линтеров для Go
1. Что это и зачем нужно
golangci-lint — это мощный агрегатор линтеров для языка Go, который объединяет десятки популярных инструментов статического анализа кода в единый, быстрый и настраиваемый интерфейс. Вместо того чтобы устанавливать и запускать каждый линтер по отдельности (например, vet, staticcheck, errcheck, gocyclo и десятки других), разработчик получает одну команду, которая запускает их все параллельно, кэширует результаты и выдает единый отчет. Это экономит часы настройки CI/CD и локальной разработки.
Зачем это нужно? В больших проектах на Go (микросервисы, CLI-утилиты, веб-серверы) поддержание качества кода вручную невозможно. Ошибки вроде необработанных ошибок, неиспользуемых переменных, неправильного форматирования или потенциальных deadlock'ов могут привести к багам в production. golangci-lint автоматически находит такие проблемы на этапе code review или pre-commit. Он также помогает соблюдать единый стиль кода в команде, что критически важно для читаемости и поддержки.
Ключевые преимущества перед запуском линтеров по отдельности: скорость (линтеры запускаются параллельно, результаты кэшируются), гибкость (можно включить/отключить любой линтер, настроить его правила через YAML-конфиг), интеграция (работает с IDE, CI/CD системами, поддерживает go vet из коробки) и единый формат вывода (JSON, SARIF, цветной текстовый — для любого инструмента).
2. Установка
Установка golangci-lint выполняется несколькими способами. Рекомендуемый способ — через go install (требуется Go 1.18+):
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
После установки бинарник будет доступен в $GOPATH/bin (обычно ~/go/bin). Проверьте установку:
golangci-lint --version
Альтернативные способы (для CI/CD или если нет Go):
- Linux/macOS (curl):
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2 - macOS (Homebrew):
brew install golangci-lint - Docker:
docker run --rm -v $(pwd):/app -w /app golangci/golangci-lint:v1.55.2 golangci-lint run -v
3. Быстрый старт — минимальный рабочий пример
Создайте простой Go-модуль и файл с намеренной ошибкой:
mkdir myproject && cd myproject
go mod init myproject
Создайте файл main.go:
package main
import "fmt"
func main() {
x := 10
fmt.Println("Hello")
// Ошибка: неиспользуемая переменная x
}
Запустите линтер:
golangci-lint run
Вывод будет примерно таким:
main.go:6:2: `x` is unused (deadcode)
x := 10
^
Линтер нашел неиспользуемую переменную. Исправьте код (удалите строку x := 10) и запустите снова — ошибок не будет.
Для более строгой проверки используйте флаг -v (verbose) и --fast (только быстрые линтеры):
golangci-lint run -v --fast
4. Основные методы/функции/команды
В отличие от библиотеки, golangci-lint — это CLI-инструмент. Его "методы" — это команды и флаги. Рассмотрим 8 ключевых команд и опций.
4.1. run — основной запуск линтеров
Сигнатура: golangci-lint run [флаги] [путь]
Что делает: Запускает все активные линтеры для указанной директории (по умолчанию — текущая). Выводит найденные проблемы.
Рабочий пример:
# Запустить линтеры для всего проекта, игнорируя vendor
golangci-lint run ./...
# Запустить только для пакета internal/utils
golangci-lint run ./internal/utils/...
4.2. linters — список доступных линтеров
Сигнатура: golangci-lint linters
Что делает: Выводит полный список всех линтеров, которые можно использовать, с их статусом (включен/выключен по умолчанию) и кратким описанием.
Рабочий пример:
golangci-lint linters
В выводе вы увидите что-то вроде:
Enabled by default:
errcheck: Errcheck is a program for checking for unchecked errors in go programs.
gosimple: Linter for Go source code that specializes in simplifying code...
govet: Vet examines Go source code and reports suspicious constructs...
ineffassign: Detects when assignments to existing variables are not used.
staticcheck: Staticcheck is a go vet on steroids...
unused: Checks Go code for unused constants, variables, functions and types.
Disabled by default (enable with --enable):
bodyclose: checks whether HTTP response body is closed successfully...
dupl: Tool for code clone detection...
funlen: Tool for detection of long functions...
...
4.3. --enable и --disable — управление линтерами
Сигнатура: golangci-lint run --enable=линтер1,линтер2 --disable=линтер3
Что делает: Включает или отключает конкретные линтеры для текущего запуска. Полезно для точечной проверки.
Рабочий пример:
# Включить линтер bodyclose (проверка закрытия HTTP-тел) и отключить unused
golangci-lint run --enable=bodyclose --disable=unused ./...
4.4. --config — использование конфигурационного файла
Сигнатура: golangci-lint run --config=путь/к/файлу.yml
Что делает: Загружает настройки из YAML-файла. Позволяет гибко настроить линтеры, исключить папки, задать пороги сложности и т.д.
Рабочий пример:
Создайте файл .golangci.yml в корне проекта:
linters:
enable:
- errcheck
- gosimple
- govet
- staticcheck
- bodyclose
- dupl
disable:
- unused
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 0
run:
timeout: 5m
skip-dirs:
- vendor
- third_party
Запуск:
golangci-lint run --config=.golangci.yml
4.5. --out-format — формат вывода
Сигнатура: golangci-lint run --out-format=json|line-number|colored-line-number|tab|checkstyle|code-climate|junit-xml|sarif
Что делает: Определяет формат отчета. JSON удобен для автоматической обработки, SARIF — для интеграции с GitHub Code Scanning, colored-line-number — для чтения человеком.
Рабочий пример:
# Вывод в JSON для дальнейшего парсинга
golangci-lint run --out-format=json ./... > report.json
# Вывод в формате SARIF для GitHub
golangci-lint run --out-format=sarif ./... > report.sarif
4.6. --print-issued-lines — показывать строки с ошибками
Сигнатура: golangci-lint run --print-issued-lines=true
Что делает: Включает или отключает вывод строк кода, в которых найдена проблема. По умолчанию включено. Полезно отключить для кратких отчетов.
Рабочий пример:
# Отключить показ строк кода (только файл, строка, сообщение)
golangci-lint run --print-issued-lines=false
4.7. --fast — только быстрые линтеры
Сигнатура: golangci-lint run --fast
Что делает: Запускает только линтеры, которые не требуют полного построения AST (Abstract Syntax Tree) и выполняются быстро. Полезно для pre-commit хуков, когда скорость критична.
Рабочий пример:
# Быстрая проверка перед коммитом
golangci-lint run --fast ./...
4.8. cache — управление кэшем
Сигнатура: golangci-lint cache status | clean
Что делает: status показывает размер и расположение кэша линтера. clean очищает кэш (полезно, если линтер ведет себя странно после обновления).
Рабочий пример:
# Посмотреть статус кэша
golangci-lint cache status
# Очистить кэш
golangci-lint cache clean
5. Реальный практический пример (100-200 строк кода)
Создадим небольшой HTTP-сервер с намеренными ошибками и покажем, как golangci-lint их находит.
Структура проекта:
myapp/
├── main.go
├── handler.go
├── .golangci.yml
└── go.mod
Файл main.go:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", homeHandler)
http.HandleFunc("/api", apiHandler)
fmt.Println("Server starting on :8080")
// Ошибка: не обрабатываем ошибку ListenAndServe
http.ListenAndServe(":8080", nil)
}
func homeHandler(w http.ResponseWriter, r *http.Request) {
// Ошибка: не закрываем тело запроса
r.Body.Read([]byte{})
fmt.Fprintf(w, "Home Page")
}
func apiHandler(w http.ResponseWriter, r *http.Request) {
// Ошибка: неиспользуемая переменная
data := r.URL.Query().Get("data")
// Ошибка: не обрабатываем ошибку
w.Write([]byte("API response"))
// Ошибка: неиспользуемая переменная
_ = data
}
Файл handler.go:
package main
import (
"io"
"net/http"
)
// Ошибка: функция слишком сложная (высокая цикломатическая сложность)
func complexHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
if r.URL.Path == "/a" {
w.Write([]byte("a"))
} else if r.URL.Path == "/b" {
w.Write([]byte("b"))
} else if r.URL.Path == "/c" {
w.Write([]byte("c"))
} else {
w.WriteHeader(http.StatusNotFound)
}
} else if r.Method == "POST" {
body, _ := io.ReadAll(r.Body)
// Ошибка: не закрываем тело после чтения
w.Write(body)
}
}
Файл .golangci.yml:
linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- bodyclose
- gocyclo
- dupl
issues:
max-issues-per-linter: 0
max-same-issues: 0
run:
timeout: 3m
Запуск линтера:
golangci-lint run --config=.golangci.yml ./...
Вывод (сокращенный):
main.go:14:14: Error return value of `http.ListenAndServe` is not checked (errcheck)
http.ListenAndServe(":8080", nil)
^
main.go:19:5: bodyclose: response body must be closed (bodyclose)
r.Body.Read([]byte{})
^
main.go:26:7: `data` is unused (unused)
data := r.URL.Query().Get("data")
^
main.go:28:5: Error return value of `w.Write` is not checked (errcheck)
w.Write([]byte("API response")) Также в библиотеке
Email отправка на PHP: PHPMailer, SwiftMailer и mail() — полное руководство
Работа с CSV в TypeScript: библиотеки и примеры кода
Работа с CSV в TypeScript: библиотеки, парсинг и генерация данных
Wolverine для C# (.NET): полное руководство с примерами