托管一个私人的Dart软件包库的方法

406 阅读5分钟

Dart 2.15中对安全企业软件包托管的改进

我们最近的Dart 2.15版本增加了对私有软件包库的支持。包库是一个托管Dart包的服务器,供dart pub客户端使用。这篇文章讨论了如何利用Dart 2.15中引入的新令牌认证机制,在pub.dev旁边使用私有软件包库。

这项功能已经被CloudsmithJFrog Artifactory采用,他们提供了自定义软件包库的服务。

Cloudsmith和JFrog Artifactory现在都提供私有Dart资源库。

为什么使用自定义软件包库?

默认的软件包库,pub.dev,是由Dart团队运营的。它促进了Dart包的发布,供公众使用,并允许任何Dart开发人员使用这些包,只需将包的名称添加到他们的pubspec文件。这使得一个活跃的生态系统能够创建一个丰富的Dart包集合。然而,自定义包库在以下情况下可能很有用。

  1. 在一个组织内分享私人软件包。这可能是一个组织希望在该组织的成员之间分享内部专有软件包,而不是与一般公众分享。
  2. 严格控制企业环境中的依赖关系。一些企业希望明确地为批准使用的特定软件包版本创建允许列表。这样做可能是为了执行代码审查政策或许可证合规性。
  3. 没有公共互联网接入的安全环境。一些组织在监管环境下运作,不允许连接到公共互联网,以及延伸到pub.dev,。这通常适用于政府机构/承包商和一些银行服务提供者。

那些只需要分享一些专有软件包的组织(场景1)可能会使用git-dependencies。这是一个强大的机制,因为dart pub client命令会调用git客户端来克隆git-dependencies--这样你可能会用你的SSH密钥来认证克隆一个依赖关系,并使用GitHub/GitLab团队管理权限。由于GitHub支持使用Yubikeys的SSH,这种设置在某些情况下效果不错。

然而,在解决版本问题时,dart pub中的git-dependencies有一些限制。当使用git-dependency时,dart pub只是克隆pubspec.yaml中指定的标签/分支/ref,因为没有机制来尝试多个版本的依赖。因此,使用私有的自定义软件包库会很有吸引力,因为自定义软件包库可以为dart pub客户端提供一个版本列表,让版本解算器挑选一个兼容的版本,避免冲突。这也使得使用dart pub outdated和dart pub upgrade --major-versions来升级私有和公共依赖关系变得容易。

Git 依赖关系不支持那些希望对依赖关系进行更严格控制的组织(场景2)或使用没有互联网接入的安全环境(场景3),因为很多软件包必须被镜像。在这些情况下,最好是有一个自定义的软件包库,镜像pub.dev中允许列出的子集。

使用自定义软件包库

从Dart 2.15开始,你可以使用简短的托管依赖语法来依赖来自自定义软件包库的软件包。

dependencies:
  foo:
    hosted: https://dart-packages.example.com
    version: ^1.4.0
environment: 
  sdk: >=2.15.0 <3.0.0

短式语法需要SDK约束>=2.15.0,因为旧版本的Dart SDK不支持这种语法。如果你不想担心语法问题,你也可以直接使用dart pub add命令来添加来自自定义软件包库的依赖关系。

$ dart pub add foo --hosted https://dart-packages.example.com
Resolving dependencies... 
+ foo 1.4.0
Changed 1 dependency!

使用hosted-dependency语法,可以将来自自定义软件包库的依赖关系与来自官方软件包库的依赖关系混合起来。下面的例子依赖来自dart-packages.example.com仓库的软件包foo,以及来自pub.dev的软件包retry。

dependencies:
  retry: ^3.0.0
  foo:
    hosted: https://dart-packages.example.com
    version: ^1.4.0
environment: 
  sdk: >=2.15.0 <3.0.0

这在使用自定义软件包库共享私有软件包时很有用(场景1)。但对于更严格的依赖关系控制(情况2)或在没有互联网访问的安全环境中工作(情况3),覆盖默认的软件包仓库可能是更好的。

覆盖默认的软件包库

默认情况下,dart pub get从pub.dev获取依赖项,除非使用hosted-dependency语法来指定一个自定义的包库。然而,我们可以使用环境变量PUB_HOSTED_URL来覆盖默认的软件包库。这种方法在镜像 pub.dev 的子集时特别有用(方案 2 或 3),因为不需要更新 pubspec.yaml 文件来引用自定义软件包仓库的 URL。例如,写下以下内容就足够了。

dependencies:
  retry: ^3.0.0
  foo: ^1.0.0

如果允许列出的软件包retry和foo的版本被复制到自定义软件包仓库,并且环境变量PUB_HOSTED_URL指向自定义软件包仓库的URL,pub get可以如下工作。

$ export PUB_HOSTED_URL=https://dart-packages.example.com
$ dart pub get
Resolving dependencies... 
+ retry 3.1.0
+ foo 1.4.0
Changed 2 dependencies!

当构建服务器位于企业防火墙后面时,这种方法效果最好,因为防火墙禁止外发网络连接,以避免有人忘记设置PUB_HOSTED_URL时意外的注入攻击。同样,建议在pubspec.yaml中设置pub_to: ,以避免意外发布到pub.dev(当PUB_HOSTED_URL没有定义时)。

针对自定义软件包库的认证

大多数自定义软件包库都可能是需要认证的私有软件包库。Dart 2.15引入了dart pub token命令来管理认证令牌。对自定义软件包库的请求使用一个秘密令牌进行认证。你从你的自定义软件包库获得秘密令牌,并把它传递给dart pub token add ,它提示你使用该令牌,如下所示。

$ dart pub token add https://dart-packages.example.com
Enter secret token: [enter secret token]
Requests to "dart-packages.example.com" will now be authenticated using the secret token.

来自CI环境的认证

当在CI中运行时,通常可以在环境变量中存储秘密,虽然可以使用echo $TOKEN | dart pub token add 来传递秘密,但也可以告诉dart pub在与特定的自定义版本库对话时从环境变量中读取秘密。

$ dart pub token add https://dart-packages.example.com --env-var MY_SECRET_TOKEN
Requests to "https://dart-packages.example.com" will now be authenticated using the secret token stored in the environment variable "MY_SECRET_TOKEN".

这确保了dart pub不会在其配置文件中实际存储秘密,而只是存储它应该从环境变量$MY_SECRET_TOKEN中读取秘密这一事实。这使得秘密只在CI环境下运行时被存储在环境变量中,并在CI工作之间共享执行环境时减少秘密被意外泄露的风险。

发布到自定义软件包库

要发布软件包到自定义软件包库,你可以在pubspec.yaml中指定pub_to: ,然后运行dart pub publish。这使用了与dart pub get相同的认证令牌。至少,你的pubspec.yaml文件应该看起来像下面这样。

name: mypkg
version: 1.0.0
publish_to: https://dart-packages.example.com
dependencies:
  meta: ^1.7.0
environment:
  sdk: >=2.15.0 <3.0.0

当运行dart pub publish时,审查给出的信息是很重要的。在确认发布动作之前,你应该总是做以下工作。

  • 检查软件包将被发布的URL。
  • 检查要包含在软件包中的文件列表。
  • 考虑软件包的验证建议。比如说。
$ dart pub publish
Publishing mypkg 1.0.0 to https://dart-packages.example.com
|-- CHANGELOG.md
|-- LICENSE
|-- README.md
|-- lib
|   '-- mypkg.dart
'-- pubspec.yaml
Package validation found the following potential issue:
* It's strongly recommended to include a "homepage" or "repository" field in your pubspec.yaml
...

对于发布到自定义存储库来说,这些建议可能并不重要,但为包提供元数据通常是有用的。在pub.dev上,有适当的元数据和文档的包会得到更多的pub分

通过使用环境变量 PUB_HOSTED_URL 来覆盖默认的软件包仓库,是可以发布到自定义仓库的,但如果你这样做,强烈建议你在 pubspec.yaml 文件中指定 publish_to: ;这可以防止你不小心把你的专有软件包发布到公共仓库。如果你不想把你的专有软件包发布到任何软件包仓库,指定 publish_to: none 来防止意外发布。

获取一个自定义软件包仓库

如前所述,自定义软件包库可作为多个商业供应商的服务,减轻了你托管和维护自己的自定义软件包库的开销。

Cloudsmith中的令牌认证

自2020年以来,Cloudsmith一直在提供私人和公共Dart软件包库。Cloudsmith最近宣布,他们的Dart包库产品支持令牌认证。欲了解更多信息,请查看Cloudsmith的文档

由Cloudsmith托管的私人Dart包库。

JFrog Artifactory中的Dart支持

JFrog Artifactory最近宣布支持自定义Dart包库,包括支持分层包库和从pub.dev上的公共包库镜像包库。欲了解更多信息,请查看JFrog Artifactory的文档

一个由JFrog管理的自定义Dart包库。

结束语

对于那些想编写自己的自定义包库的人来说,我们已经发布了一个为Dart包库服务的规范。它相当简单,但如果有什么不清楚的地方,或者你有什么改进的建议,请随时在pub.dev库中开问题。


托管私有Dart包库最初发表于Darton Medium,人们通过强调和回应这个故事来继续对话。