Golang中如何将vendor模式转换为module模式

Golang中如何将vendor模式转换为module模式 大家好,

我是Go语言的新手。我克隆了一个Go代码库,他们使用vendor目录来存储依赖项。我在vendor文件夹中添加了一个自定义包,但是当我执行go build mod=vendor时,出现了cannot find module providing package <package which is present in vendor dir>的错误。

此外,手动将包从$GOPATH/pkg/mod复制到这个vendor目录非常麻烦,感觉永无止境。我该如何将这种"vendor"模式转换为模块模式,并包含我之前编写的自定义包?我最终该如何构建这个项目?

我知道这是一个基础问题,但Go语言的构建/打包机制确实令人困惑,我几乎要放弃了!请帮帮我! 谢谢!


更多关于Golang中如何将vendor模式转换为module模式的实战教程也可以访问 https://www.itying.com/category-94-b0.html

8 回复

感谢Holloway!!!你真是我的救星。

更多关于Golang中如何将vendor模式转换为module模式的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


请记住,在资源允许时进行长期迁移。请随时为最合适的回复标记"已解决"作为解决方案。

谢谢Halloway,但我还是不太明白。

invalid module path 这个错误是关于 replace 行的——而不是模块的名称。😕

我不确定是否应该将自定义模块推送到 GitHub 仓库?另外,在 Go 代码中,自定义私有模块的 import 语句应该怎么写?

请帮帮忙!能否写下实现这一点的步骤?哪怕是一个大致的提纲也会非常有帮助!

非常感谢!!

satishrao:

module /Users/sanupin/Downloads/admission-controller-webhook-demo-master

这不是一个有效的模块路径。有效的路径应该类似于:github.com/ABC/XYZ/admission-controller-webhook-demo-master。这本质上是 admission-controller-webhook-demo-masterimport 路径。

satishrao:

这里,couchbase/api/couchbase-cluster/v1 是唯一自定义、自己编写的包 - 它本身可能有一些在 GitHub 上可用的依赖项。但是当我运行

你需要进入 ./vendor/couchbase/api/couchbase-cluster/v1 并像上面那样初始化另一个 go.mod。是的,这是另一个模块。在将其推送到专用仓库之前,名称应为:couchbase/api/couchbase-cluster/v1

satishrao:

我知道这是个基础问题,但Go语言的构建/打包机制实在太令人困惑,我几乎要放弃了!请帮帮我!

欢迎来到Golang Bridge!🚀🎆

虽然这个问题并不简单,但请不要担心。

satishrao:

另外,手动将包从$GOPATH/pkg/mod复制到这个vendor目录实在太麻烦了。

如果你正在使用模块,就不应该再使用vendor了。这会让所有人都感到困惑。因此,我们的目标是在没有mod=vendor参数的情况下进行go build

satishrao:

我该如何将这个"vendor"模式转换为模块模式,并包含我之前编写的自定义包?我最终该如何构建这个项目?

根据紧急程度,我建议两种方法。


短期解决方案

vendor对整体仓库有一个主要影响需要考虑:它会影响源代码中的本地导入。例如:

  1. 与其像import "github.com/XYZ/abc"这样导入包,将该包放入vendor目录vendor/abc中,允许源代码像import "abc"这样导入包。

因此,我们需要解决这个问题。幸运的是,go.mod有一个replace子句,可以将源代码中的任何导入子句替换为本地目录。基于上面的例子:

replace (
	abc => ./vendor/abc
	...
)

一旦你相应地替换了所有vendor依赖项,请在没有mod=vendor参数的情况下再次测试运行。如果一切正常,你现在就可以继续了。


长期迁移方案

对于长期迁移,你需要将源代码中的import子句改回其原始仓库点。这将消除对vendor远程包的依赖。至于非远程的vendor包,你可以为其创建一个独立的模块仓库,并将其导入回项目中。

当我说长期时,意味着迁移需要时间,这取决于仓库的大小以及vendor导入语句在所有源代码中的分布范围。可以将其视为一次重构工作。

您好,

如果我的 go.mod 文件如下所示:

module /Users/sanupin/Downloads/admission-controller-webhook-demo-master
  
go 1.12

require (
        github.com/ghodss/yaml v1.0.0
        github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415
        github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b
        github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf
        github.com/json-iterator/go v1.1.5
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd
        github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742
        github.com/spf13/pflag v1.0.3
        golang.org/x/net v0.0.0-20180124060956-0ed95abb35c4
        golang.org/x/text v0.3.0
        gopkg.in/inf.v0 v0.9.0
        gopkg.in/yaml.v2 v2.2.2
        k8s.io/api v0.0.0-20180308224125-73d903622b73
        k8s.io/apimachinery v0.0.0-20180228050457-302974c03f7e
)

replace (
        couchbase/api/couchbase-cluster/v1 =&gt; ./vendor/couchbase/api/couchbase-cluster/v1
)

这里,couchbase/api/couchbase-cluster/v1 是唯一自定义、自己编写的包——它本身可能有一些在 GitHub 上可用的依赖项。但是当我运行:

bash-3.2$ GOOS=linux GOARCH=amd64 go build ./cmd/webhook-server/

go: errors parsing go.mod:

/Users/sanupin/Downloads/admission-controller-webhook-demo-master/go.mod:23: invalid module path

我的模块路径有什么问题导致它报错?

satishrao:

不是模块名称的问题

名称确实是个问题,因为你实际上是把模块锁定在了本地机器上。目前能正常工作主要得益于 git 版本控制系统的机制。

satishrao:

我不确定是否应该将自定义模块推送到 GitHub 仓库?另外,在 Go 代码中导入这个自定义私有模块的语句应该怎么写?

从长远来看,你应该把 ./vendor/couchbase/api/couchbase-cluster 推送到它自己专属的代码仓库。这是另一个模块,所以需要有自己的 go.mod 文件。

目前,我们先关注短期解决方案。

satishrao:

invalid module path 错误是关于 replace 语句的问题 - 不是模块名称的问题 😕

replace 路径是否正确?也就是说,这个包是在你的 {main repo}/vendor/couchbase/api/couchbase-cluster/v1 还是 {main repo}/vendor/couchbase/api/couchbase-cluster 目录下?

这个错误是因为 replace 指向了一个无效的相对路径。

satishrao:

你能写下完成这个任务的步骤吗?哪怕是个大致提纲也会非常有帮助!

迁移清单相当直接:

  1. [x] 使用版本控制系统(git commit)冻结版本
  2. [x] 初始化 go.mod
  3. [x] 检查所有 vendor 外部依赖项并将其添加到 require 子句中
  4. [ ] 替换所有 vendor 内部依赖项并将其添加到 replace 子句中
  5. [ ] 构建并测试结果,必须重现步骤 1 的结果
  6. [ ] 使用版本控制系统(git commit)冻结版本
  7. [ ] 将所有内部依赖项迁移到外部的独立模块中
  8. [ ] 修正所有 import 语句以使用步骤 7 中创建的外部模块
  9. [ ] 构建并测试结果,必须重现步骤 1 的结果
  10. [ ] 使用版本控制系统(git commit)冻结版本
  11. [ ] 移除 replace 子句
  12. [ ] 构建并测试结果,必须重现步骤 1 的结果
  13. [ ] 删除 vendor 目录
  14. [ ] 使用版本控制系统(git commit)冻结版本
  15. [ ] 用双层芝士汉堡 + 可乐犒劳自己 [结束]

步骤 6 之后是长期实施方案。你现在处于步骤 4。如果你想直接跳到长期方案,可以跳到步骤 7。

要解决这个问题,你需要将项目从vendor模式转换为Go模块模式。以下是具体步骤和示例代码:

  1. 初始化Go模块:在项目根目录运行以下命令,将项目转换为模块模式。假设你的模块路径是example.com/myproject

    go mod init example.com/myproject
    

    这会生成一个go.mod文件。

  2. 添加依赖项:运行以下命令来自动发现并添加依赖项到go.mod

    go mod tidy
    

    这会基于你的代码导入分析依赖,并下载缺失的模块到缓存中(默认在$GOPATH/pkg/mod)。

  3. 处理自定义包:如果你在vendor目录中添加了自定义包(例如mypackage),确保它位于项目内的一个子目录中(如./mypackage/),并在代码中正确导入。例如,如果你的自定义包在项目根目录下的mypackage文件夹中:

    • 在Go代码中导入它:
      import "example.com/myproject/mypackage"
      
    • 运行go mod tidy来验证依赖。
  4. 移除vendor目录(可选):模块模式默认使用模块缓存,因此你可以删除vendor目录以简化项目结构:

    rm -rf vendor
    

    如果你需要保留vendor目录用于离线构建,可以使用go mod vendor重新生成它:

    go mod vendor
    
  5. 构建项目:现在使用标准命令构建项目,无需-mod=vendor标志:

    go build
    

    或者,如果仍需使用vendor目录构建:

    go build -mod=vendor
    

完整示例过程:

  • 假设项目结构如下:
    myproject/
    ├── main.go
    └── vendor/
        └── mypackage/
            └── mypackage.go
    
  • main.go中导入自定义包:
    package main
    
    import "example.com/myproject/mypackage"
    
    func main() {
        mypackage.MyFunction()
    }
    
  • 执行命令:
    go mod init example.com/myproject
    go mod tidy
    go build
    

如果自定义包不在项目根目录下,确保其路径在模块中可访问。例如,如果自定义包在internal/mypackage,则导入应为import "example.com/myproject/internal/mypackage"

通过以上步骤,你的项目将成功转换为模块模式,并能够正确构建包含自定义包的代码。

回到顶部