I have a .net core 3.1 solution with several web and class library projects in it. All packages use the <PackageReference>
format in the .csproj
file.
I am building the solution with Azure DevOps Pipelines and I want to reduce my build times by caching Nuget packages instead of restoring them from nuget.org on every run.
Following some guidance from documentation and blog posts, I've:
Added this
nuget.config
file to my solution so packages are always restored from nuget.org:<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <clear /> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> </packageSources> </configuration>
Added a
Directory.Build.props
file to my solution so that for each project, apackages.lock.json
will be generated when building.<Project> <PropertyGroup> <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile> <DisableImplicitNuGetFallbackFolder>true</DisableImplicitNuGetFallbackFolder> </PropertyGroup> </Project>
Added the generated
packages.lock.json
files to git.Integrated the Cache@2 task into my
azure-pipeline.yml
file like so:trigger: - master - users/* pool: vmImage: 'windows-latest' variables: buildConfiguration: 'Debug' NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages steps: - task: Cache@2 displayName: Cache nuget packages inputs: key: 'nuget 5 | "$(Agent.OS)" | **/packages.lock.json,!**/bin/**' restoreKeys: | nuget 5 | "$(Agent.OS)" path: $(NUGET_PACKAGES) cacheHitVar: CACHE_RESTORED5 - task: DotNetCoreCLI@2 displayName: 'Restoring nuget packages ($(buildConfiguration))' condition: ne(variables.CACHE_RESTORED5, 'true') inputs: command: 'restore' projects: '**/*.csproj' includeNuGetOrg: true arguments: '--configuration $(buildConfiguration)' - task: DotNetCoreCLI@2 displayName: 'Build solution ($(buildConfiguration))' inputs: command: 'build' arguments: '--configuration $(buildConfiguration) --no-restore'
I've managed to run the pipeline successfully so on the first run it restores packages from nuget.org and saves them to the cache and on subsequent runs it reads them from the cache instead.
My problem is that every once in a while my build breaks at the "Build solution" stage with this kind of error message showing for each one of my solution projects:
##[error]C:\Program Files\dotnet\sdk\3.1.402\Sdks\Microsoft.NET.Sdk\targets\Microsoft.PackageDependencyResolution.targets(241,5): Error NETSDK1004: Assets file 'D:\a\1\s\MyProject\obj\project.assets.json' not found. Run a NuGet package restore to generate this file.
​When this happens I am forced to increment my cache key and environment variable to force a cache refresh and then the following build works.
My question is - Why is this happening and how can I fix it so my build stops being so flaky?
Update:
Caching can be effective at improving build time provided the time to restore and save the cache is less than the time to produce the output again from scratch. Because of this, caching may not be effective in all scenarios and may actually have a negative impact on build time.
Since the project.assets.json needs to be regenerated on the build machine with the resolved paths to this shared cache even if no packages need to be downloaded.
For this scenario, to improve the performance, you can run pipeline on self-hosted agent or push these packages to DevOps repo directly.
More details please take a look at this blog:
According to your description and error info,
NUGET_PACKAGES: $(Pipeline.Workspace)/.nuget/packages
.Looks like you didn't cached project.assets.json which caused this issue.
This problem happening when your build tool is not set to do restore on projects set to use
PackageReference
vspackages.config
and mostly affect Net Core and Netstandard new style projects.Actually it's not related to key, when you force use a newly key. Azure DevOps Service is not using your cached package and force "run a new NuGet Restore". And during the complete restore process, this file generated succeed. Thus build also succeed.