【Go 语言学习笔记】仓库、模块、包和版本

发表于 2023-06-14

为什么需要模块、包和版本

有过开发经验的人都知道,在大型项目中存在大量代码又是多人开发,如果没有合理的管理方式,就会因为混乱的代码导致项目的开发和维护极其困难。

而常规的管理方式就是:模块、包和版本,从 1.12 开始,Go 有了 Modules 包管理工具。

仓库、模块、包

Go 模块其实就是为了方便管理 Go 代码,也是为了方便不同开发者共享代码,避免重复造轮子。

为了共享和管理,比较重要的就是仓库和模块,仓库表示代码的存储服务,而模块就是一个共享的代码集合。

包是处于同一目录中的一些源代码文件的集合,这些文件将被编译在一起。

可以简单理解为模块内的子目录,虽然不太准确,大概是目录的意思,就是分类整合 Go 代码的管理层级。

包也有路径,并且其全路径就是:模块路径 + 包相对模块的路径

例如模块 devnolo/module 有一个子目录为 dir,一般这个 dir 就是该模块的一个包存在,包含管理了一些列源代码。

模块

模块由一系列已发布、版本化、被分发的包构成。

有一个特殊的模块(main)是指包含了 go 命令的目录所对应的模块。

模块根目录是指包含 go.mod 文件的目录。例如 go.mod 文件在目录 /workspace/project 里,在这个例子里根目录就是 /workspace/project

模块路径

模块的唯一标识:模块路径,模块路径在 go.mod 文件中声明。

完整的模块路径包含了两部分:仓库地址 + 模块地址。

例如:“github.com/用户名/项目名” 通过该路径可以定位到代码仓库 ---> https://github.com/用户名/项目名

仓库

仓库(repository)是存放模块的服务,就是熟知的代码仓库。

开发者可以共享自己的模块到代码仓库,使用者可以通过代码仓库使用他人共享的模块。

版本

版本(version)指模块的不同时刻的模块快照,开发人员根据可以版本定位到唯一的模块。

意味着只要能确定版本,任何人任何时刻根据这个版本号获取到的模块都是一摸一样的。

版本号

一个版本标示着模块的不可变快照,每个版本都以字母 v 开头,后紧跟着语义版本,例如:v2.3.4。

主要版本号

在发布不兼容的公共接口更新后,例如模块里的某个包被删除,必须递增,并且将次要和补丁版本设置为零。

例如版本 v2.28.18 发生不兼容发布,版本号将递增为 v3.0.0。

次要版本号

在发布向后兼容的更新后,例如添加新函数后,必须递增且补丁版本设置为零。

例如版本 v2.28.18 发生不兼容发布,版本号将递增为 v2.29.0。

补丁版本号

在不修改到公共接口的情况下,例如 Bug 修复或者做了一些优化,必须递增。

预发版本

-pre 后缀意味着某个版本的预发版,在指定版本的后面添加。

例如在 v1.2.3 之前,会先发布 v1.2.3-pre 预发版,完全确定后,才会正式发布 v1.2.3。

元数据

使用 + 符号连接元数据,例如 +incompatible 表示在迁移到模块版本主版本 2 或更高版本之前发布的版本。

构建元数据会在版本比较时被忽略。附带构建元数据的标识符会在版本库中被忽略,然而在 go.mod 文件中会被保留。

伪版本

伪版本是一种特殊格式,主要是对版本控制存储库(Git)中特定修订的信息进行编码。

多用于开发过程中,并没有正式发布的时候就会使用伪版本打包,例如:v0.0.0-20191109021931-daa7c04131f5。

每个伪版本有三个部分:

  • 基础版本前缀,它派生自修订之前的语义版本标记(vX.0.0)。
  • 时间戳,即创建修订的 UTC 时间(yyyymmddhhmmss)。在 Git 中,就是提交时间。
  • 修订版标识符,它是提交散列的 12 个字符的前缀(daa7c04131f5)。

主版本后缀

从第 2 个版本开始,模块路径必须有个类似 /v2 的后缀,以此来匹配主版本。

就是 v0.x.x 或 v1.x.x 没有主版本后缀。因为 v0.x.x 版本不稳定且没有兼容性保证,v1.x.x 版本与最后一个 v0.x.x 版本向后兼容。

例如一个模块在版本 v1.x.x 的模块路径为 example.com/model,那么它在 v2.x.x 的模块路径将是 example.com/model/v2。

模块下载

关于模块的下载就不得不提到 GOPROXY 代理,如果不用代理下载就极其缓慢。

通过代理就可以访问到代理服务缓存的模块,实现加速下载。

使用代理也带来了新的问题,只能下载代理服务上有的模块。这个时候可以通过配置 GOPRIVATE 指定仓库不走代理。

例如

我使用自己的 GitLab 管理我的模块代码,定义模块路径:gitlab.devnolo.com/liqingsong/go-modules-demo,其仓库地址是:https://gitlab.devnolo.com/liqingsong/go-modules-demo。并创建相应的 Tag 或者 GitHub 上创建 release ===> v0.1.0。

模块路径和仓库地址是按约定的存在,换种说法:仓库地址去掉网络协议就是模块路径。

在需要引用模块的电脑上配置 GOPRIVATE。

go env -w GOPRIVATE="gitlab.devnolo.com/*"

在模块的文件 go.mod 文件中声明需要引用模块的模块路径。

require gitlab.devnolo.com/liqingsong/go-modules-demo v0.1.0

通过模块路径 gitlab.devnolo.com/liqingsong/go-modules-demo 识别出仓库地址 https://gitlab.devnolo.com。

再拼接剩余路径,就可获得完整的仓库地址。