缓存 NuGet 包

Azure DevOps Services | Azure DevOps Server 2022 | Azure DevOps Server 2020

管道缓存通过存储依赖项以便在将来的运行中重复使用来帮助缩短生成时间。 本文将介绍如何使用缓存任务来缓存和还原 NuGet 包。

注意

在经典发布管道中不支持管道缓存。

先决条件

产品 要求
Azure DevOps - 一个 Azure DevOps 项目
- 权限:
    - 若要授予对项目中所有管道的访问权限,你必须是项目管理员组的成员。

锁定依赖项

在设置缓存任务之前,需要锁定项目的依赖项并生成 package.lock.json 文件。 唯一缓存密钥派生自此锁文件内容的哈希,以确保生成之间的一致性。

若要锁定项目的依赖项,请将 RestorePackagesWithLockFile 属性添加到 csproj 文件中,并将其设置为 true。 运行 nuget restore时,它会在项目的根目录中生成 packages.lock.json 文件。 请确保将 packages.lock.json 文件签入到你的源代码中。

<PropertyGroup>
  <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
</PropertyGroup>

缓存 NuGet 包

若要缓存 NuGet 包,请定义一个管道变量,该变量指向运行管道的代理上的包的位置。

在下面的示例中,将哈希处理 packages.lock.json 的内容以生成动态缓存密钥。 这可确保每当文件更改时,都创建一个新的缓存密钥。

显示如何在 Azure Pipelines 中生成缓存密钥的屏幕截图。

variables:
  NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages

- task: Cache@2
  displayName: Cache v2 task 
  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'

注意

如果使用 Ubuntu 24.04 或更高版本,则必须将 NuGetAuthenticate 任务与 .NET CLI 一起使用,而不是 NuGetCommand@2 任务。 有关详细信息,请参阅支持较新的 Ubuntu 托管映像

处理“project.assets.json 找不到”错误

如果在生成任务期间遇到错误 “找不到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)'

此方法也适用于 .NET Core 项目,前提是项目使用 packages.lock.json 锁定包版本。 可以通过在 * Csproj* 文件中将 RestorePackagesWithLockFile 设置为 True,或者运行以下命令来启用此功能:dotnet restore --use-lock-file