V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
liulaomo
V2EX  ›  Go 编程语言

忘掉 GOPATH,迎接 Go modules,进入 Go 项目依赖库版本管理新时代

  •  1
     
  •   liulaomo · 2019-05-22 21:06:53 +08:00 · 6103 次点击
    这是一个创建于 2020 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Go SDK 1.13 测试版马上就要发布了。从此版本开始,Go modules 依赖库版本管理特性将正式开始推荐使用。本文将解释一些和 Go modules 相关的一些命令和概念。

    Module (模块)定义为一些 Go 代码包的集合。通常体现为一个含有若干代码包的目录。每个模块可以发布一系列版本。版本号使用semver (语义化版本)表示。一个模块可能依赖于其它若干模块;准确说来,是依赖于其它若干模块的各自的某个具体版本。每个模块可以在自己的根目录下的go.mod文件中指定其所依赖的各个模块的具体版本。

    如果你所维护的某个模块尚未使用 Go modules 管理依赖,你可以打开一个命令行终端,进入此模块根目录,运行下面这条命令将此模块转换为一个使用 Go modules 管理依赖的模块。

    go mod init host.prefex/mypkg
    

    其中,host.prefex/mypkg为其它包引入此模块中的包时的引入路径的前缀。常常地,host.prefexgithub.com等源代码托管网站;mypkg常为 user/project 这种形式。当然,你也可以将你自己的域名 my.website 用做引入路径的前缀(host.prefex)。但是这时如果不能从引入路径my.website/mypkg 中判断出此代码包使用何种源代码版本管理工具(比如 git/hg/svn 等),则my.website网站必须响应 https://my.website/mypkg?go-get=1 HTTPS 请求( HTTP 也可以但不推荐),并在在 HTML 的返回体中的<head>部分包含一个<meta>标签来指名具体到哪里下载此模块。(本公众号将另开一篇文章详解自定义域名引入路径。)

    当 go.mod 文件已经创建出来之后,我们可以在此文件中手动指定此模块所要依赖的其它模块和这些以来模块的版本号(版本号必须制定但可以使用伪版本号,比如 <v1.12.3>=v1.5.6latest和分支名master等)。我们也可以运行 go build 和 go test 等命令来自动发现并且在go.mod文件中加入依赖模块和它们的具体版本。手动指定的伪模块版本号将被go build等命令更改为确切的版本号。其中

    • latest伪版本号将被解读为最新正式发布版本,正式发布版本是标号为形如vX.Y.Z的语义化版本( semver,X/Y/Z均为整数数字)。
    • 分支名伪版本号将被解读为指定分支的最新提交。
    • <v1.12.3 将被解读为v1.12系列版中最大的小于v1.12.3的版本。

    当使用源代码版本管理工具时,一个 tag 的名称将被视为一个版本号。形如 v1.2.3-pre1 的预发布版本不属于正式版本。(关于模块的版本匹配规则,本公众号将另发一篇文章详述。)

    go build等命令将下载并缓存尚未缓存的依赖模块的版本代码,并将各个模块(包括直接和间接依赖)的哈希存储在go.sum文件(和go.mod文件处于同一目录)中。

    在一个模块目录下运行 go get a.b.c/x/[email protected] 将在此模块的go.mod文件中加入一个依赖。

    默认情况下,go build等命令将访问 sumdb ( Checksum Database,默认值为 https://sum.golang.org/ )验证各个直接或者间接依赖模块的哈希值是否和各个go.sum中记录的哈希值相匹配。如果不匹配,很可能某些环节出了问题(比如下载的模块代码被人恶意更改了)。(关于 sumdb,本公众号将另发一篇文章详述。)

    一般说来,go.mod文件中只记录当前模块的直接依赖。每个依赖体现为一条require或者replace指令。比如

    module my.website/cmd/myprogram
    
    require github.com/boltdb/bolt v1.3.0
    ​
    replace my.website/mypkg github.com/myname/myproject v1.0.0
    

    其中的 replace 指令表示,当遇到引入路径前缀为 my.website/mypkg 的代码包时,真实的下载的代码包为处于路径 github.com/myname/myprojectv1.0.0版本的模块下的相应代码包。

    如果你的一个旧项目是使用其它流行第三方工具(比如 deps 和 glide 等)来管理包依赖的,则在此项目下运行 go mod init host.prefex/mypkg 命令将自动将此项目转换为一个使用 go modules 管理依赖的项目。

    如果你的一个新项目需要依赖于一个当前正使用其它流行第三方工具来管理包依赖的库,则请到此库的根目录下运行以下 go mod init a.b/c 命令(引入路径可任意),然后将生成的go.mod中的所有require指令复制到你的新项目下的go.mod文件中。(至少对于目前的 Go SDK 1.12 是如此,以后的 Go SDK 版本可能会对此过程进行改进。)

    一些其它的和 modules 相关的常用命令和命令选项:

    • go list -m all 列出所有的(包括直接的和间接的)将在 go build 中使用的各个模块和它们的具体版本号
    • go list -u -m all 列出所有的(包括直接和间接)使用的各个模块目前可用的小更新或者补丁版本号
    • go get -u or go get -u=patch 将目前所有的(直接和间接)依赖的模块的版本号更新到最新可用的小更新或者补丁版本号。
    • go mod tidygo.mod中删除目前已经不再使用的依赖模块和加入其它操作系统和架构所需的依赖
    • go mod vendor 将所有依赖放入当前模块下的vendor子目录中
    • go build -mod vendor 使用当前模块下的vendor子目录中的依赖代码(而不是缓存中依赖模块代码)来编译构建

    本文首发在微信Go 101公众号,欢迎各位转载本文。Go 101公众号将尽量在每个工作日发表一篇原创短文,有意关注者请扫描下面的二维码。

    image


    关于更多 Go 语言编程中的事实、细节和技巧,请访问《 Go 语言 101 》项目 https://github.com/golang101/golang101。

    21 条回复    2019-05-27 11:53:43 +08:00
    lhx2008
        1
    lhx2008  
       2019-05-22 21:12:14 +08:00
    还没搞明白怎么继续 import GOPATH 里面的包
    liulaomo
        2
    liulaomo  
    OP
       2019-05-22 21:34:40 +08:00
    @lhx2008 “ import GOPATH 里面的包” 和 “ import 模块中的包” 两者时不兼容的
    Leigg
        3
    Leigg  
       2019-05-22 21:58:37 +08:00 via iPhone
    一直关注,支持
    littlewing
        4
    littlewing  
       2019-05-22 22:37:11 +08:00 via iPhone
    看起来不错,找个时间研究下,gopath 实在太恶心了
    多谢分享
    notreami
        5
    notreami  
       2019-05-22 22:48:40 +08:00
    建议向 maven 学习,Go modules 依旧难用。。。
    whitehack
        6
    whitehack  
       2019-05-22 23:19:34 +08:00 via iPhone
    借楼安利一波 https://smallwhite.ml/pub/go/deploy-go-modules-proxy.html
    部署 go modules 代理
    insert000
        7
    insert000  
       2019-05-22 23:34:27 +08:00 via iPhone
    用了有一段时间了,依旧很难用,碰到有的包需要 1.12 版本,有的包开了代理也下载不了,下载也没个进度条,几乎每天都在以为卡死了,不停的 go mod tidy 很是影响心情
    liulaomo
        8
    liulaomo  
    OP
       2019-05-23 00:01:57 +08:00
    @insert000 @notreami 设置 GOPROXY 之后就和 maven 一样了。国内应该很快会出现一些 goproxy 服务。

    不过最好还是自行解决 https_proxy 代理问题为好。

    > 有的包开了代理也下载不了

    这个应该是代理的问题。

    另外,从 Go 1.13 开始,哈希检查也可能会对一些 go 命令的响应时间有所影响。希望 sum.golang.org 能够在国内访问,不然国内的 goproxy 服务没人敢用。我们可以将 GONOSUMDB 环境变量设为 off 来关闭哈希检查。
    lhx2008
        9
    lhx2008  
       2019-05-23 09:00:07 +08:00 via Android
    @liulaomo JFROG 有个 Go center 的代理,还凑合着用吧
    Aruforce
        10
    Aruforce  
       2019-05-23 09:54:50 +08:00
    蛋疼的一点是:
    在 GOPATH 里面使用 go mod,即使在 GoPath 里面有要使用的 pkg...
    在构建等等的时候 go 还是会去 web 上下载...
    也就是说完全不管当前目录下面有没有 相应的 PKG...
    也不管你有没有修改要使用的 pkg...老子就是要下载人家发布的包...
    超级蛋疼...
    感觉和 maven 相比差的不少...
    cissoid
        11
    cissoid  
       2019-05-23 10:16:51 +08:00
    摆脱 GOPATH 依赖是很好,但是当项目中既包括需要科学上网访问的包,又包括私有 repo 上的包时...go mod tidy 就变成很蛋疼的事情了.

    不走代理, 外网的包拉不下来. 走 GOPROXY 代理, 私有 repo 的包拉不下来. 最终只能通过自建 HTTP 分域名代理来解决...
    index90
        12
    index90  
       2019-05-23 10:29:20 +08:00
    我一直觉得 GOPATH 没有错,只是我们依然停留在有版本号的时代。
    leopku
        13
    leopku  
       2019-05-23 10:38:38 +08:00
    go modules 会出莫名其妙的问题
    还不如 dep 稳定好用
    liulaomo
        14
    liulaomo  
    OP
       2019-05-23 15:29:35 +08:00
    @cissoid

    > GOPROXY 代理, 私有 repo 的包拉不下来.

    设置 GONOPROXY=private.domain/* ?
    flxxy
        15
    flxxy  
       2019-05-23 15:36:20 +08:00
    11 开始用,不太重度的使用体验还不错,重度使用仁者见仁智者见智
    lepig
        16
    lepig  
       2019-05-23 15:38:43 +08:00
    有我 大 PHP 的 composer 好用 me 哈哈哈
    wonderingray
        17
    wonderingray  
       2019-05-23 16:06:46 +08:00
    @liulaomo 并不是的,实际上 GOSUMDB 本身(比如 sum.golang.org )也是可以被 GOPROXY 所代理的,而且由于 GOSUMDB 的设计所以即使代理了也是很安全的,你看 https://go.googlesource.com/proposal/+/master/design/25530-sumdb.md 就知道了,所以无论国内还是国外的代理都是可以放心使用的。推荐我在用的一个支持代理 GOSUMDB 的 GOPROXY,速度很快: https://github.com/goproxy/goproxy.cn

    ```
    export GOPROXY=https://goproxy.cn
    export GOSUMDB=https://sum.golang.org
    ```

    这样设置就安全了,放心使用吧。
    liulaomo
        18
    liulaomo  
    OP
       2019-05-24 06:14:12 +08:00
    @wonderingray

    > 实际上 GOSUMDB 本身(比如 sum.golang.org )也是可以被 GOPROXY 所代理的

    这个倒没注意。我再研究一下。
    liulaomo
        19
    liulaomo  
    OP
       2019-05-24 06:52:55 +08:00
    @wonderingray

    其实 sumbdb 只能放置一个 module 发布之后被修改,但是不能防止一个 module 含有恶意代码。所以只能一定程度上防止恶意行为。
    wonderingray
        20
    wonderingray  
       2019-05-24 12:10:03 +08:00
    @liulaomo 嗯,没有绝对地安全。不过能防止代理们篡改信息就是了,因为即使是发布后缓存签名也是需要 GOSUMDB 先回源拉去一次的,代理们还是没法儿做手脚。
    linxl
        21
    linxl  
       2019-05-27 11:53:43 +08:00
    正在学习使用, 之前碰到网络问题(但我看不出是网络造成的), 各种墙真的好蛋疼. 还有总算可 import 相对路径了.
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5632 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 07:02 · PVG 15:02 · LAX 23:02 · JFK 02:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.