NuGet パッケージをキャッシュする

Azure DevOps Services

パイプラインのキャッシュを使うと、後の実行で再利用する依存関係をキャッシュすることで、ビルド時間を短縮できます。 この記事では、キャッシュ タスクを使用して、NuGet パッケージをキャッシュおよび復元する方法をについて説明します。

Note

パイプライン キャッシュは、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'

Note

キャッシュは変更不可であり、キャッシュが作成されると、その内容は変更できません。

キャッシュを復元する

このタスクは、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) を削除することで解決できます。 これにより、restore コマンドが実行され、project.assets.json ファイルが生成されます。 復元タスクでは、対応するフォルダーに既に存在するパッケージはダウンロードされません。

Note

パイプラインには 1 つ以上のキャッシュ タスクを含めることができます。また、同じパイプライン内のジョブとタスクは、同じキャッシュにアクセスして共有できます。

パフォーマンスの比較

パイプライン キャッシュは、パイプラインの実行を高速化する優れた方法です。 次に、2 つの異なるパイプラインのパフォーマンスを並べて比較します。 キャッシュ タスクを追加する前は (右)、復元タスクに約 41 秒かかりました。 キャッシュ タスクを 2 番目のパイプラインに追加し (左)、キャッシュ ミスが発生したときに実行するように復元タスクを構成しました。 この場合の復元タスクは、完了するまで 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)'