Golang构建前如何列出并下载go generate的依赖项?
Golang构建前如何列出并下载go generate的依赖项? 你好!我在为一个网络隔离的容器构建预下载依赖项时遇到了一个可能比较奇怪的问题。
我想做的事情:
- 通过
go mod download将依赖项预下载到一个本地目录 - 在容器构建时将该目录挂载进去,并设置
GOMODCACHE指向这个目录 - 在没有网络访问的情况下构建容器
问题在于:构建过程的一部分是运行 go generate。这个命令想要下载一些额外的模块,而这些模块没有被 go mod download 下载。由于没有网络访问,这会导致失败。
go: downloading golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa
go: downloading github.com/olekukonko/tablewriter v0.0.5
/tmp/gocache/entgo.io/ent@v0.11.3/cmd/internal/printer/printer.go:16:2: github.com/olekukonko/tablewriter@v0.0.5: Get "https://proxy.golang.org/github.com/olekukonko/tablewriter/@v/v0.0.5.zip": dial tcp: lookup proxy.golang.org: i/o timeout
/tmp/gocache/entgo.io/ent@v0.11.3/entc/load/load.go:27:2: golang.org/x/tools@v0.1.13-0.20220804200503-81c7dc4e4efa: Get "https://proxy.golang.org/golang.org/x/tools/@v/v0.1.13-0.20220804200503-81c7dc4e4efa.zip": dial tcp: lookup proxy.golang.org: i/o timeout
/tmp/gocache/entgo.io/ent@v0.11.3/entc/gen/graph.go:25:2: golang.org/x/tools@v0.1.13-0.20220804200503-81c7dc4e4efa: Get "https://proxy.golang.org/golang.org/x/tools/@v/v0.1.13-0.20220804200503-81c7dc4e4efa.zip": dial tcp: lookup proxy.golang.org: i/o timeout
storage/ent/generate.go:3: running "go": exit status 1
复现步骤 (使用 go1.19.2):
准备缓存:
git clone https://github.com/dexidp/dex
cd dex
git checkout v2.35.1
env GOMODCACHE=$(realpath ../gocache) go mod download
cd ..
容器文件:
FROM golang:1.19.1-alpine3.16 AS builder
# alpine-sdk would be needed if we could get past the download step
# RUN apk add alpine-sdk
COPY ./dex /src/dex
WORKDIR /src/dex
ARG GOMODCACHE=/tmp/gocache
RUN export GOMODCACHE="$GOMODCACHE" && \
export GOOS=linux && \
go generate -mod=mod /src/dex/storage/ent/
构建:
sudo podman network create --internal internal
sudo podman build --network=internal -v $(realpath ./gocache):/tmp/gocache:Z --build-arg GOMODCACHE=/tmp/gocache .
可能的解决方案?
最终目标是使容器构建尽可能安全和可复现。实际上,这是一个构建系统的一部分,其工作流程大致如上所述:预下载依赖项,然后在网络隔离的情况下构建容器。
考虑到这一点,我找到了两个次优的解决方案:
- 在构建之前,除了运行
go mod download之外,再运行go generate - 使用
go mod download all代替(它会下载generate所需的依赖,原因不明)
这两个方案的问题在于:示例项目的 go.sum 文件不包含这些额外的依赖项。这意味着额外的下载内容没有得到验证。
go generate 的问题在于:主机系统不知道应该传递什么参数给 go generate。它也可能没有成功生成所需的各种非 Go 语言依赖项。
download all 的问题在于:它会下载更多的依赖项,其中大部分甚至对于 go generate 来说也是不需要的。
我是否遗漏了其他下载 go generate 依赖项的方法?理想的解决方案是让它们包含在项目的 go.mod 和 go.sum 中。然而,这与通常的 Go 开发工作流程不同。即使运行 go mod tidy 也会再次移除它们。
更多关于Golang构建前如何列出并下载go generate的依赖项?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang构建前如何列出并下载go generate的依赖项?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Go项目中,go generate可能依赖的模块不会自动添加到go.mod中,因为它们通常被视为构建时依赖而非运行时依赖。以下是几种解决方案:
1. 使用go mod download all下载所有依赖
这是最直接的方法,虽然会下载更多依赖,但能确保go generate所需模块被缓存:
# 下载所有依赖到指定缓存目录
GOMODCACHE=$(realpath ./gocache) go mod download all
2. 在主机上运行go generate触发依赖下载
在预下载阶段运行go generate,让Go工具自动下载所需依赖:
# 在主机上运行generate,触发依赖下载
cd dex
GOMODCACHE=$(realpath ../gocache) go generate ./...
3. 使用go list找出generate依赖
通过分析代码中的//go:generate指令,找出可能需要的工具:
// 列出所有go:generate指令使用的工具
grep -r "//go:generate" --include="*.go" . | \
grep -o "go run .*" | \
cut -d' ' -f3 | \
sort -u
然后手动下载这些工具模块:
# 示例:手动下载特定工具
GOMODCACHE=$(realpath ./gocache) go get golang.org/x/tools@v0.1.13-0.20220804200503-81c7dc4e4efa
GOMODCACHE=$(realpath ./gocache) go get github.com/olekukonko/tablewriter@v0.0.5
4. 创建预下载脚本
编写脚本自动处理generate依赖的预下载:
// pregenerate.go
package main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
)
func main() {
cacheDir := os.Getenv("GOMODCACHE")
if cacheDir == "" {
home, _ := os.UserHomeDir()
cacheDir = filepath.Join(home, "go", "pkg", "mod")
}
// 设置环境变量
os.Setenv("GOMODCACHE", cacheDir)
// 下载所有模块依赖
cmd := exec.Command("go", "mod", "download", "all")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = "."
if err := cmd.Run(); err != nil {
fmt.Printf("下载依赖失败: %v\n", err)
os.Exit(1)
}
// 尝试运行generate来触发工具下载
cmd = exec.Command("go", "generate", "./...")
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Dir = "."
// 即使generate失败,依赖可能已经下载
_ = cmd.Run()
fmt.Println("预下载完成")
}
5. 使用vendor模式
启用vendor模式可以确保所有依赖(包括generate需要的)都被包含:
# 启用vendor模式
go mod vendor
# 构建时使用vendor目录
go build -mod=vendor ./...
go generate -mod=vendor ./...
在Dockerfile中:
FROM golang:1.19.1-alpine3.16 AS builder
COPY ./dex /src/dex
WORKDIR /src/dex
# 复制vendor目录
COPY ./vendor /src/dex/vendor
# 使用vendor模式运行generate
RUN go generate -mod=vendor /src/dex/storage/ent/
6. 检查go.mod中的工具依赖
确保工具依赖在go.mod中被明确声明:
// 在go.mod中添加工具依赖
require (
golang.org/x/tools v0.1.13-0.20220804200503-81c7dc4e4efa
github.com/olekukonko/tablewriter v0.0.5
)
// 使用replace指令确保使用本地版本(如果需要)
replace golang.org/x/tools => ./local/tools
对于dex项目的具体解决方案,由于它使用了entgo,需要确保entgo的代码生成工具被正确下载。最可靠的方法是在预下载阶段运行go generate,即使生成的文件不会被使用,但依赖会被下载到缓存中。

