Go mod配置踩坑小记【依赖管理&本地仓库配置】

8,586 阅读3分钟

之前在学mit6.824分布式数据库课程,今年来忙着跳槽和适应新工作环境,搁置了好长一段时间。近期拾起,计划将后续的课程lab尽力悉数完成。(见之前的课程lab juejin.cn/post/686567…

之前是在linux上面用vim写代码,感觉效率上有些欠佳,还是习惯在ide上编写。另外,之前图省事直接采用$GOPATH作为工作目录,个人理解基本做不到包管理工具应有的功能,包括依赖管理、版本管理等等。go 1.11版本引入了go mod作为官方的包管理工具,是目前go包管理工具的主流。于是花了一周的时间在mac上搭建了环境,期间踩了不少坑,在此记录供参考。

Go环境基本配置

现在mac上go的安装也十分方便了,直接在官网 (golang.google.cn/dl/) 下载pkg安装包即可。我这边下载的是1.15版本的安装包。安装后的安装内容会在/usr/local/go这个路径下面,如果需要更新版本则在官网下载更新的版本即可,降级版本则删除/usr/local/go然后重新下载即可。

另外还有Homebrew的安装方式也在这里记录一下:

# 安装go,默认安装最新的版本
$ brew install go

# 升级go
$ brew upgrade go

# 降级go,eg:1.17->1.15
$ brew install go@1.15
$ brew unlink go
$ brew link --force go@1.15

# 查看验证版本
$ go version
> go version go1.15.15 darwin/amd64

配置环境配置在~/.bashrc下即可,如果是zsh则配置在~/.zshrc,别忘了source ~/.bash_profile生效。

#go env
export GOROOT=/usr/local/go
export GOPATH=~/gopath
export PATH=$PATH:$GOROOT/bin
export PATH=$PATH:$GOPATH/bin

其中GOROOT就是go的安装路径,GOPATH是go的工作空间,也就是项目和包的路径。

可以通过go env来查看和验证go的环境信息,例如我的部分配置信息如下:

GO111MODULE="on"
GOARCH="amd64"
GOBIN=""
GOCACHE="/Users/Janus/Library/Caches/go-build"
GOENV="/Users/Janus/Library/Application Support/go/env"
GOMODCACHE="/Users/Janus/gopath/pkg/mod"
GONOPROXY="github.com"
GONOSUMDB="github.com"
GOPATH="/Users/Janus/gopath"
GOPRIVATE="github.com"
GOPROXY="https://goproxy.cn,direct"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"

GOPATH管理依赖

上述提到的GOPATH也就是工作空间,里面包含了三个文件夹:

bin:用于存放编译后生成的可执行文件,需要注意的是go installgo build的区别:

# 先编译导入的包,再编译主程序
# 编译后的包文件放在$GOPATH/pkg,主程序编译后的可执行文件放在$GOPATH/bin
$ go install

# 如果想要设置可执行文件的生成路径,则设置环境变量中的GOBIN即可
# 可在~/.bashrc添加环境变量
export GOBIN=$GOPATH/bin

# 检查编译是否有错误,如果是main文件则在被编译源码的文件夹下面生成可执行文件
$ go build

# go build可以通过-o来指定生成可执行文件路径
# 会在当前文件夹生成的同时在指定路径生成
$ go build -o ~/gopath/bin

pkg:存放编译后生成的包文件,即go install后生成的文件。并且,如果是通过go mod管理包,那么包也会下载到这个文件夹。

src:用于存放项目的源码。在不启用go mod的情况下只能将源码放在该文件夹中。

可以写个简单的demo对GOPATH管理依赖进行实现验证:

// 路径$GOPATH/src/hello
package main

import "fmt"

func main() {
	fmt.Print("hello")
}
# 验证代码
$ go build hello.go
$ ./hello

Go mod管理依赖

Go mod是Go 1.11引入的新特性,建议升级到1.12及以上进行使用。主要用于解决依赖的问题,可以记录和解析对其他模块的依赖性,从而替代原来的基于GOPATH的指定构建中使用哪些源文件。

简单一句话来说,可以使用go mod$GOPATH/src以外的地址创建项目,同时可以为每个项目指定依赖版本。

GO111MODULE

要启用go mod首先需要了解一个环境配置GO111MODULE

GO111MODULE含义
GO111MODULE=off不启用go mod,直接从$GOPATH获取依赖
GO111MODULE=on启用go mod,需要通过go mod的配置获取依赖(经本地测试,官方提供的标准库不配置go mod也能获取得到)
GO111MODULE=auto分两种情况:
当前源代码路径在$GOPATH/src外面或源代码文件夹下面存在go.mod时,同GO111MODULE=on;
当前源码在$GOPATH/src下且文件夹下没有go.mod,同GO111MODULE=off

目前1.16版本之前的环境默认值GO111MODULE=auto,在1.16及之后的版本默认值为GO111MODULE=on,可以看到官方意图取缔掉$GOPATH这种管理方式。可以通过更改配置文件或者输入命令进行配置:

#在~/.bashrc下或~/.zshrc下添加环境配置
export GO111MODULE=on

#或者采用命令进行环境配置
$ go env -w GO111MODULE=on

#如果想要恢复默认值
$ go env -u GO111MODULE

开启之后,即可使用go mod命令来管理包了,不过在这之前,如果是在国内网络建议还有一步配置模块代理要做。

GOPROXY

go env环境变量中有个变量GOPROXY允许对下载源进行控制,设置了这个变量,就会开启Go模块代理,可以提高下载依赖的稳定性和速度。

如果GOPROXY设置为空或为设置为direct,表示去目标模块的源地址进行下载(如github.com),如果是设置为off则表示不使用代理。由于golang.org的模块在国内无法访问的问题,所以国内可以采用七牛云提供的代理goproxy.cn

$ go env -w GOPROXY=https://goproxy.cn,direct

https://goproxy.cn,direct逗号后的direct表示如果通过代理拉取模块遇到错误,则通过direct的方式拉取模块。该功能在1.13版本后上线,所以在1.13版本之前只能配置为GOPROXY=https://goproxy.cn

更多可查看goproxy.cn/github.com/goproxy/gop…

GO MOD

可以通过go mod help查看一下go mod的命令:

commandexplanation
downloaddownload modules to local cache
editedit go.mod from tools or scripts
graphprint module requirement graph
initinitialize new module in current directory
tidyadd missing and remove unused modules
vendormake vendored copy of dependencies
verifyverify dependencies have expected content
whyexplain why packages or modules are needed

接下来讲解一下如何使用go mod,以mit6.824这个课程的项目(github.com/Januslll/mi…) 来举例:

该项目原来没有采用Go mod进行配置,它的目录结构为:

mit-go
 └─src 
    ├─main
    │  ├─mrsequential.go
    │  ├─mrmaster.go
    │  ├─mrworker.go
    │  └─...
    ├─ mr
    │  ├─master.go
    │  ├─worker.go
    │  └─rpc.go
    └─...

首先,我们在mit-go文件夹下面输入命令:

# 初始化go mod,mit-go表示包名
$ go mod init mit-go

会在当前文件夹下面看到生成了一个go.mod文件,这个时候go.mod文件只会包含有包名信息和go的版本信息。

接下来我们在mit-go/src/main/mrmaster.go中引入mit-go/src/mr这个包,引入方式就是在mrmaster.go中加入import github.com/Januslll/mit-go/src/mr。其中的github.com/Januslll/mit-go/src/mrmr这个包在github的项目上的路径。

接着可以通过go mod进行下载或者通过go get进行下载包都可以。

# go mod 下载包
# go mod tidy会自动下载需要的包,并把没有用的包移除
$ go mod tidy

# go mod download下载依赖的包到本地cache,和go mod tidy相比不会移除包
$ go mod download

# go get命令在没有go.mod文件的情况下也能下载包
# 如果有go.mod则会同时更新go.mod的内容
go get -u # 表示升级所有依赖
go get -u -v # -v表示打印详细日志
# 下面这语句表示下载https://github.com/Januslll/mit-go这个路径地址下的包
go get -u -v github.com/Januslll/mit-go 

这个时候可以查看go.mod中的内容:

# module名称
module mit-go

# go 版本
go 1.15

# require 用来定义依赖包的版本
# 后面的// indirect表示间接依赖,通常如果有间接依赖说明import写的有问题或者依赖的包存在问题
require github.com/Januslll/mit-go v0.0.0-20200827115747-2ab09a53eea2// indirect

这个时候就表示已经拉取到依赖了,依赖的格式通常为版本号+时间戳+hash,例如我这里就是v0.0.0-20200827115747-2ab09a53eea2

其中版本号是v0.0.0,这就表示没有版本的控制,官方建议通过git tag加上版本号,如下所示:

git tag v1.0.0
git push --tags

加上后就会变成v1.0.0版本。

时间戳和hash分别表示commit提交的时间戳和commit记录的hash值取前12位,输入git log可查看,如下所示:

commit 2ab09a53eea24dcbb01307b77bb5ed08080ababc (HEAD -> master, tag: v1.0.0, origin/master, origin/HEAD)
Author: Janus <274123369@qq.com>
Date:   Thu Aug 27 19:57:47 2020 +0800

可以通过修改版本号、时间戳、hash来实现依赖版本的管理。同样,go mod还提供了依赖添加和删除的语句:

# 添加依赖
go mod edit -require=github.com/Januslll/mit-go

# 移除依赖
go mod edit -droprequire=github.com/Januslll/mit-go

另外还会看到一个文件go.sum文件,里面记录了每个依赖包的hash值,防止下载的依赖包有被篡改的可能性,如果篡改了则拒绝构建,对里面的实现感兴趣的可以移步go.dev/blog/module…

到此,go mod配置基本完成。

使用本地仓库

上述配置好之后,github.com/Januslll/mit-go/src/mr引用的依赖包就是从github下载下来的依赖包,但是如果有bug你想要本地调试修改,就得使用本地的包来进行调试。

有两个解决办法,第一个就是改import依赖路径,第二种方法就是使用go mod的replace。

# 方法一,修改依赖(不够优雅,不建议)
import mit-go/src/mr

# 方法二,使用go mod的replace,使用go mod edit -replace对go.mod进行修改
# 命令格式:go mod edit -replace [old git package]@[version]=[new git package]@[version]
# 如果new git package是本地仓库则输入本地仓库路径即可,无须带version
go mod edit -replace github.com/Januslll/mit-go@v0.0.0-20200827115747-2ab09a53eea2=/Users/Janus/my_project/mit-go

使用go mod edit -replace后,会看到go.mod中新增了一条记录如下所示,表示已经用本地仓库替换了github上下载下来的依赖。

replace github.com/Januslll/mit-go v0.0.0-20200827115747-2ab09a53eea2 => /Users/Janus/my_project/mit-go

至此,可以愉快地本地调试写代码了。

使用私有仓库

公司内部往往会使用公司私有仓库,这个时候只配置了GOPROXY是无法获取得到依赖的。

在go 1.13版本后,提供了GOPRIVATE环境变量来配置私有仓库。

# 在~/.bashrc下或~/.zshrc下添加环境配置,例如gitlab.com/xxx是公司私有仓库
# 该配置可以用逗号分开配置多个仓库
export GOPRIVATE=gitlab.com/xxx

#或者采用命令进行环境配置
$ go env -w GOPRIVATE=gitlab.com/xxx

配置了之后,还需要配置私有仓库的访问权限设置,我这里采用ssh进行配置。由于我需要将个人用户和工作用户隔离开故需要配置不同的ssh,这里记录下如何配置。

# 分别生成ssh key
ssh-keygen -t rsa -C "your_email@example.com" -f ~/.ssh/id_rsa_gitlab
ssh-keygen -t rsa -C "your_email@example.com" -f ~/.ssh/id_rsa_github

# 添加私钥
ssh-add ~/.ssh/id_rsa_gitlab
ssh-add ~/.ssh/id_rsa_github

# ssh配置
vim ~/.ssh/config

# ~/.ssh/config配置内容
# Host:别名
# Hostname:git服务器名字 一般都是github.com 或者公司自己的gitlab
# User:用户名
# IdentityFile:ssh key的文件

# 个人ssh
Host github
Hostname github.com
User Janus
IdentityFile ~/.ssh/id_rsa_github

# 工作ssh
Host gitlab
Hostname gitlab.com
User Janus
IdentityFile ~/.ssh/id_rsa_gitlab

#测试配置,其中temp为上面的HOST,如果Host为github,则为git@github
ssh -T git@temp

# 分别配置用户信息
cd my_project
git config --local user.name "yourName"
git config --local user.email "your_email@example.com"

cd work_project
git config --local user.name "yourName"
git config --local user.email "your_email@example.com"

跟着上面的步骤即可配置完成,有个坑点是如果你原本是https的需要更改为ssh,例如:

git config --global url."git@github.com:Januslll/mit-go.git".insteadOf "https://github.com/Januslll/mit-go.git"

最后把ssh添加到账户的github.com/gitlab.com页面的SSH keys上面去即可。

综上,Go的整个配置环境完成,如有疑问欢迎交流。

参考

github.com/goproxy/gop…

roberto.selbach.ca/intro-to-go…

github.com/golang/go/w…

maelvls.dev/go111module…

go.dev/doc/faq#git…

www.kancloud.cn/cattong/go_…