也许 Go 开发可以更简单!Go += Package Versioning
缪斯的情人 · · 2517 次点击 · · 开始浏览简评:对于Go来说一直以来依赖包的版本控制上没有一个好的方案,尽管社区诞生了不下十余个解决该问题的工具,但一直以来没有一个官方的支持。这个提案有望在Go
的下个版本中看到官方的包版本控制了,去除了GOPATH依赖,同时还引入了module 的概念,真正意义上实现了重编译,可谓一次大的变更编译:缪斯的情人
是时候为 Go 添加包版本控制了!
更确切地说,我们需要把包版本的概念普及到Go 开发者和工具常用词汇中,以便在后续的相互沟通时能准确一致的表达哪些程序代码需要编译、运行和解析。同样,go 命令也需要准确的告诉开发者在编译中使用了哪个包的哪个版本。
版本控制可以让我们能够实现重编译。当我让你试用我程序最新版本时,我清楚的知道你不仅仅获取到的是我最新程序的代码,还包括我代码所依赖的相同版本的包,这样才能编译出完全一样的二进制包。
版本控制还能让我们不同阶段保持同样的编译方式,即使我们的依赖包可能有新版本了,只要我们的配置未允许使用,go 命令也不会使用新版本的包。
尽管添加版本控制是必须的功能,但同时我们也不能失去go 命令行现有的优秀特性:简单、高效、易懂。目前来看,很多程序员还是不太重视版本控制,主要是大部分情况下也都没出现什么问题。试想如果我们有一个合理模式的设计和默认配置,让开发者不需要太多关注版本控制的东西,程序仍然能很好的工作,并且对现有工程影响较小,发布新版本足够简单,甚至日常开发中可以忽略掉版本控制的工作,这样的版本控制模式才是我们想要的。
简而言之,版本控制是必须的,但是应该足够透明不能破坏掉go get 本身功能。这篇文章探讨了一个能完全实现这些的提案,并且提供了一个现在可用的原型demo,希望这能为集成到go命令奠定基础。我打算通过这篇文章讨论下在产品化过程中哪些是需要做的,哪些是不需要做的,基于这个讨论,我将对提案和原型做进一步调整,并且提交一个官方提案,作为可选功能集成到Go 1.11版本中。
这个提案保留了go get的精华部分,增加了重复构建,采用了语义化的版本控制,弃用了vendor,废弃了基础工程创建时依赖GOPATH,并且提供了老项目平滑迁移的方式,目前这个提案还处于初级阶段,如果细节上有问题,我们会在Go主版本发布前修复掉
背景
在我们讨论这个提案之前,先了解下当下的现状。讲起来可能会很长,但历史的教训对现在有很重要的参考意义,并且让我们能够清楚的知道这个提案改变了什么。如果你觉得没意思,那么可以直接跳到demo
Makefiles, goinstall, 和 go get
2009年11月,Go 发布了带有编译器、链接器和一些内置库的初代版本,在当时你必须通过运行6g 和6l 来编译和链接你的程序,还包含一些简单的makefiles。大多数情况下,通过简单包装的gobuild可以编译一个单包程序并且生成对应的makefile。在当时,也没有一个合适的方式把代码分享给其他人。尽管还有很多功能没提供,但是仍然发布了,并且Go 计划将一些剩余的功能放到社区去做。
2010年2月,我们介绍了go命令中使用 go get 来替换goinstall 。
总的来说,go get 是变革性的,它让 Go 开发者能够共享代码和相互构建,并且通过工具隔离了 go 命令编译系统中的细节,但是,go get 缺少版本控制的概念,实际上在goinstall第一次讨论中就清楚的意识到需要版本控制相关的功能。不幸的是,至少在当时我们的 Go 团队中还不清楚应该怎么去做。当go get需要一个包时,总是从像 Git 或者 Mercurial 这样的远程版本控制系统中下载最新的副本,包版本管理上的缺失至少导致了两个重大的缺陷。
Versioning 和 API 的稳定性
首先在没有版本控制时go get 的一个重大缺陷是对于给定的更新无法知道是否是用户所期望的。
在2013年11月,Go 1.2 添加了一个发关于包版本控制基本建议的FAQ:
公共发布的包应该保持向后兼容性。gopkg.in,倡导"Go语言稳定性API"。这个网站其实是个感知 GitHub 版本变化的重定向器,你可以通过gopkg.in/yaml.v1和gopkg.in/yaml.v2的这样的导入方式来指向Git库的不同提交版本(也可能在不同分支)。按照这个思路,依赖库的功能有了重大改变后你可以把之前的 v1 版本导入路径作为备用,然后创建一个新版本 v2,通过v2 导入路径引入完全不同的API。2015年8月,Dave Cheney 提出了Glide in the Sea of Go Package Managers" 比较了godep和很多后来出现的工具,其中最为突出的是
glide。2015年4月,Dave Cheney 介绍了dep,相关背景信息请查看Sam 在2016年2月发布的文章"The Saga of Go Dependency Management" 以及2017年7月GopherCon 的分享"examples)。
Goinstall和旧的go get通过像git 和hg 这样的版本控制工具直接下载代码,这种方式存在很多问题,其中包括碎片化严重:用户如果没有bzr就没法下载托管在Bazaar 资源库的代码。相比之下,模块则是通过HTTP 下载zip 包的方式。之前,遇到特殊需求的包go get通过版本控制的命令行工具去主流的代码托管网站下载,现在vgo 直接通过网站提供的 API 下载需要的包。模块统一通过zip包的形式提供可以让下载协议更简单,公司或者个人可以处于任何原因考虑(安全或者想要缓存副本防止源被删除)自己做下载代理,使用代理来确保可用性并且通过
go.mod定义了哪些代码需要用到,vendor目录也就不再需要了。go 命令
go 命令必须更新才能使用模块功能。一个重要的变化就是常用的构建命令,像
gobuild,go install,go run, 和go test将需要按指定需求解析对应的依赖关系了,在新模块中使用golang.org/x/text时只需在Go 源码中导入编译就可以了,无需单独关注版本问题。但是,最重要的变化还是终结了
GOPATH作为Go 代码工作空间的设置,由于go.mod文件包含了完整的模块路径并且还定义了每个使用的依赖的版本,因此包含go.mod文件的目录就可以被认为是一个目录树的根目录了,该目录树作用于自身的工作空间,并且和其他类似的目录彼此隔离。现在你只需git clone然后cd就可以直接撸代码了,不再需要GOPATH了下一步计划?
我还发布了一篇文章"vgo吧,开始在你的版本库中通过tag标记版本吧,创建并检入go.mod文件。注意如果你的资源库有一个空的
go.mod但是存在dep,glide,glock,godep,godeps,govend,govendor或者gvt配置文件的话,vgo将会使用它们填充go.mod文件。原文:https://research.swtch.com/vg...
有疑问加站长微信联系(非本文作者)
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
关注微信- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码` - 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传
收入到我管理的专栏 新建专栏
简评:对于Go来说一直以来依赖包的版本控制上没有一个好的方案,尽管社区诞生了不下十余个解决该问题的工具,但一直以来没有一个官方的支持。这个提案有望在Go
的下个版本中看到官方的包版本控制了,去除了GOPATH依赖,同时还引入了module 的概念,真正意义上实现了重编译,可谓一次大的变更编译:缪斯的情人
是时候为 Go 添加包版本控制了!
更确切地说,我们需要把包版本的概念普及到Go 开发者和工具常用词汇中,以便在后续的相互沟通时能准确一致的表达哪些程序代码需要编译、运行和解析。同样,go 命令也需要准确的告诉开发者在编译中使用了哪个包的哪个版本。
版本控制可以让我们能够实现重编译。当我让你试用我程序最新版本时,我清楚的知道你不仅仅获取到的是我最新程序的代码,还包括我代码所依赖的相同版本的包,这样才能编译出完全一样的二进制包。
版本控制还能让我们不同阶段保持同样的编译方式,即使我们的依赖包可能有新版本了,只要我们的配置未允许使用,go 命令也不会使用新版本的包。
尽管添加版本控制是必须的功能,但同时我们也不能失去go 命令行现有的优秀特性:简单、高效、易懂。目前来看,很多程序员还是不太重视版本控制,主要是大部分情况下也都没出现什么问题。试想如果我们有一个合理模式的设计和默认配置,让开发者不需要太多关注版本控制的东西,程序仍然能很好的工作,并且对现有工程影响较小,发布新版本足够简单,甚至日常开发中可以忽略掉版本控制的工作,这样的版本控制模式才是我们想要的。
简而言之,版本控制是必须的,但是应该足够透明不能破坏掉go get 本身功能。这篇文章探讨了一个能完全实现这些的提案,并且提供了一个现在可用的原型demo,希望这能为集成到go命令奠定基础。我打算通过这篇文章讨论下在产品化过程中哪些是需要做的,哪些是不需要做的,基于这个讨论,我将对提案和原型做进一步调整,并且提交一个官方提案,作为可选功能集成到Go 1.11版本中。
这个提案保留了go get的精华部分,增加了重复构建,采用了语义化的版本控制,弃用了vendor,废弃了基础工程创建时依赖GOPATH,并且提供了老项目平滑迁移的方式,目前这个提案还处于初级阶段,如果细节上有问题,我们会在Go主版本发布前修复掉
背景
在我们讨论这个提案之前,先了解下当下的现状。讲起来可能会很长,但历史的教训对现在有很重要的参考意义,并且让我们能够清楚的知道这个提案改变了什么。如果你觉得没意思,那么可以直接跳到demo
Makefiles, goinstall, 和 go get
2009年11月,Go 发布了带有编译器、链接器和一些内置库的初代版本,在当时你必须通过运行6g 和6l 来编译和链接你的程序,还包含一些简单的makefiles。大多数情况下,通过简单包装的gobuild可以编译一个单包程序并且生成对应的makefile。在当时,也没有一个合适的方式把代码分享给其他人。尽管还有很多功能没提供,但是仍然发布了,并且Go 计划将一些剩余的功能放到社区去做。
2010年2月,我们介绍了go命令中使用 go get 来替换goinstall 。
总的来说,go get 是变革性的,它让 Go 开发者能够共享代码和相互构建,并且通过工具隔离了 go 命令编译系统中的细节,但是,go get 缺少版本控制的概念,实际上在goinstall第一次讨论中就清楚的意识到需要版本控制相关的功能。不幸的是,至少在当时我们的 Go 团队中还不清楚应该怎么去做。当go get需要一个包时,总是从像 Git 或者 Mercurial 这样的远程版本控制系统中下载最新的副本,包版本管理上的缺失至少导致了两个重大的缺陷。
Versioning 和 API 的稳定性
首先在没有版本控制时go get 的一个重大缺陷是对于给定的更新无法知道是否是用户所期望的。
在2013年11月,Go 1.2 添加了一个发关于包版本控制基本建议的FAQ:
公共发布的包应该保持向后兼容性。gopkg.in,倡导"Go语言稳定性API"。这个网站其实是个感知 GitHub 版本变化的重定向器,你可以通过gopkg.in/yaml.v1和gopkg.in/yaml.v2的这样的导入方式来指向Git库的不同提交版本(也可能在不同分支)。按照这个思路,依赖库的功能有了重大改变后你可以把之前的 v1 版本导入路径作为备用,然后创建一个新版本 v2,通过v2 导入路径引入完全不同的API。2015年8月,Dave Cheney 提出了Glide in the Sea of Go Package Managers" 比较了godep和很多后来出现的工具,其中最为突出的是
glide。2015年4月,Dave Cheney 介绍了dep,相关背景信息请查看Sam 在2016年2月发布的文章"The Saga of Go Dependency Management" 以及2017年7月GopherCon 的分享"examples)。
Goinstall和旧的go get通过像git 和hg 这样的版本控制工具直接下载代码,这种方式存在很多问题,其中包括碎片化严重:用户如果没有bzr就没法下载托管在Bazaar 资源库的代码。相比之下,模块则是通过HTTP 下载zip 包的方式。之前,遇到特殊需求的包go get通过版本控制的命令行工具去主流的代码托管网站下载,现在vgo 直接通过网站提供的 API 下载需要的包。模块统一通过zip包的形式提供可以让下载协议更简单,公司或者个人可以处于任何原因考虑(安全或者想要缓存副本防止源被删除)自己做下载代理,使用代理来确保可用性并且通过
go.mod定义了哪些代码需要用到,vendor目录也就不再需要了。go 命令
go 命令必须更新才能使用模块功能。一个重要的变化就是常用的构建命令,像
gobuild,go install,go run, 和go test将需要按指定需求解析对应的依赖关系了,在新模块中使用golang.org/x/text时只需在Go 源码中导入编译就可以了,无需单独关注版本问题。但是,最重要的变化还是终结了
GOPATH作为Go 代码工作空间的设置,由于go.mod文件包含了完整的模块路径并且还定义了每个使用的依赖的版本,因此包含go.mod文件的目录就可以被认为是一个目录树的根目录了,该目录树作用于自身的工作空间,并且和其他类似的目录彼此隔离。现在你只需git clone然后cd就可以直接撸代码了,不再需要GOPATH了下一步计划?
我还发布了一篇文章"vgo吧,开始在你的版本库中通过tag标记版本吧,创建并检入go.mod文件。注意如果你的资源库有一个空的
go.mod但是存在dep,glide,glock,godep,godeps,govend,govendor或者gvt配置文件的话,vgo将会使用它们填充go.mod文件。原文:https://research.swtch.com/vg...