添加了 NuGet 4.8+ 对跨平台插件的支持。 这是通过构建新的插件扩展性模型来实现的,该模型必须符合一组严格的作规则。 插件是自包含可执行文件(.NET Core 世界中的可运行对象),NuGet 客户端在单独的进程中启动。 这是一个真正的“编写一次,到处运行”的插件。 它将使用所有 NuGet 客户端工具。 插件可以采用任何编程语言编写,但最简单的插件开发和安装体验是使用 .NET 编写的。 NuGet 客户端和插件之间的版本控制通信协议已定义。 在启动握手期间,2 个进程协商协议版本。
工作原理
高级工作流可按如下所述进行描述:
- NuGet 发现可用的插件。
- 如果适用,NuGet 将按优先级顺序循环访问插件,并逐个启动它们。
- NuGet 将使用可处理请求的第一个插件。
- 不再需要插件时,这些插件将被关闭。
常规插件要求
当前协议版本为 2.0.0。 在此版本中,要求如下:
- 在当前 NuGet 客户端工具的安全上下文中支持无状态启动。 例如,NuGet 客户端工具不会在稍后所述的插件协议之外执行提升或其他初始化。
- 非交互式,除非显式指定。
- 遵守商定的插件协议版本。
- 在合理的时间段内响应所有请求。
- 执行任何正在进行的操作的取消请求。
从 PATH 环境变量(例如通过 dotnet tool
安装)发现的插件还必须与文件名模式 nuget-plugin-*
匹配。
该 nuget-plugin-
部分必须完全以小写字母编写。
NuGet 6.12(MSBuild 17.12 和 .NET SDK 9.0.100)及更早版本在 Windows 上还要求插件用 Authenticode 签名。
以下所列的规范更详细地介绍了技术规格:
客户端 - 插件交互
NuGet 客户端工具和插件通过标准流(stdin、stdout、stderr)与 JSON 通信。 所有数据都必须经过 UTF-8 编码。 插件以参数“-Plugin”启动。 如果用户在启动插件可执行文件时没有使用此参数,插件可以提供提示信息,而不是等待协议握手。 协议握手超时为 5 秒。 插件应尽量缩短设置时间。 NuGet 客户端工具将通过传入 NuGet 源的服务索引来查询插件支持的操作。 插件可以使用服务索引来检查是否存在受支持的服务类型。
NuGet 客户端工具和插件之间的通信是双向的。 每个请求的超时时间为 5 秒。 如果作需要更长的时间,相应的进程应发送进度消息,以防止请求超时。在处于非活动状态 1 分钟后,插件被视为空闲并关闭。
插件安装和发现
NuGet 从基于约定的目录结构搜索插件,并扫描 PATH 环境变量。
基于约定的发现
CI/CD 场景和高级用户可以使用环境变量来覆盖行为。
使用环境变量时,只允许绝对路径。 请注意,仅在 NuGet 工具 5.3+ 及更高版本中才可使用 NUGET_NETFX_PLUGIN_PATHS
和 NUGET_NETCORE_PLUGIN_PATHS
。
-
NUGET_NETFX_PLUGIN_PATHS
- 定义将由基于 .NET Framework 的工具(NuGet.exe/MSBuild.exe/Visual Studio)使用的插件。 优先于NUGET_PLUGIN_PATHS
. (仅限 NuGet 版本 5.3+ ) -
NUGET_NETCORE_PLUGIN_PATHS
- 定义将由基于 .NET Core 的工具(dotnet.exe)使用的插件。 优先于NUGET_PLUGIN_PATHS
. (仅限 NuGet 版本 5.3+ ) -
NUGET_PLUGIN_PATHS
- 定义将用于该 NuGet 进程的插件,保留优先级。 如果设置了此环境变量,它将替代基于约定的发现。 如果指定了任何一个特定于框架的变量,则忽略该变量。 - 用户位置,NuGet 主页位置。
%UserProfile%/.nuget/plugins
无法重写此位置。 其他根目录将用于 .NET Core 和 .NET Framework 插件。
框架 | 根发现位置 | 使用者 |
---|---|---|
.NET 核心 | %UserProfile%/.nuget/plugins/netcore |
dotnet CLI(命令行界面) |
.NET 框架 | %UserProfile%/.nuget/plugins/netfx |
MSBuild、NuGet.exe、Visual Studio |
每个插件都应安装在其自己的文件夹中。 插件入口点将是已安装文件夹的名称,其中包含 .NET Core 的 .dll 扩展,以及 .NET Framework 的 .exe 扩展。
.nuget
plugins
netfx
myPlugin
myPlugin.exe
nuget.protocol.dll
...
netcore
myPlugin
myPlugin.dll
nuget.protocol.dll
...
路径发现
从 NuGet 6.13 开始,NuGet 将在 PATH 环境变量中提供的每个目录搜索与模式 nuget-plugin-*
匹配的文件。
模式匹配区分大小写, nuget-plugin-
必须完全以小写字母编写。
在 Windows 上,文件必须具有 .exe
或 .bat
扩展名。
在 Linux 和 Mac 上,文件必须设置可执行位。
这样,NuGet 插件就可以通过 dotnet tool
命令、WinGet、Linux 分发包管理器或任何其他方法安装,这些方法可将可执行文件置于用户的 PATH 上。
这也允许使用任何编程语言编写 NuGet 插件(以前,Linux 和 Mac 的插件必须用 .NET 编写)。
我们建议在 .NET 中开发插件,以便可以使用 NuGet.Protocol 包 来避免编写 json RPC 代码,并允许客户通过它 dotnet package search nuget-plugin
发现插件。
支持的操作
新插件协议支持两个操作。
操作名称 | 最低协议版本 | 最低 NuGet 客户端版本 |
---|---|---|
下载包 | 1.0.0 | 4.3.0 |
身份验证 | 2.0.0 | 4.8.0 |
在正确的运行时下运行插件
对于 dotnet.exe 方案中的 NuGet,插件需要能够在 dotnet.exe的特定运行时下执行。 责任在于插件供应商和用户,以确保使用兼容的 dotnet.exe/plugin 组合。 例如,在 2.0 运行时下 dotnet.exe 尝试使用为 2.1 运行时编写的插件时,用户位置插件可能会出现潜在问题。
能力缓存
插件的安全验证和实例化成本高昂。 下载操作的频率比身份验证操作更频繁,但是平均而言,NuGet 用户通常只有一个身份验证插件。 为了提升用户体验,NuGet将缓存特定请求的操作声明。 此缓存是每个插件,插件密钥是插件路径,此功能缓存的过期时间为 30 天。
缓存位于 %LocalAppData%/NuGet/plugins-cache
,并可通过环境变量 NUGET_PLUGINS_CACHE_PATH
覆盖。
若要清除此 缓存,可以使用此选项 plugins-cache
运行局部变量命令。
本地 all
选项现在还将删除插件缓存。
协议消息索引
协议版本 1.0.0 消息:
关闭
- 请求方向:NuGet -> 插件
- 请求将不包含有效负载
- 不期望有回应。 正确的响应是让插件进程及时退出。
在包中复制文件
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包裹的ID和版本
- 包源存储库位置
- 目标目录路径
- 要从包中复制到目标目录路径的文件列表
- 响应将包含:
- 表示操作结果的响应代码
- 如果操作成功,将列出目标目录中已复制文件的完整路径的枚举
复制包文件 (.nupkg)
- 请求路径:NuGet -> 插件
- 请求将包含:
- 软件包 ID 和版本
- 软件包源代码库位置
- 目标文件路径
- 响应将包含:
- 操作结果的响应代码
获取凭据
- 请求方向:插件 -> NuGet
- 请求将包含:
- 软件包源存储库位置
- 使用当前凭据从包源存储库获取的 HTTP 状态代码
- 响应将包含:
- 用于指示操作结果的响应代码
- 用户名(如果可用)
- 密码(如果可用)
获取包中的文件
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包标识和版本
- 软件包来源代码库位置
- 响应将包含:
- 指示操作结果的响应代码
- 在操作成功的情况下,包中可以获得一系列文件路径。
获取操作声明
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包裹来源的服务 index.json
- 软件包源存储库位置
- 响应将包含:
- 指示操作结果的响应代码
- 如果操作成功,则会返回支持的操作的枚举列表(例如包下载)。 如果插件不支持包源,该插件必须返回一组空的受支持的操作。
注释
此消息已在 版本 2.0.0 中更新。 它位于客户端上以保持向后兼容性。
获取包哈希
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包标识和版本
- 软件包源存储库位置
- 哈希算法
- 响应将包含:
- 指示操作结果的响应代码
- 如果操作成功,则使用请求的哈希算法生成包文件的哈希值。
获取包版本
- 请求方向:NuGet -> 插件
- 请求将包含:
- 软件包 ID
- 软件包源存储库位置
- 响应将包含:
- 指示操作结果的响应代码
- 操作成功时,包版本的列表。
获取服务索引
- 请求方向:插件 -> NuGet
- 请求将包含:
- 软件包源存储库位置
- 响应将包含:
- 指示操作结果的响应代码
- 服务索引(如果操作成功)
握手
- 请求方向:NuGet <-> 插件
- 请求将包含:
- 当前插件协议版本
- 支持的最低插件协议版本
- 响应将包含:
- 指示操作结果的响应代码
- 如果操作成功,则为协商的协议版本。 失败将导致插件终止。
初始化
- 请求方向:NuGet -> 插件
- 请求将包含:
- NuGet 客户端工具版本
- NuGet 客户端工具的有效语言。 这考虑了 ForceEnglishOutput 设置(如果使用)。
- 默认请求超时,取代协议默认值。
- 响应将包含:
- 指示操作结果的响应代码。 失败将导致插件终止。
日志
- 请求方向:插件 -> NuGet
- 请求将包含:
- 请求的日志级别
- 要记录的消息
- 响应将包含:
- 指示操作结果的响应代码。
监视 NuGet 进程退出
- 请求方向:NuGet -> 插件
- 请求将包含:
- NuGet 进程 ID
- 响应将包含:
- 指示操作结果的响应代码。
预提取包
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包的 ID 和版本
- 软件包源存储库位置
- 响应将包含:
- 指示操作结果的响应代码
设置凭据
- 请求方向:NuGet -> 插件
- 请求将包含:
- 软件包源存储库的位置
- 上一个已知包源用户名(如果可用)
- 上一个已知包源密码(如果可用)
- 最后一个已知代理用户名(如果可用)
- 上一个已知代理密码(如果可用)
- 响应将包含:
- 指示操作结果的响应代码
设置日志级别
- 请求方向:NuGet -> 插件
- 请求将包含:
- 默认日志级别
- 响应将包含:
- 表示操作结果的响应代码
协议版本 2.0.0 消息
- 获取操作声明
请求方向:NuGet -> 插件
- 请求将包含:
- 软件包源的服务 index.json
- 软件包源仓库位置
- 响应将包含:
- 指示操作结果的响应码
- 如果操作成功,将返回受支持的操作枚举。 如果插件不支持软件包源,该插件必须返回一组空的受支持操作。
如果服务索引和包源为 null,则插件可以使用身份验证进行应答。
- 请求将包含:
- 获取身份验证凭据
- 请求方向:NuGet -> 插件
- 请求将包含:
- Uri
- isRetry
- 非交互
- 可以显示对话框
- 响应将包含
- 用户名
- 密码
- 消息
- 身份验证类型列表
- 信息响应代码