为什么 GO111MODULE 无处不在,以及关于 Go Modules 的一切(随 Go 1.20 更新)

Q shen 2023-06-05 17:02:18

“我的Go+语言初体验” | 征文活动进行中......

您可能已经注意到GO111MODULE=on到处都在蓬勃发展。许多自述文件都有:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@latest
</code></span></span>

 

请注意,go get自 Go 1.17 以来已弃用安装二进制文件,并且在 Go 1.18 中将变得不可能。如果您使用的是 Go 1.16 或更高版本,则应改用:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@latest
</code></span></span>

 

在这篇简短的文章中,我将解释为什么GO111MODULE在 Go 1.11 中引入它,它的注意事项和在处理 Go 模块时需要了解的有趣信息。


表中的内容:


GOPATHGO111MODULE

首先,让我们谈谈 GOPATH。当 Go 在 2009 年首次推出时,它并没有附带包管理器。相反,go get将使用它们的导入路径获取所有源并将它们存储在$GOPATH/src. 没有版本控制,“master”分支代表包的稳定版本。

Go 模块(以前称为 vgo -- 版本化的 Go)是在 Go 1.11 中引入的。Go Modules 不是使用 GOPATH 来存储每个包的单个 git checkout,而是存储标记的版本并go.mod跟踪每个包的版本。

从那时起,“GOPATH 行为”和“Go 模块行为”之间的交互已成为 Go 最大的问题之一。一个环境变量造成了 95% 的痛苦:GO111MODULE.

环境GO111MODULE变量

GO111MODULEgo是一个环境变量,可以在用于更改 Go 导入包的方式时设置。第一个痛点是,根据 Go 版本的不同,它的语义会发生变化。

GO111MODULE可以通过两种不同的方式设置:

  • shell 中的环境变量,例如:export GO111MODULE=on.
  • go env只有使用才知道的“隐藏”全局配置go env -w GO111MODULE=on(仅自 Go 1.12 起可用)。

如果 Go 似乎按照您认为的方式运行,建议您检查过去是否使用go env -w GO111MODULE=on(或off)设置了全局配置:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">env </span>GO111MODULE
</code></span></span>

 

请注意,环境变量优先于使用存储的值go env -w GO111MODULE

要取消设置全局配置,您可以执行以下操作:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">env</span> <span style="color:var(--syntax-error-color)">-u</span> GO111MODULE
</code></span></span>

 

GO111MODULE使用 Go 1.11 和 1.12

  • GO111MODULE=on即使项目在您的 GOPATH 中,也会强制使用 Go 模块。需要go.mod工作。
  • GO111MODULE=off强制 Go 以 GOPATH 方式运行,即使在 GOPATH 之外。
  • GO111MODULE=auto是默认模式。在这种模式下,Go 将表现

    • 类似于GO111MODULE=on当你在外面时GOPATH
    • 类似于当GO111MODULE=off你在里面时,GOPATH即使 ago.mod存在。

每当您在 GOPATH 中并且想要执行需要 Go 模块的操作(例如,go get特定版本的二进制文件)时,您需要执行以下操作:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get github.com/golang/mock/tree/master/mockgen@v1.3.1
</code></span></span>

 

GO111MODULE使用 Go 1.13

使用 Go 1.13,GO111MODULE默认 ( auto) 更改:

  • 表现得像在 GOPATH 之外的任何地方GO111MODULE=on都有OR ,go.mod即使没有 go.mod因此,您可以使用 Go 1.13 将所有存储库保存在您的 GOPATH 中。
  • 表现得像GO111MODULE=off在没有go.mod.

GO111MODULE使用 Go 1.14

GO111MODULE变量具有与 Go 1.13 相同的行为。

请注意,与发生的行为无关的一些细微变化GO111MODULE

  • 自动拾取vendor/这有破坏 Gomock(问题)的趋势,它在 1.14 之前不知不觉地没有使用vendor/
  • cd && GO111MODULE=on go get当你不想弄乱你当前的项目时,你仍然需要使用它go.mod(那太烦人了)。

GO111MODULE使用 Go 1.15

GO111MODULEGo 1.15没有任何改变。

GO111MODULE使用 Go 1.16

从 Go 1.16 开始,默认行为是GO111MODULE=on,这意味着如果你想继续使用旧GOPATH方法,你将不得不强制 Go 不使用 Go Modules 功能:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">export </span><span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>off
</code></span></span>

 

Go 1.16 中最好的消息是我们终于得到了一个专门的命令来安装 Go 工具,而不是依赖于不断go get更新go.mod. 代替:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)"># Old way</span>
<span style="color:var(--syntax-error-color)">(</span><span style="color:var(--syntax-text-color)">cd</span> <span style="color:var(--syntax-error-color)">&&</span> go get golang.org/x/tools/gopls@latest<span style="color:var(--syntax-error-color)">)</span>
</code></span></span>

 

你现在可以运行:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@latest
</code></span></span>

 

需要注意的是, 的语义go install与 略有不同go get如Go
博客
中所述:

为了消除关于使用哪个版本的歧义,go.mod在使用此安装语法时,对程序文件中可能存在的指令有一些限制。特别是,替换和排除指令是不允许的,至少现在是这样。从长远来看,一旦新版本go install program@version在足够多的用例中运行良好,我们计划停止go get安装命令二进制文件。有关详细信息,请参阅问题 43684

例如,您不能(截至 2021 年 4 月)安装gopls的主版本:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>% go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@master
go: downloading golang.org/x/tools v0.1.1-0.20210316190639-9e9211a98eaa
go: downloading golang.org/x/tools/gopls v0.0.0-20210316190639-9e9211a98eaa
go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@master: golang.org/x/tools/gopls@v0.0.0-20210316190639-9e9211a98eaa
        The go.mod file <span style="color:var(--syntax-declaration-color)">for </span>the module providing named packages contains one or
        more replace directives. It must not contain directives that would cause
        it to be interpreted differently than <span style="color:var(--syntax-declaration-color)">if </span>it were the main module.
</code></span></span>

 

go.mod文件中的替换指令如下所示:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">replace</span> <span style="color:var(--syntax-text-color)">golang</span><span style="color:var(--syntax-error-color)">.</span><span style="color:var(--syntax-text-color)">org</span><span style="color:var(--syntax-error-color)">/</span><span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-error-color)">/</span><span style="color:var(--syntax-text-color)">tools</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-error-color)">../</span>
</code></span></span>

 

幸运的是,gopls 项目确保replace在每次发布之前删除该指令,这意味着您可以使用latest标签:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@latest
<span style="color:var(--syntax-comment-color)">#                                   ^^^^^^</span>
</code></span></span>

 

GO111MODULE使用 Go 1.17

Go 1.17 于 2021 年 8 月 16 日发布。至于 1.16,GO111MODULE=on是默认行为,GO111MODULE=auto相当于GO111MODULE=on. 如果你仍然想使用这种GOPATH方式,你将不得不强制 Go 不使用 Go Modules 特性GO111MODULE=off(参见关于 的部分direnv)。

可能影响您使用方式的三个重要变化GO111MODULE如下:

如果您使用 Git 获取模块,则可以更快地下载依赖项

如果您决定更新go您的行go.mod以利用新模块图形修剪,您go.mod将希望得到更新。第一次使用 时go build,您会看到以下错误:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go build ./...
go: updates to go.mod needed; to update it:
        go mod tidy
</code></span></span>

 

运行后go mod tidy,你会看到你的go.mod新块发生了很大变化require

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>diff --git a/go.mod b/go.mod
index 3af719e..26ed354 100644
<span style="color:var(--syntax-error-color)">--- a/go.mod
</span><span style="color:var(--syntax-name-color)">+++ b/go.mod
</span><span style="color:var(--syntax-text-color)">@@ -1,12 +1,12 @@</span>
   module github.com/maelvls/users-grpc

-go 1.12
<span style="color:var(--syntax-name-color)">+go 1.17
</span>
   require (
   github.com/MakeNowJust/heredoc/v2 v2.0.1
   github.com/fsnotify/fsnotify v1.4.9 // indirect
   github.com/golang/mock v1.4.4
<span style="color:var(--syntax-error-color)">-   github.com/golang/protobuf v1.4.3
</span><span style="color:var(--syntax-name-color)">+   github.com/golang/protobuf v1.5.2
</span>   github.com/grpc-ecosystem/go-grpc-middleware v1.0.0
   github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
   github.com/hashicorp/go-memdb v1.3.0
<span style="color:var(--syntax-text-color)">@@ -37,8 +37,25 @@</span> require (
   golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
   google.golang.org/genproto v0.0.0-20201116205149-79184cff4dfe // indirect
   google.golang.org/grpc v1.33.2
<span style="color:var(--syntax-error-color)">-   google.golang.org/protobuf v1.25.0
</span><span style="color:var(--syntax-name-color)">+   google.golang.org/protobuf v1.27.1
</span>   gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
   gopkg.in/ini.v1 v1.62.0 // indirect
   gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 // indirect
   )
<span style="color:var(--syntax-name-color)">+
+require (
+   github.com/beorn7/perks v1.0.0 // indirect
+   github.com/davecgh/go-spew v1.1.1 // indirect
+   github.com/hashicorp/go-immutable-radix v1.3.0 // indirect
+   github.com/hashicorp/golang-lru v0.5.4 // indirect
+   github.com/hashicorp/hcl v1.0.0 // indirect
+   github.com/inconshreveable/mousetrap v1.0.0 // indirect
+   github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
+   github.com/pmezard/go-difflib v1.0.0 // indirect
+   github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 // indirect
+   github.com/prometheus/common v0.4.0 // indirect
+   github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 // indirect
+   github.com/spf13/pflag v1.0.5 // indirect
+   github.com/subosito/gotenv v1.2.0 // indirect
+   gopkg.in/yaml.v2 v2.3.0 // indirect
+)
</span></code></span></span>

 

require块添加了更多// indirect行。多亏了这些新行,Go 命令的go get下载量减少了(该机制称为惰性模块加载)。以前,go get必须下载每个项目才能访问它们的go.mod文件,即使其中一些项目实际上并未被您的代码使用。GOPROXY这对于基于 git 的项目来说是个大问题,而对于使用该机制的人(即其他所有人)来说问题不大,因为go.mod在使用该机制时无需获取整个存储库即可获取GOPROXY

例如,使用该GOPROXY机制,使用标签 v1.4.0 的 cert-manager 项目,HTTP GET 调用的次数从 252 次减少到 169 次(减少 49% 的 HTTP 请求)。HTTP 调用的数量是使用 mitmproxy 计算的GOCACHE=off。虽然 HTTP 调用的次数减少了,但我没有注意到任何显着的时间减少。

最大的不同将发生在依赖 Git 下载存储库的人身上。每次 Go 需要读取一个go.mod,它需要下载整个 Git 存储库。我们可以使用以下命令强制 Go 克隆 Git 存储库GOPRIVATE=*

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)"># With Go 1.16:</span>
<span style="color:var(--syntax-text-color)">$ </span><span style="color:var(--syntax-text-color)">time </span><span style="color:var(--syntax-text-color)">GOPATH</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-string-color)">$(</span><span style="color:var(--syntax-text-color)">mktemp</span> <span style="color:var(--syntax-error-color)">-d</span><span style="color:var(--syntax-string-color)">)</span> <span style="color:var(--syntax-text-color)">GOPRIVATE</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-string-color)">'*'</span> go get ./...
161.65s user 34.07s system 38% cpu 8:23.58 total


<span style="color:var(--syntax-comment-color)"># With Go 1.17:</span>
<span style="color:var(--syntax-text-color)">$ </span><span style="color:var(--syntax-text-color)">time </span><span style="color:var(--syntax-text-color)">GOPATH</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-string-color)">$(</span><span style="color:var(--syntax-text-color)">mktemp</span> <span style="color:var(--syntax-error-color)">-d</span><span style="color:var(--syntax-string-color)">)</span> <span style="color:var(--syntax-text-color)">GOPRIVATE</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-string-color)">'*'</span> go get ./...
158.09s user 33.39s system 39% cpu 7:59.63 total
</code></span></span>

 

这快了 5%(使用 30 Mbit/s DSL 连接时减少了 24 秒)。我本来期待更好的结果,但这个数字将取决于go.mod你有!

安装二进制文件GO111MODULE=on go get已弃用

在 Go 1.17 中,使用go get安装二进制文件现在显示以下警告:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">$ GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@latest
go get: installing executables with <span style="color:var(--syntax-string-color)">'go get'</span> <span style="color:var(--syntax-declaration-color)">in </span>module mode is deprecated.
     Use <span style="color:var(--syntax-string-color)">'go install pkg@version'</span> instead.
     For more information, see https://golang.org/doc/go-get-install-deprecation
     or run <span style="color:var(--syntax-string-color)">'go help get'</span> or <span style="color:var(--syntax-string-color)">'go help install'</span><span style="color:var(--syntax-text-color)">.</span>
</code></span></span>

 

要解决此警告,您可以使用Go 1.16 中go install教授的处理方法:@version

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@latest
</code></span></span>

 

请注意,这@version意味着 Go 模块模式,这意味着GO111MODULE=on当您不在具有go.mod.

go run知道@version(终于!)

go run命令被教导如何处理@version!就像go install,它暗示着GO111MODULE=on。到目前为止,如果你想运行一次二进制文件,你有两种方法:

  • 如果您从已启用的项目中运行二进制文件go.mod,则可以指定要在您的项目中运行的二进制文件的版本go.mod,然后运行go run -mod=mod​​.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code># Before Go 1.17:
go get github.com/golang/mock/mockgen@v1.3.1
go run -mod=mod github.com/golang/mock/mockgen

# With Go 1.17:
go run github.com/golang/mock/mockgen@v1.3.1
</code></span></span>

 

//go:generate如果你用来生成模拟,这很好。例如,在项目users-grpc中,我能够替换我的//go:generate指令:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// Old way, before 1.17:</span>
<span style="color:var(--syntax-comment-color)">//go:generate go run -mod=mod github.com/golang/mock/mockgen -build_flags=-mod=mod -package mocks -destination ./mock_service.go -source=../  user.go</span>
<span style="color:var(--syntax-comment-color)">// New way, Go 1.17:</span>
<span style="color:var(--syntax-comment-color)">//go:generate go run github.com/golang/mock/mockgen@v1.4.4 -package mocks -destination ./mock_service.go -source=../user.go</span>
</code></span></span>

 

GO111MODULE使用 Go 1.18

如果您仍需要使用go get安装二进制文件,则需要设置GO111MODULE=off. 推荐的方法是改为使用go install。没有GO111MODULE=offgo get只会更新你的go.mod。否则,它不会做任何事情。网上所有的README都要更新;如果他们不这样做,人们会因为看不到正在安装的二进制文件而感到困惑。

GO111MODULE使用 Go 1.19

GO111MODULEGo 1.19没有任何改变。

GO111MODULE使用 Go 1.20

GO111MODULEGo 1.20没有任何改变。

为什么GO111MODULE到处都是?(转到 1.15 及以下版本)

现在我们知道这GO111MODULE对于 Go 1.15 及以下版本对于启用 Go 模块行为非常有用,答案是:那是因为GO111MODULE=on允许您选择一个版本。没有 Go Modules,go get从 master 获取最新的提交。使用 Go Modules,您可以根据 git 标签选择特定版本。

GO111MODULE=on在 Go 1.16 发布之前,当我想在最新版本和gopls(Go 语言服务器)的 HEAD 版本之间切换时,我经常使用:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)"># With Go 1.15.</span>
<span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@latest
<span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@master
<span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@v0.1
<span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@v0.1.8
<span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-string-color)">"on"</span> go get sigs.k8s.io/kind@v0.7.0
</code></span></span>

 

不过在 Go 1.16 中这变得容易多了。我可以做同样的事情:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)"># With Go 1.16.</span>
go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@latest
</code></span></span>

 

被静默更新的陷阱go.mod(Go 1.15及以下)

对于 Go 1.15 及以下版本,我们安装二进制文件的唯一选择是使用go get. 您可能在 README 中遇到过这种奇怪的单行代码:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-error-color)">(</span><span style="color:var(--syntax-text-color)">cd</span> <span style="color:var(--syntax-error-color)">&&</span> <span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get golang.org/x/tools/gopls@latest<span style="color:var(--syntax-error-color)">)</span>
</code></span></span>

 

注意:后缀@latest会使用gopls最新的git标签。请注意,-u不需要(这意味着“更新”),@v0.1.8因为这是一个“固定”版本,更新固定版本并没有真正意义。有趣的是,使用@v0.1,go get将获取该标签的最新补丁版本。

那么,为什么我们需要使用这个调用子 shell 并移动到您的 HOME 的人为命令?这是 Go 1.16 修复的另一个 Go 专制政治:在 Go 1.15 及以下版本中,默认情况下(你不能关闭它),如果你在一个有 的文件夹中,将用你刚刚安装的内容go.mod更新go getgo.mod。对于像goplskind这样的开发二进制文件,您绝对不希望它们出现在go.mod文件中!

因此,对于使用 Go 1.15 及以下版本的任何人来说,解决方法是提供一个单行代码,确保您不会在启用go.mod-enabled 的文件夹中:(cd && go get)正是这样做的。

幸运的是,在 Go 1.16 中,现在可以明确区分go get添加依赖项go.mod(如 npm install)和go install安装二进制文件而不会弄乱你的go.mod. 在 Go 1.16 中,上面的内容go get变成了:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">install </span>golang.org/x/tools/gopls@latest
</code></span></span>

 

陷阱-u@version_

我被这个困扰了很多次:使用时go get @latest(至少对于二进制文件),你应该避免使用,-u以便它使用go.sum. 否则,它会将所有依赖项更新到它们最新的次要修订版……并且由于大量项目选择在次要版本(例如 v0.2.0 到 v0.3.0)之间进行重大更改,因此使用很有可能会造成破坏-u

所以如果你看到这个:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)"># Both -u and @latest!</span>
<span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on go get <span style="color:var(--syntax-error-color)">-u</span> golang.org/x/tools/gopls@latest
</code></span></span>

 

然后你会立即意识到(1)它使用旧的 preGo .1.16 方式安装 Go 二进制文件,并且(2)你想使用在go.sumgo-getting 二进制文件时给出的记录版本!

Rebecca Stambler提醒我们不要-u与版本一起使用:

-u不应与@latest标签一起使用,因为它会给您不正确的依赖项版本。

但它有点隐藏在这个问题中......我猜它写在 Go 帮助的某个地方(顺便说一句,git help与和?@version_-u

使用 Go 模块时的注意事项

现在,让我们回顾一下我在使用 Go 模块时遇到的一些注意事项。

请记住,这go get也会更新您的go.mod

在 Go 1.16 发布之前,这是一个奇怪的事情go get:有时,它用于安装二进制文件或下载包。go.mod对于 Go 模块,如果你在一个go get带有go.mod.

幸运的是,在 Go 1.16 中,go install已经了解了后缀@version。使用go install foo@version,您的本地go.mod不会受到影响!在 Go 1.18 中,go get将不再安装二进制文件,只会用于向你的go.mod.

如果你想运行go testgo buildgo.mod更新,你可以使用标志-mod=readonly。例如:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>go <span style="color:var(--syntax-text-color)">test</span> <span style="color:var(--syntax-error-color)">-mod</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-text-color)">readonly</span> ./...
go build <span style="color:var(--syntax-error-color)">-mod</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-text-color)">readonly</span> ./...
</code></span></span>

 

Go Modules 的依赖来源在哪里

使用 Go Modules 时,使用的包go build存储在$GOPATH/pkg/mod. 当尝试检查 vim 或 VSCode 中的“导入”时,您可能最终会看到包的 GOPATH 版本,而不是编译期间使用的 pkg/mod 版本。

出现的第二个问题是当您想破解您的依赖项之一时,例如出于测试目的。

解决方案 1:使用go mod vendorgo build -mod=vendor。这将强制go使用 vendor/ 文件而不是使用那些$GOPATH/pkg/mod文件。此选项还解决了 vim 和 VSCode 无法打开包文件的正确版本的问题。

解决方案 2:在您的末尾添加“替换”行go.mod

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>replace github.com/maelvls/beers => ../beers
</code></span></span>

 

../beers我要检查和破解的依赖项的本地副本在哪里。

GO111MODULE在每个文件夹的基础上 设置direnv

在旧版本的 Go(1.15 及更低版本)上,从基于 GOPATH 的项目(主要使用 Dep)迁移到 Go Modules 时,我发现自己在两个不同的地方挣扎:GOPATH 内部和外部。所有 Go 模块都必须保存在 GOPATH 之外,这意味着我的项目位于不同的文件夹中。为了解决这个问题,我GO111MODULE广泛使用了。我会把我所有的项目都放在 GOPATH 中,对于支持 Go Modules 的项目,我会设置export GO111MODULE=on.

注意:由于 Go 1.16 中的默认行为现在是GO111MODULE=on,因此不再需要此技巧。

这是direnv派上用场的地方。Direnv 是一个用 Go 编写的轻量级命令,它会.envrc在您进入一个目录并且.envrc存在时加载一个文件。对于每个支持 Go Module 的项目,我都会有这个.envrc

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)"># .envrc</span>
<span style="color:var(--syntax-text-color)">export </span><span style="color:var(--syntax-text-color)">GO111MODULE</span><span style="color:var(--syntax-error-color)">=</span>on
<span style="color:var(--syntax-text-color)">export </span><span style="color:var(--syntax-text-color)">GOPRIVATE</span><span style="color:var(--syntax-error-color)">=</span>github.com/mycompany/<span style="color:var(--syntax-literal-color)">\*</span>
<span style="color:var(--syntax-text-color)">export </span><span style="color:var(--syntax-text-color)">GOFLAGS</span><span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-error-color)">-mod</span><span style="color:var(--syntax-error-color)">=</span>vendor
</code></span></span>

 

环境GOPRIVATE变量禁用某些导入路径的 Go 代理(在 Go 1.13 中引入)。我还发现设置有用,-mod=vendor以便每个命令都使用vendor文件夹 ( go mod vendor)。

私有 Go 模块和 Dockerfile

许多公司选择使用私有存储库作为导入路径。如上所述,我们可以使用GOPRIVATE命令告诉 Go(从 Go 1.13 开始)跳过包代理并直接从 Github 获取我们的私有包。

但是如何构建 Docker 镜像呢?如何go get从 docker 构建中获取我们的私有存储库?

解决方案 1:销售

使用go mod vendor,无需将 Github 凭据传递给 docker 构建上下文。我们可以把所有东西都放进去vendor/,问题就解决了。在 Dockerfile 中,-mod=vendor将是必需的,但开发人员甚至不必费心,-mod=vendor因为他们无论如何都可以使用本地 Git 配置访问私有 Github 存储库

  • 优点:在 CI 上更快地构建(减少约 10 到 30 秒)
  • 缺点:PR 因vendor/更改而臃肿,回购的大小可能很大

解决方案 2:无销售

如果vendor/太大(例如,对于 Kubernetes 控制器,vendor/大约 30MB),我们可以很好地完成它而无需 vendoring。这将需要传递某种形式的 GITHUB_TOKEN 作为参数docker build,并在 Dockerfile 中设置如下内容:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code>git config <span style="color:var(--syntax-error-color)">--global</span> url.<span style="color:var(--syntax-string-color)">"https://foo:</span><span style="color:var(--syntax-declaration-color)">${</span><span style="color:var(--syntax-text-color)">GITHUB_TOKEN</span><span style="color:var(--syntax-declaration-color)">}</span><span style="color:var(--syntax-string-color)">@github.com/company"</span>.insteadOf <span style="color:var(--syntax-string-color)">"https://github.com/company"</span>
<span style="color:var(--syntax-text-color)">export </span><span style="color:var(--syntax-text-color)">GOPRIVATE</span><span style="color:var(--syntax-error-color)">=</span>github.com/company/<span style="color:var(--syntax-literal-color)">\*</span>
</code></span></span>

 

  • 2020 年 6 月 22 日更新:它说的use replace不仅仅是replace.
  • 2021 年 4 月 8 日更新:使用 Go 1.16 进行更新。
  • 2021 年 9 月 20 日更新:使用 Go 1.17 进行更新。
  • 2023 年 2 月 18 日更新:提及Josh Sorefgo env GO111MODULE报告的问题。
...全文
470 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

1,071

社区成员

发帖
与我相关
我的任务
社区描述
Go+ 官方开发者社区。我们希望向广大的开发者和数据科学家介绍 Go+ 的定位和意义,并邀请更多开发者一起贡献代码、共建 Go+ 生态。 Go+ 官网:https://goplus.org/
其他 企业社区
社区管理员
  • Go+
  • 杨东杰
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

本社区为 Go+ 官方开发者社区。我们希望向广大的开发者和数据科学家介绍 Go+ 的定位和意义,并邀请更多开发者一起贡献代码、共建 Go+ 生态。

Go+ 官网:https://goplus.org/
GitHub地址:https://github.com/goplus/gop

试试用AI创作助手写篇文章吧