NuGet 跨平台插件
在 NuGet 4.8+ 中,已添加对跨平台插件的支持。 这是通过构建新的插件扩展模型实现的,该模型必须符合一组严格的操作规则。 这些插件是自包含可执行文件(可在 .NET Core 环境中运行),可由 NuGet 客户端在单独的进程中启动。 这是真实的一次性写入,可在任何位置运行插件。 它将适用于所有 NuGet 客户端工具。 这些插件可以是 .NET Framework(NuGet.exe、MSBuild.exe 和 Visual Studio)或 .NET Core (dotnet.exe)。 已定义 NuGet 客户端与插件之间的受版本控制的通信协议。 在启动握手期间,这 2 个进程会协商协议版本。
要涵盖所有 NuGet 客户端工具方案,则同时需要 .NET Framework 和 .NET Core 插件。 下面介绍插件的客户端/框架组合。
客户端工具 | 框架 |
---|---|
Visual Studio | .NET Framework |
dotnet.exe | .NET Core |
NuGet.exe | .NET Framework |
MSBuild.exe | .NET Framework |
Mono 上的 NuGet.exe | .NET Framework |
工作原理
高级别工作流的描述如下:
- NuGet 发现可用插件。
- 如果适用,NuGet 将按优先级顺序循环访问插件,并逐一启动插件。
- NuGet 将使用第一个可为请求提供服务的插件。
- 不再需要使用插件时,系统会将其关闭。
一般插件要求
当前协议版本是 2.0.0。 在此版本下,要求如下所示:
- 具有有效的、受信任的 Authenticode 签名程序集,将在 Windows 和 Mono 上运行。 没有针对 Linux 和 Mac 上运行的程序集的特殊信任要求。 相关问题
- 支持在 NuGet 客户端工具的当前安全上下文中进行无状态启动。 例如,NuGet 客户端工具不会在稍后介绍的插件协议之外执行提升或其他初始化。
- 除非显式指定,否则为非交互式。
- 遵循协商的插件协议版本。
- 在合理的时间段内响应所有请求。
- 优先处理任何进行中的操作的取消请求。
以下说明详细介绍了技术规范:
客户端 - 插件交互
NuGet 客户端工具和插件通过标准流(stdin、stdout、stderr)与 JSON 通信。 所有数据必须采用 UTF-8 编码。 插件通过参数“-Plugin”启动。 如果用户在未使用此参数的情况下直接启动插件可执行文件,则该插件可提供信息性消息而不是等待协议握手。 协议握手超时时间为 5 秒。 插件应在尽可能短的时间内完成设置。 NuGet 客户端工具将通过传入 NuGet 源的服务索引来查询插件支持的操作。 插件可以使用服务索引来检查是否存在受支持的服务类型。
NuGet 客户端工具与插件之间的通信是双向的。 每个请求的超时时间为 5 秒。 如果操作本应花费更长的时间,则相应进程应发送进度消息,以防请求超时。在非活动状态保持 1 分钟后,插件将被视为空闲并关闭。
插件安装和发现
系统将通过基于约定的目录结构来发现插件。
CI/CD 方案和高级用户可使用环境变量来替代行为。 使用环境变量时,只允许使用绝对路径。 请注意,NUGET_NETFX_PLUGIN_PATHS
和 NUGET_NETCORE_PLUGIN_PATHS
仅适用于 5.3+ 及以上版本的 NuGet 工具。
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 进程的插件,保留优先级。 如果已设置该环境变量,它将替代基于约定的发现。 如果已指定任何一个框架特定的变量,则忽略。- 用户位置,
%UserProfile%/.nuget/plugins
中的 NuGet 主页位置。 无法替代此位置。 不同的根目录将用于 .NET Core 和 .NET Framework 插件。
框架 | 根发现位置 |
---|---|
.NET Core | %UserProfile%/.nuget/plugins/netcore |
.NET Framework | %UserProfile%/.nuget/plugins/netfx |
每个插件都应安装在它自己的文件夹中。 插件入口点将为安装文件夹的名称,其中包含 .NET Core 的 .dll 扩展和 .NET Framework 的 .exe 扩展。
.nuget
plugins
netfx
myPlugin
myPlugin.exe
nuget.protocol.dll
...
netcore
myPlugin
myPlugin.dll
nuget.protocol.dll
...
注意
目前没有用于安装插件的用户情景。 只需将所需文件移动到预先确定的位置即可。
不支持的操作
新的插件协议支持两个操作。
操作名称 | 最低协议版本 | 最低 NuGet 客户端版本 |
---|---|---|
下载包 | 1.0.0 | 4.3.0 |
身份验证 | 2.0.0 | 4.8.0 |
在正确的运行时下运行插件
对于 dotnet.exe 方案中的 NuGet,插件需要能够在 dotnet.exe 的特定运行时下执行。 插件提供程序和使用者需确保使用的是兼容的 dotnet.exe/插件组合。 user-location 插件可能在此情况下出现潜在问题:例如,2.0 运行时下的 dotnet.exe 尝试使用为 2.1 运行时编写的插件。
功能缓存
插件的安全验证和实例化的成本较高。 下载操作的频率远高于身份验证操作,但是普通的 NuGet 用户只可能拥有身份验证插件。 为了改善体验,NuGet 将缓存给定请求的操作声明。 此缓存针对于每个插件,插件密钥作为插件路径,并且此功能缓存的有效期为 30 天。
缓存位于 %LocalAppData%/NuGet/plugins-cache
中,并通过环境变量 NUGET_PLUGINS_CACHE_PATH
进行替代。
要清理此缓存,可以通过 plugins-cache
选项运行 locals 命令。
现在,all
locals 选项也将删除插件缓存。
协议消息索引
协议版本 1.0.0 消息:
Close
- 请求方向:NuGet -> 插件
- 请求不会包含有效负载
- 不会有响应。 适当的响应是为了使插件进程迅速退出。
复制包中的文件
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包 ID 和版本
- 包源存储库位置
- 目标目录路径
- 包中要复制到目标目录路径的文件的可枚举项
- 响应将包含:
- 指示操作结果的响应代码
- 目标目录中已复制文件的完整路径的可枚举项(如果操作成功)
复制包文件 (.nupkg)
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包 ID 和版本
- 包源存储库位置
- 目标文件路径
- 响应将包含:
- 指示操作结果的响应代码
获取凭据
- 请求方向:插件 -> NuGet
- 请求将包含:
- 包源存储库位置
- 使用当前凭据从包源存储库获取的 HTTP 状态代码
- 响应将包含:
- 指示操作结果的响应代码
- 用户名(如果可用)
- 密码(如果可用)
获取包中的文件
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包 ID 和版本
- 包源存储库位置
- 响应将包含:
- 指示操作结果的响应代码
- 包中文件路径的可枚举项(如果操作成功)
获取操作声明
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包源的服务 index.json
- 包源存储库位置
- 响应将包含:
- 指示操作结果的响应代码
- 受支持的操作(如下载包)的可枚举项(如果操作成功)。 如果插件不支持包源,则该插件必须返回受支持的操作的空集。
注意
此消息已在版本 2.0.0 中进行了更新。 由客户端保持后向兼容性。
获取包哈希
- 请求方向:NuGet -> 插件
- 请求将包含:
- 包 ID 和版本
- 包源存储库位置
- 哈希算法
- 响应将包含:
- 指示操作结果的响应代码
- 使用请求的哈希算法的包文件哈希(如果操作成功)
获取包的版本
- 请求方向: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
- NonInteractive
- CanShowDialog
- 响应将包含:
- 用户名
- 密码
- Message
- 身份验证类型列表
- MessageResponseCode