Go Module 工程化实践(三):工程实践篇

1
尊重生命,即使是蚂蚁,也不会踩死。 -- JayL

3. 工程实践篇

如何实现企业内项目的Go Module工程化迁移?

以本人以往所在公司的实际现状作为样例,说明具体的Go Module工程化迁移过程。

原有Go项目均采用单一vendor的模式进行依赖控制,即企业内所有Go项目的第三方依赖均引用该统一的vendor仓库,由专人专组独立维护。这样做的好处就是依赖包不会随实际开发者的版本变更而变更,企业内部维护一套相对稳定的版本,缺点就是缺少了依赖包的版本控制。

Go Module工程化迁移的目标就是保持本地版本的稳定的同时兼顾版本控制功能。

3.1 modules仓库

在非Go Module项目中,所有项目的依赖包,使用了vendor仓库,在迁移到Go Module模式下,同样需要相应的modules仓库, 来保证本地开发包依赖版本的稳定。

modules仓库的依赖包从何而来。不妨看看:


$: tree -L 1 $GOPATH/pkg/mod/cache/download
├── cloud.google.com
├── git.apache.org
├── git.yixindev.net
├── github.com
├── go.opencensus.io
├── go.uber.org
├── golang.org
├── gonum.org
├── google.golang.org
├── gopkg.in
├── gotest.tools
├── honnef.co
├── k8s.io
└── layeh.com

这就是我们要维护的modules仓库依赖包。在开发过程中,新的依赖包都会下载到这个路径。将新的依赖包版本复制到modules仓库相应的路径。就完成了modules仓库依赖包的版本维护。

需要注意的点是:

我们是从$GOPATH/pkg/mod/cache/download目录中复制新的依赖包到modules仓库中。但并不是所有的文件都需要进行维护,特别是本地下载过程中的一些临时文件。

$: tree -L 1 $GOPATH/pkg/mod/cache/download/github.com/x-mod/httpclient/@v/
├── list
├── list.lock
├── v0.1.2.info
├── v0.1.2.lock
├── v0.1.2.mod
├── v0.1.2.zip
├── v0.1.2.ziphash
├── v0.2.0.info
├── v0.2.0.lock
├── v0.2.0.mod
├── v0.2.0.zip
├── v0.2.0.ziphash
├── v0.2.1.info
├── v0.2.1.lock
├── v0.2.1.mod
├── v0.2.1.zip
└── v0.2.1.ziphash

在这个github.com/x-mod/httpclient依赖包中,我们仅仅需要具体版本的四个类型文件:

  • info
  • mod
  • zip
  • ziphash

其它类型的文件,是不需要进行modules仓库维护的。所以可以在modules仓库中通过.gitignore进行忽略。

3.2 CI过程更新

完成了modules仓库的维护后,我们就可以对原有项目的CI过程进行更新了。在CI编译机或者容器上

  • git clone modules repo 到指定位置 path/to/modules
  • 开启GoModule编译选项, 设置export GO111MODULE=on
  • 设置GoProxy环境变量, 设置通过本地文件代理: export GOPROXY=file:///path/to/modules

现在所有GO项目就会开启GoModule选项同时,可以完成依赖包的版本控制。如何缺少依赖包,只需要从本地将新增依赖包的版本添加到modules仓库即可。

3.3 企业仓库GoGet代理优化

如果阅读了Go Get原理之后,针对企业依赖包的GoGet,我们可以写一个简单的http代理程序, 这样就设定自己的:


// Example
// code server http://aaa.com:888/user/repo.git
// code import path: bbb.com/user/repo
// 
// host => aaa.com:888
// vcs  => git
// root => bbb.com

type Getter struct {
    host    string //gitlab address
    vcs     string //git
    root    string //git
}

func NewGetter(host string, vcs string, root string) *Getter {
    return &Getter{
        host: host,
        vcs:  vcs,
        root: root,
    }
}

func (x *Getter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if r.URL.Query().Get("go-get") == "1" {
        sp := strings.Split(r.URL.Path[1:], "/")
        if len(sp) < 2 {
            http.Error(w, fmt.Errorf("unsupport path: %s", r.URL.Path).Error(), http.StatusBadRequest)
            return
        }
        prefix := fmt.Sprintf("%s/%s/%s", x.host, sp[0], sp[1])
        repository := fmt.Sprintf("%s/%s/%s.%s", x.root, sp[0], sp[1], x.vcs)
        fmt.Fprintf(w, `<html><head><meta name="go-import" content="%s %s %s" /></head></html>`, prefix, x.vcs, repository)
        log.Println("go get [", prefix, "] from repository [", repository, "].")
        return
    }
    http.Error(w, fmt.Errorf("unsupport request: %s", r.URL.Path).Error(), http.StatusBadRequest)
}

通过这个简单的代理,你就可以实现:

code server http://aaa.com:888/user/repo.git
code import path: bbb.com/user/repo

host => aaa.com:888
vcs => git
root => bbb.com

这样的非标依赖包的拉取了。

这一篇拖了好久,花一个小时完结掉这个系列。

更多文章可直接访问个人BLOG:GitDiG.com

相关阅读:


如果觉得我的文章对你有用,请随意赞赏

你可能感兴趣的

Fengly · 5月20日

您好,拜读了您的文章对go mod 使用加深了更深一层的认识,实践中遇到一个关于公共库更新的问题:github上获取项目A,用到了golang.org/x/net包,go.mod文件replace到github.com/golang/net vx.x.x安装完成,然后再到github获取项目B,也用到golang.org/x/net这个包,更新就出现了问题,go get -u github.com/A 不报错,go get -u github.com/B 结果就报错:upgrading golang.org/x/net@v0.0.0-20190503192946-f4e77d36d62c: unrecognized import path "golang.org/x/net" (https fetch: Get https: //golang.org/x/net?go-get=1: dial tcp 216.239.37.1:443: i/o timeout),不知道能否帮忙解惑

回复

0

项目B:也需要使用replace,和项目A一样
go.mod:

replace golang.org/x/net v0.0.0 => github.com/golang/net
或者 直接将github.com/golang/net 依赖clone到本地,不过还是需要进行replace操作。
你得明确知道具体net包的使用版本,如果B的版本和A相同的话,且$GOPATH/pkg/mod/路径下已经存在对应版本,go get 才会优先使用本地已经拉取的依赖包,否则都会进行远程拉取。

JayL 作者 · 5月20日
0

是的,项目B也使用了replace而且和A的版本是一样的,$GOPATH/pkg/mod/下也存在该v0.0.0 20190503192946...版本,按照我的理解,两个项目执行 go get -u ...应该是一样的不报错,现在却是A不报错,B就报错upgrading golang.org/x...,另外既然用github.com replace golang.org/x/...,错误提示居然还是去请求的 https: //golang.org/x/net...

Fengly · 5月20日
0

@Fengly 个人建议:

replace 主要使用场景是映射本地开发的依赖包。

对于远程依赖包尽可能使用GOPROXY或者本地直接VPN拉取。

JayL 作者 · 5月20日
载入中...