golang 依赖管理:glide 从入门到放弃(转投 dep)

0x0 结论

先丢结论,两个推荐选项:

glide 声名在外,但太子 dep 出生以来不再活跃,bug 不少且修复不活跃,要改用网友 fork 的版本。如果不会搞 proxy,推荐这个

长远考虑,官方支持的 dep 更好 ,别的不说,光 prune 一个特性就值了。

折腾过程请看下文:


go 最近几年热了起来,我一个长期靠 Java 吃饭的人,也从 Python 吹,变成了 go 吹。

0x1 依赖管起来 glide init

一开始就知道 go 官方的依赖管理不给力。如果第三方依赖有 breaking changes,或者干脆删库走人,好好一个项目,换个没有依赖包缓存的机器,就编译不能了。1.5 开始引入了 vendor 目录,但毕竟没有默认启用,工具支持也不够(当时 dep 不知道是没发布,还是还在 alpha)。

当时看了网上的讨论,好像一致推崇 glide 。安装,玩一下就算了。当时写小工具,依赖很少,没把 glide 用起来。(多数是用 go 重写 Python 工具。把工具共享出去时别人没装 Python,懒得折腾打包。)

breaking changes

直到写稍大一点的项目,依赖多起来。但还是拖着,心想具体用哪个包可能还会变,不会刚写两天就来一个 breaking change。

写到稍有模样,碰到个 bug,最后发现是依赖包引起的,去主页一看,作者已经 fix 了,是个 breaking change,发了个大版本。我升级了,还好 breaking 的地方对我没有影响。但这样一来,就下决心把依赖管理起来。

碰『壁』

(这不是教学帖,不教安装使用。反正教程一搜一大把。)

1
2
3
4
5
6
7
8
9
10
glide init
...... // 省略 glide 问我的一堆问题
glide install
[INFO] Lock file (glide.lock) does not exist. Performing update.
[INFO] Downloading dependencies. Please wait...
...... // 省略那些成功的包
[INFO] --> Fetching golang.org/x/crypto/acme/autocert
[WARN] Unable to checkout golang.org/x/crypto/acme/autocert
[ERROR] Error looking for golang.org/x/crypto/acme/autocert: Cannot detect VCS
...... // 这种报错一个 4 个,都是 golang.org 上的包

出问题问谷歌,然后找到这么一个文章: https://studygolang.com/articles/10453 , 里面说是 golang.org 被墙了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
curl golang.org
curl: (7) Failed to connect to golang.org port 80: Timed out
curl https://golang.org
curl: (7) Failed to connect to golang.org port 443: Timed out
curl -x socks5://127.0.0.1:1080 https://golang.org/x/crypto/acme/autocert
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="go-import" content="golang.org/x/crypto git https://go.googlesource.com/crypto">
<meta name="go-source" content="golang.org/x/crypto https://github.com/golang/crypto/ https://github.com/golang/crypto/tree/master{/dir} https://github.com/golang/crypto/blob/master{/dir}/{file}#L{line}">
<meta http-equiv="refresh" content="0; url=https://godoc.org/golang.org/x/crypto/acme/autocert">
</head>
<body>
Nothing to see here; <a href="https://godoc.org/golang.org/x/crypto/acme/autocert">move along</a>.
</body>
</html>

确实被墙了。那平时上为什么毫无障碍的?这时才想起翻 pac 配置,发现 golang.org 赫然在列,说明被屏蔽不是一天两天,早早被加入了 gfwlist。(后来得知网站托管在 google 服务器上)

而且结合 golang.org/x/crypto 的重定向 和 meta 信息,可以看出来 golang.org 只是提供重定向信息,go getglide 应该是解析这些信息之后,再去 googlesource 或者 github 取代码。而目前第一步就 Time out 了,所以即使给 git 加 proxy 也没用。

0x2 配置镜像 glide mirror

mirror 的 N 种配置方法

那配置镜像吧。咦,不对,别人的例子都是 import 依赖包的根目录,例如 golang.org/x/crypto ,可以直接映射 github.com/golang/crypto ,但是子包 golang.org/x/crypto/acme/autocert 怎么办?

1
glide mirror set golang.org/x/crypto github.com/golang/crypto

照样 Cannot detect VCS 。 这个命令实际上写入了 ~/.glide/mirrors.yaml

1
2
3
repos: # 以这个包为例子,省略同理的其他包,下同
- original: golang.org/x/crypto
repo: github.com/golang/crypto

所以为了避免反复 glide mirror remove 再 set,我直接在 mirrors.yaml 上修改:

1
2
3
repos: # 考虑 import 的路径是 golang.org/x/crypto/acme/autocert,可能不会命中 golang.org/x/crypto
- original: golang.org/x/crypto/acme/autocert
repo: github.com/golang/crypto/acme/autocert

继续一样的错误,再改

1
2
3
repos: # github 上实际的地址是 github.com/golang/crypto/tree/master{/dir}, github.com/golang/crypto/acme/autocert 根本不存在,也许映射到根目录,余下的会根据规则匹配?
- original: golang.org/x/crypto/acme/autocert
repo: github.com/golang/crypto

照旧,再改

1
2
3
repos: # 看到有些人加了 https,死马当活马医,加上试试
- original: https://golang.org/x/crypto/acme/autocert
repo: https://github.com/golang/crypto

好了,终于不报该死的 Cannot detect VCS 了。

看不见的折腾

这数次不断尝试和失败的过程非常痛苦。因为没有一次跑到最后,没有生成 gllide.lock 文件,所以每次执行 glide install ,实际触发的都是 glide update ;而 github 虽然可以访问,速度并不快,在知道关键的 4 个包是否成功 fetch 前,先要忍受一堆包的 fetch updates,偶尔还会因为超时出现 exit status 128

而除了上面列出的,还尝试过加 --vcs git ,加 --base ,给 git 加 proxy …… 尝试次数比看到的要多,只是最后证明无关就没有列出;每次失败之后都要思考背后的原因,看哪里还能调整一下。在这上面耗了大半天。

0x3 还有 ERROR? 另一个 glide

endless errors

你以为这就完了?并没有!

在终于消灭了所有 Cannot detect VCS 之后

1
2
3
[ERROR] Unable to export dependencies to vendor directory: Error moving files: exit status 1. output
: ***********
************** // 乱码会打乱这篇文章的编码,所以用 * 代替

还有 ERROR,而且还乱码,什么鬼啊!?? 然后我就找到了这个:

https://github.com/xkeyideal/glide/blob/master/README_CN.md

简单说,glide 有两个 bug,一个是子包的目录映射错误(还没成功 export 过,无缘见识),一个是 Windows 下 export dependencies 时的权限问题,乱码是由于编码(Windows 默认 GB2312,但 glide 输出 UTF-8)。因为原作者迟迟没有合 PR #889 ,作者就 fork 之后修复了。(我去看了,17 年 7 月的 PR,到现在 18 年 10 月,仍然没有合并……)

forked glide

先把原来的 glide 重命名为 glide.Materminds.bk (在 ~/go/bin 下),然后

1
go get github.com/xkeyideal/glide

另外,记得加上 base 参数,实测过不加 export 的目录结构确实是错的。

1
2
3
4
repos:
- original: https://golang.org/x/crypto
repo: https://github.com/golang/crypto
base: github.com/golang/crypto

已经忘了是第几次执行

1
2
# glide init 只需执行一次,只要 glide.yaml 存在就不必再次执行
glide install

终于执行到了最后。

0x4 转投 dep

完事了吗?还没有。

从上面的尝试可以看出,glide 虽在一众依赖管理工具中收获了人气, 但存在不少 bug;而 364 个 open issue / 34 个 oepn pr ,多数有一年没有处理,最早的居然是 15 年的 (截止 2018/10/24),可以看出来作者 不再活跃 ,对一个开源项目来说, 比有 bug 更糟糕

像 Windows 下的权限问题,上面 #889 一年多前提了 PR 没人管,9月又有人提了重复的PR #1020: Update winbug.go ,作者依旧没有处理。

fork 项目虽解决了几个明显问题,毕竟不是活跃的原项目,更多是先修复继续用的权益之计,以后也很难代替原作者继续推进开发。

重新评估

我要考虑对于 go 依赖管理工具的认知是不是已经过时了。重新查这方面的对比,尽量留意时间近的帖子,翻到这么两篇:

挑重点说:go 有了官方的依赖管理工具 dep,关注度很高,而这还是 17 年 11 月的比较,当时 dep 刚出不久。

关键我还在原版 glide 的 release history 发现这么个东西:

1
2
3
4
5
6
7
8
9
Release 0.13.0
@mattfarina mattfarina released this on 29 Sep 2017 · 16 commits to master since this release
The latest release of Glide brings continued support.
Consider switching to dep
Glide is used by a great number of projects and will continue to get support for some time. But, the near future is likely in dep. dep can handle importing Glide config files. Please consider trying dep on your project or converting to dep. If you encounter an issue please check if there is an existing bug report and file one if there is not.
Glide will continue to be supported for some time as there are many users who rely on it and some are not able or ready to move to dep.

重点就是:这是 Glide 最后一个持续支持的版本了(我的理解是,往后只 patch,不大改),你们考虑切换到 dep 吧。dep 都能导入 Glide 的配置了,未来是官方亲儿子的。

亲儿子 dep

说得那么好,试试。地址在这 https://github.com/golang/dep

安装略过,请按官方指引来,以后也许会变。

1
2
3
4
5
# 加 -gopath 优先检查本地 gopath,加 -v 看详细输出否则啥进度没有干着急
dep init -gopath -v
...... // 省略正常内容
init failed: unable to solve the dependency graph: Solving failure: No versions of github.com/kataras/pio met constraints:
master: unable to deduce repository and source type for "golang.org/x/sys/unix": unable to read metadata: unable to fetch raw metadata: failed HTTP request to URL "http://golang.org/x/sys/unix?go-get=1": Get http://golang.org/x/sys/unix?go-get=1: dial tcp 216.239.37.1:80: connectex: A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

又是 golang.org 无法访问。是不是同样的配方,熟悉的味道?

这还没到下载,而是分析依赖图时获取 metadata 就出错了,所以 gopath 里已有了 cache 并不改变结果。难道 glide 上的困难又得重来一遍?继续问谷歌,又找到一篇:https://my.oschina.net/u/553243/blog/1504715

具体说,就是设置 http_proxyhttps_proxy 两个环境变量,dep 自然会读取并使用。环境变量根据系统有差异,为了不影响其他地方,每次打开 cmd / shell 设一下也不算麻烦;当然 alias 也是一种解决办法。

意外惊喜

这回终于好了。依赖图分析需要网络,稍有点久,后面直接读 gopath,速度很快。之后就是把 vendor 提交掉。

不过,提交过程太流畅了,隐约有一丝不对劲。毕竟用 glide 的时候,提交卡顿了一会儿,70+M 的一堆小文件,很正常。去看一眼 vendor 文件夹,这次居然才 25+M!?太小了吧,难道漏文件了?

可是点进去看我就懂了,dep 把引用不到的子包删掉了。换言之,glide 的最小粒度是 项目,哪怕只引用了大项目里的一个子包,也会把整个项目 export 到 vendor; 而 dep 应该是基于 subpackage 或者 文件的,先分析依赖图,没有引用到的文件都不要。Gopkg.toml 里的这段可以作为佐证:

1
2
3
[prune]
go-tests = true
unused-packages = true

用过之后,感觉 dep 相当简洁,-gopath 选项 和 prune 特性则是意外之喜。

0x5 写在最后

  • 在程序员的节日折腾这些,感受 GFW 带来的不便,相当不友好,感叹国内开发的不易,总有额外成本
  • 今天这『群雄并起,太子一统』的故事套路,怎么感觉跟当年 pip 那么像

知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议
本文为本人原创,采用知识共享 “署名-非商业性使用-相同方式共享” 4.0 (CC BY-NC-SA 4.0)”许可协议进行许可。
本作品可自由复制、传播及基于本作品进行演绎创作。如有以上需要,请留言告知,在文章开头明显位置加上署名(Jayce Chant)、原链接及许可协议信息,并明确指出修改(如有),不得用于商业用途。谢谢合作。
详情请点击查看协议具体内容。