该做,但优先级低于模块缓存;Go构建缓存默认启用,复用.a等中间产物加速build/test,需配合GOMODCACHE才能避免重复下载和编译,缓存key应基于go.sum确保依赖一致性。

Go构建产物缓存该不该做?
该做,但优先级低于模块缓存。Go的go build产物本身体积小、生成快,缓存它带来的收益远不如缓存~/go/pkg/mod或~/.cache/go-build。真正值得缓存的是编译中间产物(如.a文件)和测试缓存,它们能跳过重复编译和测试执行——尤其在频繁运行go test -race或跨平台构建时。
缓存~/.cache/go-build必须注意的三件事
~/.cache/go-build是go build默认使用的构建缓存目录,启用后可复用已编译的包对象。但它不是开箱即用的:
- 必须显式启用:
GOBUILD_CACHE=1环境变量不起作用;实际依赖GOCACHE环境变量,默认就是~/.cache/go-build - 缓存键基于源码哈希和编译器版本,
go version变更会自动失效旧缓存,无需手动处理 - 不能只缓存
GOCACHE路径而忽略GOMODCACHE:两者缺一不可,否则go build仍会重新下载并编译依赖模块
推荐配置:
- name: Cache Go build cache
uses: actions/cache@v4
with:
path: ~/.cache/go-build
key: ${{ runner.os }}-go-build-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-build-
actions/setup-go@v5的cache: true到底缓了什么?
这个开关不是“一键全缓存”,它只自动缓存两个目录:
立即学习“go语言免费学习笔记(深入)”;
-
${{ env.GOPATH }}/pkg/mod(模块下载内容) -
${{ env.GOCACHE }}(构建对象缓存,即~/.cache/go-build)
但它不缓存:
- 自定义
GOBIN下的二进制(如golangci-lint),需单独配actions/cache - 测试覆盖数据(
go test -coverprofile生成的文件),这类文件应写入工作目录再上传为workflow artifact -
go generate产出物,因为动作本身不感知//go:generate指令
所以即使开了cache: true,仍建议补一手golangci-lint缓存:
- name: Cache golangci-lint
uses: actions/cache@v4
with:
path: ~/.cache/golangci-lint
key: ${{ runner.os }}-golangci-lint-${{ hashFiles('**/go.sum') }}
为什么hashFiles('**/go.sum')比hashFiles('**/go.mod')更关键?
go.sum记录所有直接+间接依赖的精确校验和,而go.mod只声明直接依赖。当一个间接依赖(比如golang.org/x/net被grpc-go引入)更新时,go.mod可能完全不变,但go.sum一定变——这意味着缓存key必须反映这一变化,否则会复用错误的模块版本。
常见错误配置:
# ❌ 错误:仅靠 go.mod 无法捕获间接依赖变更
key: go-mod-${{ hashFiles('**/go.mod') }}
<h1>✅ 正确:以 go.sum 为主,go.mod 为 fallback</h1><p>key: go-mod-${{ hashFiles('<strong>/go.sum') }}
restore-keys: |
go-mod-${{ hashFiles('</strong>/go.sum') }}
go-mod-${{ hashFiles('**/go.mod') }}</p>如果项目还没生成go.sum(比如新初始化),fallback机制能避免整个缓存跳过,但这种情况极少——go mod tidy或go build第一次运行就会生成它。


















