应直接使用官方 Action golangci/golangci-lint-action@v4,它预编译、版本锁定、自动缓存且适配 ubuntu-latest;务必先执行 actions/checkout@v4,并配合 actions/setup-go@v4 指定语义化 Go 版本(如 '1.21.x'),配置 .golangci.yml 在根目录,禁用 --fix,启用 out-format: github-actions 实现 PR 行级标注,缓存 key 应基于 **/go.sum,PR 场景推荐 --new-from-rev=origin/${{ github.base_ref }} 仅检查变更文件,exit code 由 YAML 中 issues.severity 控制门禁级别。

直接用官方 Action,别自己装二进制
GitHub Actions 中最稳妥、最省心的方式是直接使用 golangci/golangci-lint-action@v4,而不是在 workflow 里手动 curl 安装或 go install。自己装容易因权限、路径、Go 版本或缓存失效导致失败,而官方 Action 已预编译、版本锁定、自动缓存二进制,且默认适配 ubuntu-latest runner。
常见错误现象:command not found: golangci-lint 或 failed to load config: cannot find .golangci.yml,多数是因为没正确设置工作目录或跳过了 checkout 步骤。
- 必须在
golangci-lint-action前执行actions/checkout@v4,否则找不到配置文件和源码 - 如果项目不在仓库根目录(比如在
src/子目录),需显式设置working-directory: src - 不推荐用
latest版本;固定写v1.54.2(当前稳定版)避免 CI 突然行为变更 - 默认超时是 2 分钟,大型项目建议加
args: --timeout=5m
配置要匹配本地开发环境
CI 报错但本地 golangci-lint run 没问题?大概率是 Go 版本或配置差异。GitHub Actions 的 Ubuntu runner 默认不带 Go,必须显式安装,且版本要和团队开发环境一致。
关键点:
- 用
actions/setup-go@v4安装 Go,并指定语义化版本如go-version: '1.21.x'(不是1.21.0),避免小版本升级导致 workflow 失败 -
.golangci.yml必须提交到仓库根目录;若用了skip-dirs排除vendor/或mocks/,确保 CI 和本地路径结构一致 - 如果本地用
--fix自动修复,CI 中**不要加该参数**——它只改文件不提交,CI 运行完就丢弃,起不到作用,还可能掩盖真实问题 - 启用
out-format: github-actions(Action 默认已设),让问题直接标在 PR 的代码行上,而不是藏在日志里
缓存和性能陷阱:go.sum 是 key,不是 go.mod
频繁重装 golangci-lint 或重复下载依赖会拖慢 CI。Action 内部已做二进制缓存,但你仍需注意 Go modules 缓存的写法,否则每次都要 go mod download。
错误示范:key: ${{ hashFiles('go.mod') }} —— go.mod 不变但 go.sum 可能变,缓存就会命中错误版本,导致 lint 结果不一致甚至失败。
- 正确缓存 key 应为:
${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - 路径必须写成
~/go/pkg/mod,不能用${{ env.HOME }}/go/pkg/mod(某些 runner 下env.HOME为空) - 多个模块(如
cmd/和internal/各有go.sum)时,**/go.sum能覆盖全部 - 若项目禁用 module(极少见),则跳过此缓存,但应尽快迁移
PR 场景下只检查变更文件,别全量跑
在 pull_request 触发时,全量运行 golangci-lint 效率低、反馈慢。官方 Action 支持只检查 PR 中修改的 Go 文件,前提是正确传递 files 上下文。
实操建议:
- 在 job 的
steps中,golangci-lint-action之前加一步提取变更文件:git diff --name-only ${{ github.event.before }} ${{ github.event.after }} -- '*.go',再通过args传入 - 更简单的方式:用 Action 的内置逻辑,设置
args: --new-from-rev=origin/${{ github.base_ref }}(适用于基于main的 PR) - 注意:这个选项要求 base 分支(如
main)必须存在且可访问;若用 fork PR,需开启permissions: contents: read - 避免在
push到main时也用此参数——它会对比空提交,结果异常
最易被忽略的一点:golangci-lint 的 exit code 是质量门禁的关键。只要有一条 error 级别问题,它就返回非零码,CI 自动失败。但 warning 不会中断流程——如果你希望 warning 也阻断合并,得在 .golangci.yml 里设 issues: severity: error 或用 exclude-rules 升级特定规则。这层控制不在 Action 配置里,而在 YAML 文件中。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











