研究!rsc公司

关于编程的想法和链接,通过

RSS公司

定义Go模块
发布于2018年2月22日星期四。PDF格式

正如在概述帖子,a开始模块是作为一个单元进行版本控制的包的集合,以及go.mod公司列出其他必需模块的文件。迁移到模块是我们重新访问和修复的机会关于如何命令管理源代码。当前 得到当我们让它退役,取而代之的是模块。我们需要确保模块设计能为我们服务在接下来的十年里。特别地:

这篇文章介绍了vgo公司设计该地址这些问题。这里的一切都是初步的:我们将改变设计如果我们发现这是不对的。

版本发布

抽象边界使项目具有规模。最初,所有Go包都可以由所有其他Go包导入。我们介绍了内部的目录约定在Go 1.4中消除以下问题选择将程序结构为多个包的开发人员不得不担心其他用户的导入和依赖的详细信息helper包从来都不是供公众使用的。

Go社区现在也有类似的可见性问题存储库提交。今天,用户识别软件包版本是很常见的通过提交标识符(通常是Git哈希),其结果是,开发人员按照序列进行工作提交的人需要担心,至少在他们的内心深处,关于用户绑定到任何这些提交,这又一次不是供公众使用的。我们需要改变Go开源社区的期望,以建立一个规范,即作者标记发布,用户更喜欢这些发布。

我不认为用户应该从版本中选择这一点由作者发布,而不是挑选单个提交从Git的历史来看,尤其有争议。困难的部分是改变规范。我们需要让作者轻松标记提交并且用户很容易使用这些标签。

目前,作者共享代码的最常见方式是在代码托管网站上,尤其是GitHub。对于GitHub上的代码,所有作者需要做的就是标记提交并按下标签。我们还计划提供一种工具,可能称为 释放,比较模块的不同版本在类型级别实现API兼容性,以捕获无意中断类型系统中可见的更改,并帮助作者在发布应该是次要版本(因为它添加了新的API或更改了许多行代码)或者只发布补丁。

对于用户,vgo公司它完全按照标记的版本运行。然而,我们知道,至少在从旧做法过渡到新做法的过程中,也许可以无限期地引导新项目,需要一个转义舱口,以便指定提交。这可能发生在vgo公司,但其设计目的是让用户更喜欢显式标记的版本。

明确地,vgo公司理解特殊的伪版本0.0.0版-yyyymmddhhmmss年/月/日-犯罪指的是给定的提交标识符,通常是缩短的Git散列并且必须具有与(UTC)时间戳匹配的提交时间。此表单是有效的语义版本字符串对于v0.0.0的预发行版。例如,这对戈普格·托姆诗节:

[[项目]]name=“google.golang.org/appengine”包=[“内部”,“内部/底座”,“内部/数据存储”,“内部/日志”,“内部/远程api”,“内部/urlfetch”,“URL提取”]revision=“150dc57a1b433e64154302bdc40b6bb8aefa313a”版本=“v1.0.0”[[项目]]分支=“主”name=“github.com/google/go-github”packages=[“github”]修订版=“922ceac0585d40f97d283d921f872fc50480e06e”

与这些相对应go.mod公司线:

要求(“google.golang.org/appengine”v1.0.0“github.com/google/go-github”v0.0.0-20180116225909-922ceac0585d)

选择伪版本形式,以便标准semver优先规则按提交时间比较两个伪版本,因为时间戳编码使字符串比较与时间比较相匹配。该表格还确保vgo公司总是喜欢标记语义版本通过未标记的伪版本,因为即使v0.0.1非常旧,它也比任何v0.0.0预发行版具有更高的semver优先级。(还请注意,这与dep(深度)添加新的依赖于项目。)当然,伪版本字符串很难处理:他们在go.mod公司文件和vgo公司 列表 -米输出。所有这些不便都有助于鼓励作者和用户更喜欢显式标记的版本,有点像必须写作的额外步骤进口 “不安全”鼓励开发人员更喜欢编写安全代码。

这个go.mod公司文件

模块版本由源文件树定义。这个go.mod公司文件描述了模块并指示了根目录。什么时候?vgo公司在目录中运行,它将在当前目录,然后依次查找父目录go.mod公司标记根。

文件格式是面向行的//仅限评论。每行包含一个指令,这是一个动词(模块,要求,排除,或代替,定义如下最低版本选择),后跟参数:

模块“my/thing”需要“其他/事物”v1.0.2需要“新/事物”v2.3.4排除“旧/事物”v1.2.3替换“bad/thing”v1.4.5=>“good/thing“v1.4.5

前导动词可以从相邻行中去掉,导致一个块,如Go导入中:

要求(“新/事物”v2.3.4“旧/事物”v1.2.3)

我的文件格式目标是:(1)清晰、简单,(2) 便于人们阅读、编辑、操作和比较,(3) 对于像这样的程序来说很容易vgo公司读取、修改和回写,保留评论和一般结构,(4) 有有限的未来增长空间。我看过JSON、TOML、XML和YAML,但都没有似乎同时拥有这四种属性。例如Gopkg.toml公司在上面为每个需求引出三条线,使它们更难浏览、分类和区分。相反,我设计了一个最小格式的怀旧在围棋项目中名列前茅,但希望还不够接近令人困惑。我修改了现有的注释友好型解析器。

最终命令集成可能会更改文件格式,甚至可能采用更标准的帧,但为了兼容性,我们将保留阅读今天的go.mod公司文件,就像vgo公司也可以从中读取需求信息GLOCKFILE文件,Godeps/Godeps.json公司,Gopkg.lock公司,依赖关系.tsv,滑动锁定,供应商.conf,供应商.yml,供应商/清单,供应商/vendor.json文件夹。

从存储库到模块

开发人员在版本控制系统中工作,而且很清楚vgo公司必须尽可能简单。期望开发人员做好准备是不合理的例如,模块将自己存档。相反,vgo公司便于导出模块直接从任何版本控制存储库遵循一些基本的、不引人注目的约定。

首先,创建一个存储库就足够了并使用semver-formatted标记标记commit,如版本0.1.0.领先者v(v)是必需的,并且还需要有三个数字。尽管vgo公司它本身接受速记v0.1版本关于命令线,标准形式版本0.1.0必须使用在存储库标记中,以避免歧义。只需要标签。为了使用未使用vgo公司,go.mod公司此时,文件不是严格要求的。创建新的标记提交将创建新的模块版本。容易的。

当开发人员达到v2时,语义导入版本控制意味着/第2版/已添加到导入路径在模块根前缀的末尾:我的/thing/v2/sub/pkg.正如更早的职位,但它仍与现有工具不同。认识到这一点,vgo公司不会使用任何v2或更高版本的标记在源代码存储库中,而不首先检查有一个go.mod公司模块路径声明以主要版本(例如,模块 “我的/thing/v2”).Vgo公司使用该声明作为作者的证据正在使用语义导入版本控制命名该模块中的包。这对于多包装模块尤其重要,因为模块中的导入路径必须包含/第2版/元素以避免引用回v1模块。

我们预计大多数开发人员都会选择跟随通常的“主要分支”惯例,其中不同的主要版本生活在不同的分支中。在这种情况下,v2分支中的根目录将有一个go.mod公司表示v2,如下所示:

这大致是大多数开发人员已经开始工作的方式。在图片中,v1.0.0标记指向一个早于提交的提交vgo公司.它没有go.mod公司文件,这很好。在标记为v1.0.1的提交中,作者添加了go.mod公司文件上面写着模块 “我的/我的东西”.然而,在提交之后,作者分叉一个新的v2开发分支。除了提示v2的任何代码更改之外(包括更换酒吧具有夸克斯),这个go.mod公司在新的分支中更新说吧模块 “我的/东西/v2”.然后,分支可以独立进行。事实上,vgo公司真的对分支一无所知。它只是将标记解析为提交,然后看起来go.mod公司提交中的文件。再一次go.mod公司v2及更高版本需要文件以便vgo公司可以使用模块线作为代码编写完成的标志考虑到语义导入版本化,所以进口foo公司我的/thing/v2/foo/quux,我的/thing/foo/quux.

作为替代方案,vgo公司还支持“主子目录”约定,其中v1以上的主要版本在子目录中开发:

在这种情况下,v2.0.0不是通过分叉整个树来创建的复制到单独的分支中,但要将其复制到子目录中。再次go.mod公司必须更新以表明“我的/thing/v2”.之后,指向提交的v1.x.x标记会寻址文件在根目录中,不包括第2版/,而指向提交的v2.x.x标记寻址文件在中第2版/仅子目录。这个go.mod公司文件允许vgo公司区分这两种情况。拥有v1.x.x和v2.x.x标签也很有意义指向同一提交:他们会处理提交的不同子树。

我们预计开发人员可能会对选择一种约定或另一种约定。不是偏袒一方,vgo公司支持两者。注意,对于v2以上的主要版本,主要子目录方法可以为用户提供更优雅的过渡 得到.另一方面dep(深度)或供应商工具应该能够使用任一约定使用存储库。当然,我们会确保dep(深度)可以。

多模块存储库

开发人员可能还发现维护单个源代码存储库中的模块。我们想要vgo公司以支持这种可能性。总的来说,在差异程度上已经存在很大差异开发人员、团队、项目和公司申请源代码管理,我们认为这对施加一个类似“一个存储库等于一个模块”的映射所有开发人员。这里有一些灵活性也会有所帮助vgo公司适应随着souce控制方面的最佳实践不断变化。

在主要子目录约定中,第2版/包含模块“我的/thing/v2”.一个自然的扩展是允许子目录未以主要版本命名。例如,我们可以添加蓝色/子目录包含模块的“我的/东西/蓝色”,由确认蓝色/go.mod具有该模块路径的文件。在这种情况下,源代码管理提交标记寻址该模块会采取这种形式蓝色/v1.x.x.类似地,标签蓝色/v2.x.x将解决蓝色/v2/子目录。存在蓝色/go.mod文件不包括蓝色/从外部我的/东西模块。

在Go项目中,我们打算探索使用此约定来允许存储库,如golang.org/x/text定义倍数,独立模块。这让我们保留了粗粒度源代码控制的便利性但仍会在不同的时间将不同的子树提升到v1。

不推荐的版本

作者还需要能够否决某个版本,表示不应再使用它。这尚未在中实现vgo公司原型,但一种可行的方法是定义它在代码托管站点上,存在v1.0.0以上的标记已弃用(理想情况下,指向与v1.0.0相同的提交)将表示不赞成提交。当然,重要的是不要完全删除标签,因为这会破坏构建。弃用的模块将以某种方式突出显示在里面vgo公司 列表 -米 -u个输出(“显示我的模块和信息关于更新”),以便用户知道更新。

此外,因为程序可以访问自己的模块列表和版本,也可以配置程序根据某些选定的权限检查其自己的模块版本以及在运行弃用版本时以某种方式进行自我报告。这里的细节还没有解决,但这是一个很好的例子一旦开发人员和工具共享了描述版本。

发布

给定一个源代码管理存储库,开发人员需要能够以以下形式发布vgo公司可以消费。在一般情况下,我们将提供作者运行的命令将其源代码管理存储库转换为可以送达vgo公司任何能够提供静态文件的web服务器。与当前类似 得到,vgo公司需要一个带有<元>标签帮助将模块名转换为文件树用于该模块。例如,要查找swtch.com/testmod软件,的vgo公司命令获取常用页面:

$curl-sSL'https://swtch.com/testmod?go-得到=1'<!DOCTYPE html><meta name=“go import”content=“swtch.com/testmod modhttps://storage.googleapis.com/gomodules/rsc">这里没什么可看的。$

这个国防部服务器类型指示提供模块位于该基本URL的文件树中。相关文件位于storage.googleapis.com/gomodules/rsc在这个简单的例子中是:

这些URL的确切含义将在文章后面的“下载协议”部分中讨论。

代码托管站点

代码托管网站上进行了大量开发,我们希望这项工作能够融入vgo公司尽可能平稳。不是期望开发人员在其他地方发布模块,vgo公司将支持阅读所需信息直接从这些站点使用基于HTTP的API。一般来说,存档下载速度可能比现有版本控制签出。例如,在具有千兆位互联网连接的笔记本电脑上工作,下载蟑螂数据库源树作为GitHub中的zip文件但差不多四分钟后吉特 克隆它。网站只需要提供任何可以获取的表单的存档使用简单的HTTP GET。例如,Gerrit服务器仅支持下载gzipped tar文件。Vgo公司将下载的档案转换为标准格式。

最初的原型只支持GitHub和Go项目的Gerrit服务器,但我们也将增加对Bitbucket和其他主要托管网站的支持,在主Go工具链中发货之前。

通过轻量级存储库约定的组合,这与开发人员已经在做的工作基本相符,以及对已知代码托管站点的支持,我们预计大多数开源活动都不会受到迁移到模块,而不是简单地添加go.mod公司到每个存储库。

利用旧资源的公司 得到的直接使用吉特和其他源代码管理工具需要调整。也许编写一个用于这个vgo公司期望,但使用版本控制工具。然后,公司可以运行其中一个来生产类似于使用开源托管站点的体验。

模块档案

从存储库到模块的映射有点复杂,因为开发人员使用源代码管理的方式各不相同。最终目标是将所有这些复杂性映射下来Go模块的通用单一格式供代理或其他代码使用者使用(例如,godoc.org网站或任何代码检查工具)。

中的标准格式vgo公司原型是zip存档所有路径都从中开始模块路径和版本。例如,运行后vgo公司 得到属于rsc.io/报价v1.5.2,您可以在中找到zip文件vgo公司的下载缓存:

$unzip-l$GOPATH/src/v/cache/rsc.io/quote/@v/v1.52.zip1479 00-00-1980 00:00夏令时/报价@v1.5.2/许可证131 00-00-1980 00:00 rsc.io/报价@v1.5.2/自述.md240 00-00-1980 00:00 rsc.io/报价@v1.5.2/错误/错误测试.go55 00-00-1980 00:00 rsc.io/报价@v1.5.2/go.mod公司793 00-00-1980 00:00 rsc.io/报价@v1.5.2/报价go917 00-00-1980 00:00夏令时/报价@v1.5.2/报价_测试.go$

我使用zip是因为它指定得很好,得到了广泛支持,而且如果需要,可以进行干净的扩展,并且允许随机访问单个文件。(相比之下,另一个明显的选择是tar文件,它既不是也不是。)

下载协议

要下载有关模块以及模块本身的信息,这个vgo公司原型只发出简单的HTTP GET请求。一个关键的设计目标是使从静态托管站点,因此请求没有URL查询参数。

如前所述,自定义域可以指定模块托管在特定的基URL上。实施于vgo公司今天(但是,就像所有vgo公司,如有更改),该模块托管服务器必须提供四种请求形式:

中提供的JSON信息版本.info信息形式可能会演变,但今天它对应于这个结构:

类型RevInfo结构{版本字符串//版本字符串名称字符串//基础存储库中的完整ID短字符串//缩短的ID,用于伪版本时间-时间。时间//提交时间}

这个vgo公司 列表 -米 -u个命令显示每个可用更新的提交时间通过使用时间字段。

通用模块宿主服务器可以选择响应版本.info信息请求非发送版本。A类vgo公司命令式

vgo获取我的/东西/1459def下的v2

将获取1459定义信息然后使用时间领域。

还有两种可选的申请表:

这些支持在中使用未标记的提交vgo公司.如果vgo公司正在添加模块并且根本没有发现标记的提交,它使用第一个表单查找截至目前为止的最新提交。它在查找可用更新时也会这样做,假设仍然没有标记的提交。分支限制形式用于的内部模拟戈普基宁.这些表单还支持命令行语法:

vgo获取我的/东西/v2@2018-02-01T15:34:45vgo获取我的/东西/v2@2018-02-01T15:34:45@支管

这些可能是一个错误,但它们在今天的原型中,所以我要提到它们。

代理服务器

个人和公司都可能喜欢从以下站点下载Go模块代理服务器,无论是效率、可用性、安全性还是许可证合规性,或任何其他原因。具有标准Go模块格式和标准下载协议,如前两节所述,使引入对代理的支持变得很简单。如果$GOPROXY公司设置了环境变量,vgo公司从服务器获取所有模块给定的基本URL,不是从他们通常的位置。为了便于调试,$GOPROXY公司甚至可以是文件:///指向本地树的URL。

我们打算编写一个基本的代理服务器,用于vgo公司的本地缓存,根据需要下载新模块。在一组计算机之间共享这样的代理将有助于减少代理用户的冗余下载,但更重要的是将确保将来的可用性,即使原始副本消失。代理还可以选择不允许下载新模块。在这种模式下,代理会将可用模块限制为那些被代理管理员列入白名单的人。这两种代理模式都是公司环境中经常需要的功能。

也许有一天建立一个分布式收集会有意义中默认使用的代理服务器数量 得到,以确保模块可用性以及全球Go开发人员的快速下载。但还没有。今天,我们致力于确保 得到效果很好无需假设任何类型的中央代理服务器。

自动售货的终结

供应商目录有两个目的。首先,他们通过内容指定要在期间使用的依赖项 建造.其次,它们确保了这些依赖项的可用性,即使原始副本消失。另一方面,供应商目录也很难管理并膨胀它们出现的存储库。使用go.mod公司指定确切版本的文件要在期间使用的依赖项vgo公司 建造,并使用代理服务器确保可用性,供应商目录现在几乎完全冗余。然而,它们可以达到一个最终目的:以实现向新版本世界的平稳过渡。

构建模块时,vgo公司(和更高版本)将完全忽略供应商依赖性;这些依赖项也不会包含在模块的zip文件。为了使作者能够迁移到vgo公司go.mod公司同时仍然支持尚未转换的用户,新的vgo公司 小贩命令填充模块的包含用户需要的软件包的供应商目录复制vgo公司-基于构建。

接下来是什么?

这里的细节可能会修改,但今天go.mod公司文件夹将被任何未来的工具所理解。请开始用发布标签标记您的软件包;添加go.mod公司文件,如果这对您的项目有意义。

本系列的下一篇文章将介绍对工具命令行经验。