

Golang 多版本管理
source link: https://soulteary.com/2021/12/15/golang-multi-version-management.html
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.

如果你是一个 Golang 的用户,那么你大概率会遇到管理和维护 Golang 版本的诉求,如果你恰好同时需要开发调试两个不同版本的项目,在不考虑强制跳版本的情况下,你或许就需要使用“Golang 版本管理工具”来帮助你减轻负担了。
本篇文章将介绍最近几个月,我在使用的工具,它们的优势和不足。希望能够帮助到有类似需求的同学。
在本地新旧项目并行开发的过程中,你大概率会遇到一个令人头疼的问题,如何同时使用两个不同版本的 Golang Runtime 进行开发呢?
在容器和 CI 流行的当前时代下,我们似乎已经习惯了用 docker run
来切换各种语言的版本,来完成不同项目的开发,基础类型项目的兼容性测试。配合一些支持远程调试的工具,体验似乎也还行。
但是在运行效率和复杂度上,相比本地环境而言,总归是高了那么一丢丢。那么有没有更节能环保的方式呢?
基于 Golang 的版本管理工具:voidint/g
最初安装 gvm
后,总觉得工具不够“简洁”,所以我基于 https://github.com/voidint/g/ 调整了一些细节,重新编译了一个版本自用。
如果你不希望自己编译安装,也可以用作者推荐的方式进行安装:
curl -sSL https://raw.githubusercontent.com/voidint/g/master/install.sh | bash
这里如果你是 oh-my-zsh
的用户,那么你还需要做一件事,就是解决全局的 g
命令的冲突,解决的方式有两种,第一种是在你的 .zshrc
文件末尾添加 unalias
:
echo "unalias g" >> ~/.zshrc # 可选。若其他程序(如'git')使用了'g'作为别名。
# 记得重启 shell ,或者重新 source 配置
第二种,则是调整 ~/.oh-my-zsh/plugins/git/git.plugin.zsh
中关于 g
的注册,将其注释或删除掉:
# alias g='git'
我的 .zshrc
中的完整配置:
# 我的 g 的bin目录调整到了 .gvm ,所以你可能需要一些额外的调整
export PATH="${HOME}/.gvm/bin:$PATH"
export GOROOT="${HOME}/.g/go"
export PATH="${HOME}/.g/go/bin:$PATH"
export G_MIRROR=https://gomirrors.org/
但是随着使用过程中,我发现在同时使用两个版本的 Golang 的时候,会有一些问题。翻看源码实现,看到了 https://github.com/voidint/g/blob/master/cli/install.go
中的安装定义:
fmt.Println("Checksums matched")
// 删除可能存在的历史垃圾文件
_ = os.RemoveAll(filepath.Join(versionsDir, "go"))
// 解压安装包
if err = archiver.Unarchive(filename, versionsDir); err != nil {
return cli.NewExitError(errstring(err), 1)
}
// 目录重命名
if err = os.Rename(filepath.Join(versionsDir, "go"), targetV); err != nil {
return cli.NewExitError(errstring(err), 1)
}
// 重新建立软链接
_ = os.Remove(goroot)
if err := mkSymlink(targetV, goroot); err != nil {
return cli.NewExitError(errstring(err), 1)
}
fmt.Printf("Now using go%s\n", v.Name)
return nil
发现其实每次版本切换,都将重新建立软链映射。官方项目的 Issue 区,有一个类似的反馈:#44,作者当时给出了一个 g
这个程序之外的解决方案。
所以,如果你的需求比较简单,期望使用一个工具,能够从网上快速的下载 Golang 的预编译版本的 Runtime,并且不需要同时运行多个版本,那么使用 voidint/g
就可以满足你的需求了,但是如果你的需求是需要多个版本同时运行,那么你可以接着往下看。
基于 BASH 的版本管理工具:gvm
因为出现了上面的问题,所以我开始考虑调整方案。首先是考虑切换回 https://github.com/moovweb/gvm,说起 gvm
,熟悉 Node.js 生态的同学,其实可以很容易联想起 nvm
。没错,他们的理念是一致的,通过语言生态无关的 Bash 来编写语言管理工具。
在 Node.js 中,因为维护版本下载、更新、删除、切换这些功能和语言无关(比如另外一款工具n
基于 Node.js),所以其实更健壮一些,不会出现因为 Node.js 配置出现问题, 语言版本管理工具无法运行,出现无法管理语言版本的问题。(鸡生蛋、蛋生鸡的哲学问题)但在 Golang 中,其实预编译的二进制已经和语言无关了,相比之下,使用 Bash 来编写程序,会显得比较“啰嗦”。
这也是我最初没有坚持 gvm
的原因之一。除此之外,gvm
虽然用户者众,但是很长一段时间作者已经不活跃了,所以在 Issue 和 PR 区都堆积了一堆待办事项。官方的文档中也存在不少错误或者缺失的地方。
不过,这些都是可解决的。
gvm
之于用户,一般存在三类常见问题:
- 程序安装过程中遭遇失败
- 下载 Golang 指定版本失败后无法继续安装
- 用户不知道如何使用镜像资源
先来解决第一个问题,如何正确安装 gvm,官方 ReadMe 中的安装方式在 ZSH 环境中会遇到问题,推荐切换为下面的方式安装:
curl -sSL https://github.com/moovweb/gvm/raw/master/binscripts/gvm-installer | bash
执行过后,我们就可以看到正确的日志输出了:
Cloning from https://github.com/moovweb/gvm.git to /home/ubuntu/.gvm
No existing Go versions detected
Installed GVM v1.0.22
Please restart your terminal session or to get started right away run
`source /home/ubuntu/.gvm/scripts/gvm`
接着我们来看第二个问题,首次安装 Golang 某个版本的时候,因为我们没有配置下载镜像地址,所以可能你的下载会遇到“中断”,获得一个不完全的程序压缩包。程序会判断我们是否已经下载过程序,会尝试优先使用下载过的缓存内容,而不管它是否是完整的,这就导致了一部分用户反复执行 gvm install go1.17.3 -B
,但是发现一切正常,就是无法完成版本下载或者切换。
解决这个问题其实也很简单,就是清除掉这个缓存内容:
rm -rf ~/.gvm/archive/go1.17.3.darwin-amd64.tar.gz
# or
rm -rf ~/.gvm/archive/
接着我们来看第三个问题,如何使用镜像地址进行下载,加速我们切换 Golang 版本的效率。在官方文档中,有一段使用介绍:
Usage: gvm install [version] [options]
-s, --source=SOURCE Install Go from specified source.
...
但是,这个其实并不是我们要的内容,因为它解决的是“指定Golang源代码”的在线地址,而不是预构建的二进制包的地址,在 https://github.com/moovweb/gvm/blob/master/scripts/install
中我们可以看到默认使用的是 GitHub 仓库代码,所以如果你希望从零开始源码编译,这个参数可以帮助到你,但是如果你想下载二进制,那么这个参数毫无用处。
...
GO_SOURCE_URL=https://github.com/golang/go
for i in "$@"; do
case $i in
-s=*|--source=*)
GO_SOURCE_URL=$(echo "$i" | sed 's/[-a-zA-Z0-9]*=//')
;;
...
在相同文件的比较靠下的位置,我么可以看到一个名为 download_binary()
的函数:
# `GO_BINARY_BASE_URL` env allow user setting base URL for binaries
# download, e.g. "https://dl.google.com/go".
GO_BINARY_BASE_URL=${GO_BINARY_BASE_URL:-"https://storage.googleapis.com/golang"}
GO_BINARY_URL="${GO_BINARY_BASE_URL}/${GO_BINARY_FILE}"
GO_BINARY_PATH=${GVM_ROOT}/archive/${GO_BINARY_FILE}
if [ ! -f $GO_BINARY_PATH ]; then
curl -s -f -L $GO_BINARY_URL > ${GO_BINARY_PATH}
if [[ $? -ne 0 ]]; then
display_error "Failed to download binary go"
rm -rf $GO_INSTALL_ROOT
rm -f $GO_BINARY_PATH
exit 1
fi
fi
这里有一个 GO_BINARY_BASE_URL
变量,针对它进行调整,就可以达到我们的目的啦。可惜的是,这个参数自2019年末合并进来之后,并没有更新文档,如果你不阅读代码,基本不会知道还可以从镜像进行资源下载。
这里给出我目前使用的配置,在将下面的配置添加到你的 SHELL 的 rc
后,你就可以正常的使用 gvm
对 Golang 进行快速的版本切换啦。
export GO111MODULE=on
export GOPROXY=https://goproxy.io,direct
# or
# exort GOPROXY="https://goproxy.cn"
export GOPATH="$HOME/go"
PATH="$GOPATH/bin:$PATH"
export GO_BINARY_BASE_URL=https://golang.google.cn/dl/
[[ -s "$HOME/.gvm/scripts/gvm" ]] && source "$HOME/.gvm/scripts/gvm"
export GOROOT_BOOTSTRAP=$GOROOT
至于切换不同版本 Golang ,也很简单,只需要两条条命令:
gvm install go1.17.3 -B
gvm use go1.17.3
倘若你期望不借助 Golang 团队官方镜像,完全定制一个 Golang Base 的 Docker 的镜像,相比较其他工具,gvm
会是一个简单的选择,不需要预构建、也不挑系统。
来自官方的解决方案:golang/dl
如果你不喜欢来自三方的解决方案,那么或许可以试试来自官方的方案。(前提是,你不需要同时运行多个版本的 Golang)
相比较社区方案,官方的方案就更有趣了:https://github.com/golang/dl。官方维护了自 1.5 以来到 1.17 的所有版本的更新软件包。
我们可以通过安装普通软件包的方式来获取具体版本的安装工具,以及进行“覆盖安装”:
go get golang.org/dl/go1.17.3
go1.17.3 download
不过和上面不同的是,https://github.com/golang/dl/blob/master/internal/version/version.go中的写死的逻辑会让你安装的目录在用户目录的 sdk
文件夹中,所以如果你使用这种方式,export
的路径需要做一个调整:
func goroot(version string) (string, error) {
home, err := homedir()
if err != nil {
return "", fmt.Errorf("failed to get home directory: %v", err)
}
return filepath.Join(home, "sdk", version), nil
}
此外,还有两个有趣的项目,借鉴自 Rustup 的 :https://github.com/owenthereal/goup;以及借鉴 rbenv和pyenv的:https://github.com/syndbg/goenv。
最近在持续做笔记内容整理的事情,恰好看到这篇笔记草稿,顺手整理成文。
本篇就先写到这里啦,希望能够帮你节约一些时间,避过小坑。
Recommend
-
43
使用gvm管理golang版本遇到一个问题,就是我在.zshrc中设置了GOPATH,但是使用gvm切换后GOPATH又被gvm重新设置了,默认是$GVM_ROOT/pkgsets/go1.12.5/global。而我希望切换不同的GOROOT而GOPATH不变,因为我的项目代码都在原来的GOPATH中...
-
30
我的Mac上已经有一个JDK8的版本了,这不 JDK13 刚发布(2019-09-17),想快速的尝一尝鲜,就得安装多个版本的JD...
-
38
我们都知道在一些Golang写的程序中,默认会有 version 或 -v 相关的参数来输出软件版本信息,这些版本信息里可能包含软件版本,git中的commit记录,构建时间、构建环境等信息,那么这些信息都是如何在Golang程...
-
23
友情提示:此篇文章大约需要阅读 5分钟5秒,不足之处请多指教,感谢你的阅读。 订阅本站 在 Go 项目开发中,团队要保持开发版本一致,怎么能够快速的安装及部...
-
27
作者: HelloGitHub-追梦人物 API 不可能一成不变,无论是新增或者删除已有 API,都会对调...
-
13
Windows 下 golang 多版本管理 当前 golang 各个版本还是有些许不兼容,最近遇到 go-micro 框架只能运行在 go 1.13 ~ 1.14 版本,而我Windows 下安装的又是
-
21
重要提示 请始终升级到所发布系列的最新稳定版本。 MongoDB的版本...
-
10
...
-
10
一直用golang写业务代码,最近改动升级项把common module的version升级的v2,结果尴尬了,于是查了下文档,随手记录下v2.x及以后得版本的使用方法。 golang中module的版本管理分路径和版本号两部分,路径是go.mod中开始module
-
3
使用gvm管理多版本golang ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK