缓存 NuGet 包
Azure DevOps Services
使用管道缓存,可以缓存依赖项供以后的运行重复使用,从而减少生成时间。 本文将介绍如何使用缓存任务来缓存和还原 NuGet 包。
注意
YAML 和经典管道的代理池作业都支持管道缓存。 但是,它在经典发布管道中不受支持。
锁定依赖项
若要设置缓存任务,必须先锁定项目的依赖项并创建 package.lock.json 文件。 我们将使用此文件内容的哈希值为我们的缓存生成唯一键。
若要锁定项目的依赖项,请将 csproj 文件中的 RestorePackagesWithLockFile 属性设置为 true。 NuGet 还原会在项目的根目录中生成一个锁定文件 packages.lock.json。 请确保将 packages.lock.json 文件签入到你的源代码中。
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>
缓存 NuGet 包
我们需要创建一个管道变量,以指向运行管道的代理上的包位置。
在此示例中,将对 packages.lock.json 的内容进行哈希处理,以生成动态缓存键。 这确保了每次修改文件时,都会生成一个新的缓存键。
variables:
NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
- task: Cache@2
displayName: Cache
inputs:
key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**,!**/obj/**'
restoreKeys: |
nuget | "$(Agent.OS)"
nuget
path: '$(NUGET_PACKAGES)'
cacheHitVar: 'CACHE_RESTORED'
注意
缓存是不可变的,创建了缓存后便无法修改其内容。
还原缓存
仅当 CACHE_RESTORED
变量为 false 时,才会运行此任务。
- task: NuGetCommand@2
condition: ne(variables.CACHE_RESTORED, true)
inputs:
command: 'restore'
restoreSolution: '**/*.sln'
如果在生成任务期间遇到错误消息“找不到 project.assets.json”,可以通过从还原任务中删除条件 condition: ne(variables.CACHE_RESTORED, true)
来解决此错误。 通过执行此操作,将执行还原命令,生成 project.assets.json 文件。 还原任务不会下载已存在于相应文件夹中的包。
注意
一个管道可以包含一个或多个缓存任务,同一管道中的作业和任务可以访问并共享同一缓存。
性能比较
管道缓存是加快管道执行速度的好方法。 下面是两个不同管道的并排性能比较。 在添加缓存任务之前(右侧),完成还原任务大约需要 41 秒。 我们向第二个管道(左侧)添加了缓存任务,并将还原任务配置为在未命中缓存时运行。 在这种情况下,完成还原任务需要 8 秒。
下面提供了完整的 YAML 管道供参考:
pool:
vmImage: 'windows-latest'
variables:
solution: '**/*.sln'
buildPlatform: 'Any CPU'
buildConfiguration: 'Release'
NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
steps:
- task: NuGetToolInstaller@1
displayName: 'NuGet tool installer'
- task: Cache@2
displayName: 'NuGet Cache'
inputs:
key: 'nuget | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**,!**/obj/**'
restoreKeys: |
nuget | "$(Agent.OS)"
nuget
path: '$(NUGET_PACKAGES)'
cacheHitVar: 'CACHE_RESTORED'
- task: NuGetCommand@2
displayName: 'NuGet restore'
condition: ne(variables.CACHE_RESTORED, true)
inputs:
command: 'restore'
restoreSolution: '$(solution)'
- task: VSBuild@1
displayName: 'Visual Studio Build'
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'