6

Go 包管理工具 dep 和 go module 的对比

 3 years ago
source link: https://liqiang.io/post/golang-package-manager-compare-module-vs-dep-d1b2db90?lang=ZH_CN
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

All Posts

Go 包管理工具 dep 和 go module 的对比

@POST· 2021-02-24 20:33 · 11 min read

最近我将一些项目的依赖管理工具从 dep 迁移到了 go module,有一些爽的地方,也有一些不爽的地方,所以这里就简单介绍一下我的迁移历程中一些个人的观点感受。

先提一下,截止到我写这篇文章的时候,Go 的最新 release 版本是 1.16。

  • 指定版本很友好

    Dep 中支持在 Gopkg.toml 指定一个 package 的特定版本,而且支持多种语义,例如:

    • version:指定使用特定的版本,可以是 >,=,< ... ...
    • branch:指定使用特定的分支
    • revision:指定使用特定的 commit
  • 支持保存代码中没有用到的 package

    Dep 可以通过 ignored 选项来过滤代码中没有使用到的依赖,这对于引用一些工具是非常有用的。例如我们创建 mock 的工具,可以直接放在 vendor 里面,然后 ci 的时候通过 go install vendor/xxx 来安装;在 go module 中,则不行了,常见的用法是创建一个 tools.go 的文件,然后里面 import _ xxxx

  • 连接管理不稳定

    这是最多同学吐槽的点,就是 dep 经常一个 dep ensure 命令就是一天,然后还完不成,这其实就是 dep 连接管理和重试的问题了;此外,还有问题就是 dep 的本地缓存也处理得不是很好,经常会出现远程 master 分支已经更新了,但是本地缓存因为处于另外一个分支(例如是 v5 这个 release 分支),但是 dep 就是反应不过来,不会切换到 master 分支之类,然后就导致你 dep ensure 的时候它报错说找到你想要的那个版本。

    这个时候,我常用的处理方式就是 rm -rf $GOPATH/pkg/dep/sources/git---xxxx 清除掉本地的缓存,然后再 dep ensure 一遍就可以了。

  • 无法更新指定 package

    例如我想只更新一个 github.com/pkg/errors 的版本,其他无关的依赖不更新,那么对不起,不支持这个操作,我只能使用 dep ensure 更新所有的 package。

    如果我就是这么执拗,就只想更新这个 package,可以,我需要把所有的项目直接使用到的其他 package 的当前版本在 Gokpkg.toml 里面都指定好版本了,这样就不会被更新到了。

  • 包管理奇怪

    • 例如我想添加一个依赖,如果我在项目中已经 import github.com/pkg/errors 了,那么这个时候我用 dep ensure -add github.com/pkg/errors 会提示我已经使用了这个 package,如果我想将这个依赖更新到 Gopkg.lock 里面,那么我只能 dep ensure

go module

  • 多版本支持

    如果你的项目想同时使用一个 Module 的多个版本,例如主要的代码都是使用一个最新版本的 Module,但是可能因为兼容旧版本的接口之类的,你还需要在某些 API 调用的地方使用比较旧版本的 Module,那么 go module 可以帮助你做到,因为它支持在 import module 的时候指定版本。

  • 支持环境变量设置

    比较常用的:

    • GOPRIVATE:私有项目,不使用 Proxy
    • GOPROXY:支持 proxy,在内网 CI 时格外有用
  • 简单直接的包依赖管理

    例如我想添加一个新的依赖包,直接使用 go get xxx 即可被添加在 go.mod 中;想升级,直接就 go get -u 即可;如果想指定版本,也是直接 go get xxx@commit-id

  • 支持撤消发布

    这是一个新的特性,如果你使用了一个错误发布的版本,那么 go mod 会提醒你,然后你就需要进行升级或者降级了。

  • 包版本管理不友好

    虽然前面说了 go module 的包依赖管理很简单,但是,对于一些 tag 来说就不友好了,例如我想安装一个 v5 版本的 tag:go get github.com/godbus/[email protected],对不起,你会遇到这个问题:

      [[email protected]]# go get github.com/godbus/[email protected]
      go get: github.com/godbus/[email protected]: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v5
    

    那么,我需要怎么做呢?下面两种方式都是官方允许的,其中,推荐第一种(虽然我觉得很别扭,而且升级到 v6 之后还得修改代码,但是人家也是认为这是 feature,见优点 1):

      [[email protected]]# go get github.com/godbus/dbus/v5
    
      [[email protected]]# go get github.com/godbus/dbus@37bf87eef99d69c4f1d3528bd66e3a87dc201472
    
  • go 生态的版本管理不好

    go mod 认为所有被管理的包都是遵循语义化版本(见我的文档中有是说明),但是,实际上有些包是不讲武德的,例如最近我被坑的 grpc-go 这个包,x.y.z 版本的不同 y 版本之间居然是不兼容的,这都属于 google 自己家的东西你让我情何以堪(不是说 Google 没有 KPI 压力的吗,还这么分裂的么)。

    除了 grpc-go,另外一个常用的被人吐槽的库就是 etcd 了,但是因为我没怎么用(很久以前用了,但是那时还没有 go mod,而且可能那时还是比较简单的,没有太多版本的问题)。

  • 其他命令会检查 go.mod 版本

    我遇到过的常见命令就是go rungo test,他们都会检查一遍 go.sum 中的版本是否和 go.mod 以及项目中的依赖匹配,如果不匹配时,他们就会更新一把。

    有时这让我很心累,例如前面提到的我要向后兼容限制 grpc-go 的版本,经常就被升上去了(还是那个只能限制最小版本的锅啊)。

  • 大家都支持替换 package 的源地址
    • dep:override
    • gomodule:replace
  • 大家都有创建和填充 vendor 目录的功能
    • dep ensure --vendor-only
    • go mod vendor
  • 都不能很好地解决私有项目的问题
    • 内网不受信任的 github
    • github 上的私有项目
    • 这两个场景都需要设置本地的 git config 来解决

整体来看,go module 还是值得升级了,唯一让我留念 dep 的 feature 就是精准的 version 指定,在 go module 中,没法做到精准的指定,即使你指定了,也是认为这是最低要求版本 “>=”,后面很可能因为有依赖的其他 package,会将这个 package 的版本升上去,当然,偶尔也是可以满足你的指定,就是使用你想要的那个版本的。

让我最舒心的就是我提交代码不用加 vendor 目录,而是使用一个 private 的 PROXY Server,这样 CI 速度不受影响,代码提交记录也很清爽,是个非常不过的改进。

© 2012-2020 路人的技术 版权所有. Powered by LauZoo. Theme based on Nuo.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK