必须统一goproxy并配置goprivate,否则同一go.mod在不同机器上因代理差异导致校验和不匹配、go mod verify失败;direct需保留在goproxy末尾以支持私有模块,goprivate须设为import path前缀(如git.company.com);ci中须清除replace并禁用本地缓存以确保构建可重现。

为什么 GOPROXY 必须统一配置
团队里有人用 https://proxy.golang.org,有人用 https://goproxy.cn,还有人设成 direct,结果就是:同一份 go.mod 在不同机器上 go mod download 出来的依赖内容可能不一致——不是版本不同,而是校验和可能不匹配,go mod verify 失败,CI 构建直接中断。这不是“能跑就行”的问题,而是 go.sum 完整性校验被绕过的风险点。
强制统一代理的实操方式
靠口头约定或文档提醒没用,必须让配置成为构建前提。核心是两层控制:
- 在项目根目录放一个
.envrc(配合 direnv)或setup.sh,里面硬编码:export GOPROXY=https://goproxy.cn,direct - 在
Makefile或 CI 脚本开头加检查逻辑:test "$GOPROXY" = "https://goproxy.cn,direct" || (echo "GOPROXY mismatch!" && exit 1) - 把
GOPROXY写进项目 README 的「开发前必读」区块,并附一键设置命令:echo 'export GOPROXY=https://goproxy.cn,direct' >> ~/.zshrc && source ~/.zshrc
注意:direct 必须保留在代理链末尾,否则私有模块(如 git.company.com/internal/pkg)会因被代理拦截而拉取失败。
GOPRIVATE 配合使用才真正闭环
只设 GOPROXY 不设 GOPRIVATE,私有模块仍可能被公共代理拒绝或返回 404,导致 go build 卡在 “no matching versions” 错误里。
-
GOPRIVATE值必须是 import path 的前缀,不是域名,比如git.company.com,而不是https://git.company.com - 多个私有域用逗号分隔:
export GOPRIVATE=git.company.com,github.com/internal-team - 如果私有模块路径含子路径(如
git.company.com/foo/bar),只需写git.company.com即可覆盖全部
漏配 GOPRIVATE 是团队协作中最常踩的坑——现象是本地能 go build,CI 报错找不到包,根源就在代理链没绕过私有域。
CI 中清除 replace 才算真正可靠
replace 是开发期便利手段,但若 CI 环境也执行了 go mod tidy,它会把 replace 写进 go.sum,导致构建产物实际依赖本地路径,完全不可重现。
- CI 脚本第一行必须运行:
go mod edit -dropreplace=github.com/user/pkg(逐个清除) - 更稳妥的做法:CI 使用干净容器,不复用本地
go.mod缓存,每次从头go mod download - 禁止将含
replace的go.mod提交到主干分支;PR 检查项应包含:grep -q "replace" go.mod && exit 1 || true
真正难的不是配置代理,而是让所有人理解:代理不是“加速开关”,而是构建一致性契约的一部分——少一个 direct,多一个未声明的 replace,都意味着你正在悄悄放弃可重现构建。











