go.dev 是 go 官方团队于 2019 年 11 月上线的集合 go 开发资源的网站,包括一些学习课程和一些 go 的案例,当然最重要的就提供了 go 的第三方包的检索功能。没错,他就是用来取代原来的godoc.org的,现在 godoc.org 上也有提示提醒用户迁移到 pkg.go.dev。在这篇文章中,我们将把 go module 模块发布到 pkg.go.dev。
这次要发布的代码放在 github,所以新建一个项目叫 how-to-release-go-module 新增 hello.go 文件 为 hello.go 添加两个方法和相关注释
package pkg
import “fmt”
// Hello says hello.
func Hello() {
fmt.Println(“Hello go mod!”)
}
// Bye says bye.
func Bye() {
fmt.Println(“Bye go mod!”)
}
执行 go mod init 生成go.mod文件
go mod init
go: creating new go.mod: module github.com/YouEclipse/how-to-release-go-module
内容如下:
module github.com/YouEclipse/how-to-release-go-module
go 1.13
我们把代码 push 到远端分支,看起来好像第一个版本就发布完毕了。我们打开pkg.go.dev搜索一下github.com/YouEclipse/how-to-release-go-module这个包,却返回未找到这个包。这是为何?
其实在 go.dev 的about中说的很清楚了,只有通过proxy.golang.org下载包的时候,module 才会自动同步到 pkg.go.dev。
To add a package or module, simply fetch it from proxy.golang.org.
但是,实际上,proxy.golang.org 国内基本上是无法访问的,如果我们使用 goproxy.cn,也一样能够同步,我没有研究 goproxy 的源码,但是我和 goproxy 的作者确认过,goproxy 的推算行为会用到 proxy.golang.org,所以使用 goproxy.cn 作为代理也是可行的。
我们可以随便建一个项目,执行go get -u github.com/YouEclipse/how-to-release-go-module,因为 go1.13 go module 已经是默认打开的,所以会默认通过 proxy.golang.org 拉取。如果不确定是否配置 go proxy,可以执行go env和go env -w 命令查看和修改。
go get -u github.com/YouEclipse/how-to-release-go-module
go: finding github.com/YouEclipse/how-to-release-go-module latest
go: downloading github.com/YouEclipse/how-to-release-go-module v0.0.0-20200219150525-4f41ffd1dd8f
go: extracting github.com/YouEclipse/how-to-release-go-module v0.0.0-20200219150525-4f41ffd1dd8f
当我们成功拉取后,可以在 pkg.go.dev 再次搜索(具体可能需要等一段时间,大约是十分钟到半小时的样子),这时候我们可以看到搜索结果了
看起来似乎我们的第一次发布大功告成了。我们看看 pkg.go.dev 包含了的什么信息:
版本号:由 go module 自动生成
发布时间
开源协议
commit Hash
tag:latest
Overview(概览)
包名
源码地址
README
Doc: godoc 文档
Subdirectories: 子目录
Versions:已经发布过的版本
Imports:引用的包
Imported By: 引用此包的 moudule
Licenses:开源许可证
…
到了这里我们会发现,godoc 和 module 的 README 都没有正常显示,提示 “Doc” not displayed due to license restrictions.和README not displayed due to license restrictions,是说我们的包没有开源许可证,所以无法显示。对于这一点,网上有资料提到 go 官方团队和他们的律师讨论过才做的这个决定,这也是可以理解的。
pkg.go.dev 支持的证书可以在https://pkg.go.dev/license-policy查看,我们只要选择合适的开源协议证书添加到项目中即可(很遗憾,WTFPL 并不在支持的开源协议中)。这里我们选择 Apache2.0 协议,添加到项目中,并 push 到远端分支。
在等待一段时间(这里我等了大约 30 分钟)pkg.go.dev 更新后,我们会发现 README 和 doc 都可以正常显示了。这里生成的 doc 和 godoc.org 没太大的区别,都是基于代码和注释生成的,网上也有很多关于生成 godoc 最佳实践的文章,这里不做赘述。
至此,发布的 module 包有 godoc 文档,有开源许可证,看起来是这么个样子,第一个版本至此就算发布完了。
发布新版本
其实我们在发布第一个版本的时候,为了更新 license 发布了两次,但是两次的版本都是 v0.0.0,这么看起来似乎和 go modules 版本化的理念背道而驰。go module 实际上是可以通过 tag 来发布版本的。当我们需要发布新版本时,对应的,我们需要使用git tag为这个版本打上标签。假设我们发布的下个版本是 v1.0.0:
git tag v1.0.0
打上标签后 push 到远端分支,等待一段时间,我们就可以在 pkg.go.dev 上看到我们发布的 v1.0.0 版本了。
(这里有些奇怪,之前的 v0.0.0 消失了,不知道什么原因)
我们执行go get -u github.com/YouEclipse/how-to-release-go-module 即可获取最新发布的版本
go get -u github.com/YouEclipse/how-to-release-go-module
go: finding github.com/YouEclipse/how-to-release-go-module v1.0.0
go: downloading github.com/YouEclipse/how-to-release-go-module v1.0.0
go: extracting github.com/YouEclipse/how-to-release-go-module v1.0.0
发布 breaking changes
在早期没有 go module 时,假设我们引用的第三方包的做了 breaking changes,API 发生改变,在跑 CI 或者重新拉取第三方包后,代码将会编译失败。我印象比较深刻的是在 2018 年左右,go.uuid 的 API 发生了 breaking changes,将原来没有返回 err 的函数返回了 err,而当时我们没有任何包管理,都是在 docker 镜像更新时通过 go get 拉取,这就导致当时我们的 CI 都跑失败了。
go 官方其实有关于版本控制的最佳实践,叫做Semantic Import Version,即语义化版本。 关于语义化版本的说明,附录中官方的 Wiki 也有介绍,这里我们按照官方的最佳实践执行。 这里强调一下 Go 官方对于语义化版本的一个基本原则:
If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.” 如果旧软件包和新软件包具有相同的导入路径,则新软件包必须与旧软件包向后兼容
所以 Go 官方给了两个方案,针对进行大版本升级和 breaking changes:
Major branch 即通过创建 version 分支和 tag 进行版本升级
Major subdirectory 即通过创建 version 子目录来区分不同版本
这里我们只使用 Major branch 方案来发布,因为第二种方案看起来很奇怪,而且似乎背离了 VCS 的意义,所有的版本代码居然都在一块,个人不是很推荐。
我们修改原来的代码,并做出 breaking changes.
package pkg
import “fmt”
// Hello says hello.
func Hello() error {
fmt.Println(“Hello go mod!”)
return nil
}
// Bye says bye.
func Bye() error {
fmt.Println(“Bye go mod!”)
return nil
}
并将 import path 改为 v2
module github.com/YouEclipse/how-to-release-go-module/v2
go 1.13
然后提交代码,并创建 tag
git tag v2.0.0
git push –tags
git push master
在又等待了一段时间后 (pkg.go.dev 更新确实是有点慢),可以看到 v2 版本已经发布了,
这时候我们尝试在之前的测试项目拉取更新
go get -u github.com/YouEclipse/how-to-release-go-module
可以看到 go.mod 中显示我们使用的依然是 v1.0.0,显然,v1.0.0 版本的用户并没有受到 breaking changes 的影响。
如果我们要使用 v2.0.0 版本,修改 go get 的路径为github.com/YouEclipse/how-to-release-go-module/v2即可。
至此,我们的 breaking changes 版本的发布也完成了。
添加 go dev badge
大部分的开源的项目我们都可以在 README 中看到各种小图标,标识着项目的各种状态,一般称之为 badge。在 pkg.go.dev 之前,大部分的 go 项目都会添加 godoc.org 的 badge 引导开发者们去 godoc.org 查看文档,但是既然使用了 pkg.go.dev,我们自然就应该添加 go.dev 的 badge。更多的 badge 和相关设置可以在shields.io查看。
添加 badge 的方法和 markdow 添加图片的方法一样,只要替换项目在 pkg.go.dev 的路径即可
比如github.com/YouEclipse/how-to-release-go-module/v2 这个项目就可以设置成
go.dev reference
这样,我们的 go module 模块看起来就很完美了。
结语
本文从一个简单的例子基本覆盖了发布 go module 模块到 pkg.go.dev 可能会遇到的场景,希望能给阅读此文章的开发者提供帮助。
附录
1 Go modules Wiki
2 Go 夜读第 61 期 Go Modules、Go Module Proxy 和 goproxy.cn
3 go.dev: serve status badge similar to godoc.org
4 示例项目 how-to-release-go-module
5 Go module 机制下升级 major 版本号的实践
https://gocn.vip/topics/9829
些 go 的案例,当然最重要的就提供了 go 的第三方包的检索功能。没错,他就是用来取代原来的godoc.org的,现在 godoc.org 上也有提示提醒用户迁移到 pkg.go.dev。在这篇文章中,我们将把 go module 模块发布到 pkg.go.dev。
BTW,在我写这篇文章的时候(2020.02.19),go 官方刚好也宣布了 go.dev 不久将开源。
发布第一个版本
这次要发布的代码放在 github,所以新建一个项目叫 how-to-release-go-module 新增 hello.go 文件 为 hello.go 添加两个方法和相关注释
package pkg
import “fmt”
// Hello says hello.
func Hello() {
fmt.Println(“Hello go mod!”)
}
// Bye says bye.
func Bye() {
fmt.Println(“Bye go mod!”)
}
执行 go mod init 生成go.mod文件
go mod init
go: creating new go.mod: module github.com/YouEclipse/how-to-release-go-module
内容如下:
module github.com/YouEclipse/how-to-release-go-module
go 1.13
我们把代码 push 到远端分支,看起来好像第一个版本就发布完毕了。我们打开pkg.go.dev搜索一下github.com/YouEclipse/how-to-release-go-module这个包,却返回未找到这个包。这是为何?
其实在 go.dev 的about中说的很清楚了,只有通过proxy.golang.org下载包的时候,module 才会自动同步到 pkg.go.dev。
To add a package or module, simply fetch it from proxy.golang.org.
但是,实际上,proxy.golang.org 国内基本上是无法访问的,如果我们使用 goproxy.cn,也一样能够同步,我没有研究 goproxy 的源码,但是我和 goproxy 的作者确认过,goproxy 的推算行为会用到 proxy.golang.org,所以使用 goproxy.cn 作为代理也是可行的。
我们可以随便建一个项目,执行go get -u github.com/YouEclipse/how-to-release-go-module,因为 go1.13 go module 已经是默认打开的,所以会默认通过 proxy.golang.org 拉取。如果不确定是否配置 go proxy,可以执行go env和go env -w 命令查看和修改。
go get -u github.com/YouEclipse/how-to-release-go-module
go: finding github.com/YouEclipse/how-to-release-go-module latest
go: downloading github.com/YouEclipse/how-to-release-go-module v0.0.0-20200219150525-4f41ffd1dd8f
go: extracting github.com/YouEclipse/how-to-release-go-module v0.0.0-20200219150525-4f41ffd1dd8f
当我们成功拉取后,可以在 pkg.go.dev 再次搜索(具体可能需要等一段时间,大约是十分钟到半小时的样子),这时候我们可以看到搜索结果了
看起来似乎我们的第一次发布大功告成了。我们看看 pkg.go.dev 包含了的什么信息:
版本号:由 go module 自动生成
发布时间
开源协议
commit Hash
tag:latest
Overview(概览)
包名
源码地址
README
Doc: godoc 文档
Subdirectories: 子目录
Versions:已经发布过的版本
Imports:引用的包
Imported By: 引用此包的 moudule
Licenses:开源许可证
…
到了这里我们会发现,godoc 和 module 的 README 都没有正常显示,提示 “Doc” not displayed due to license restrictions.和README not displayed due to license restrictions,是说我们的包没有开源许可证,所以无法显示。对于这一点,网上有资料提到 go 官方团队和他们的律师讨论过才做的这个决定,这也是可以理解的。
pkg.go.dev 支持的证书可以在https://pkg.go.dev/license-policy查看,我们只要选择合适的开源协议证书添加到项目中即可(很遗憾,WTFPL 并不在支持的开源协议中)。这里我们选择 Apache2.0 协议,添加到项目中,并 push 到远端分支。
在等待一段时间(这里我等了大约 30 分钟)pkg.go.dev 更新后,我们会发现 README 和 doc 都可以正常显示了。这里生成的 doc 和 godoc.org 没太大的区别,都是基于代码和注释生成的,网上也有很多关于生成 godoc 最佳实践的文章,这里不做赘述。
至此,发布的 module 包有 godoc 文档,有开源许可证,看起来是这么个样子,第一个版本至此就算发布完了。
发布新版本
其实我们在发布第一个版本的时候,为了更新 license 发布了两次,但是两次的版本都是 v0.0.0,这么看起来似乎和 go modules 版本化的理念背道而驰。go module 实际上是可以通过 tag 来发布版本的。当我们需要发布新版本时,对应的,我们需要使用git tag为这个版本打上标签。假设我们发布的下个版本是 v1.0.0:
git tag v1.0.0
打上标签后 push 到远端分支,等待一段时间,我们就可以在 pkg.go.dev 上看到我们发布的 v1.0.0 版本了。
(这里有些奇怪,之前的 v0.0.0 消失了,不知道什么原因)
我们执行go get -u github.com/YouEclipse/how-to-release-go-module 即可获取最新发布的版本
go get -u github.com/YouEclipse/how-to-release-go-module
go: finding github.com/YouEclipse/how-to-release-go-module v1.0.0
go: downloading github.com/YouEclipse/how-to-release-go-module v1.0.0
go: extracting github.com/YouEclipse/how-to-release-go-module v1.0.0
发布 breaking changes
在早期没有 go module 时,假设我们引用的第三方包的做了 breaking changes,API 发生改变,在跑 CI 或者重新拉取第三方包后,代码将会编译失败。我印象比较深刻的是在 2018 年左右,go.uuid 的 API 发生了 breaking changes,将原来没有返回 err 的函数返回了 err,而当时我们没有任何包管理,都是在 docker 镜像更新时通过 go get 拉取,这就导致当时我们的 CI 都跑失败了。
go 官方其实有关于版本控制的最佳实践,叫做Semantic Import Version,即语义化版本。 关于语义化版本的说明,附录中官方的 Wiki 也有介绍,这里我们按照官方的最佳实践执行。 这里强调一下 Go 官方对于语义化版本的一个基本原则:
If an old package and a new package have the same import path, the new package must be backwards compatible with the old package.” 如果旧软件包和新软件包具有相同的导入路径,则新软件包必须与旧软件包向后兼容
所以 Go 官方给了两个方案,针对进行大版本升级和 breaking changes:
Major branch 即通过创建 version 分支和 tag 进行版本升级
Major subdirectory 即通过创建 version 子目录来区分不同版本
这里我们只使用 Major branch 方案来发布,因为第二种方案看起来很奇怪,而且似乎背离了 VCS 的意义,所有的版本代码居然都在一块,个人不是很推荐。
我们修改原来的代码,并做出 breaking changes.
package pkg
import “fmt”
// Hello says hello.
func Hello() error {
fmt.Println(“Hello go mod!”)
return nil
}
// Bye says bye.
func Bye() error {
fmt.Println(“Bye go mod!”)
return nil
}
并将 import path 改为 v2
module github.com/YouEclipse/how-to-release-go-module/v2
go 1.13
然后提交代码,并创建 tag
git tag v2.0.0
git push –tags
git push master
在又等待了一段时间后 (pkg.go.dev 更新确实是有点慢),可以看到 v2 版本已经发布了,
这时候我们尝试在之前的测试项目拉取更新
go get -u github.com/YouEclipse/how-to-release-go-module
可以看到 go.mod 中显示我们使用的依然是 v1.0.0,显然,v1.0.0 版本的用户并没有受到 breaking changes 的影响。
如果我们要使用 v2.0.0 版本,修改 go get 的路径为github.com/YouEclipse/how-to-release-go-module/v2即可。
至此,我们的 breaking changes 版本的发布也完成了。
添加 go dev badge
大部分的开源的项目我们都可以在 README 中看到各种小图标,标识着项目的各种状态,一般称之为 badge。在 pkg.go.dev 之前,大部分的 go 项目都会添加 godoc.org 的 badge 引导开发者们去 godoc.org 查看文档,但是既然使用了 pkg.go.dev,我们自然就应该添加 go.dev 的 badge。更多的 badge 和相关设置可以在shields.io查看。
添加 badge 的方法和 markdow 添加图片的方法一样,只要替换项目在 pkg.go.dev 的路径即可
比如github.com/YouEclipse/how-to-release-go-module/v2 这个项目就可以设置成
go.dev reference
这样,我们的 go module 模块看起来就很完美了。