缓存 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 的内容进行哈希处理,以生成动态缓存键。 这确保了每次修改文件时,都会生成一个新的缓存键。

一个屏幕截图,显示了如何在 Azure Pipelines 中生成缓存键。

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)'