Adding NuGet packages when offline by Mark Seemann
A fairly trivial technical detective story.
I was recently in an air plane, writing code, when I realised that I needed to add a couple of NuGet packages to my code base. I was on one of those less-travelled flights in Europe, on board an Embraer E190, and as is usually the case on those 1½-hour flights, there was no WiFi.
Adding a NuGet package typically requires that you're online so that the tools can query the relevant NuGet repository. You'll need to download the package, so if you're offline, you're just out of luck, right?
Fortunately, I'd previously used the packages I needed in other projects, on the same laptop. While I'm no fan of package restore, I know that the local NuGet tools cache packages somewhere on the local machine.
So, perhaps I could entice the tools to reuse a cached package...
First, I simply tried adding a package that I needed:
$ dotnet add package unquote Determining projects to restore... Writing C:\Users\mark\AppData\Local\Temp\tmpF3C.tmp info : X.509 certificate chain validation will use the default trust store selected by .NET. info : Adding PackageReference for package 'unquote' into project '[redacted]'. error: Unable to load the service index for source https://api.nuget.org/v3/index.json. error: No such host is known. (api.nuget.org:443) error: No such host is known.
Fine plan, but no success.
Clearly the dotnet
tool was trying to access api.nuget.org
, which, obviously, couldn't be reached because my laptop was in flight mode. It occurred to me, though, that the reason that the tool was querying api.nuget.org
was that it wanted to see which version of the package was the most recent. After all, I hadn't specified a version.
What if I were to specify a version? Would the tool use the cached version of the package?
That seemed worth a try, but which versions did I already have on my laptop?
I don't go around remembering which version numbers I've used of various NuGet packages, but I expected the NuGet tooling to have that information available, somewhere.
But where? Keep in mind that I was offline, so couldn't easily look this up.
On the other hand, I knew that these days, most Windows applications keep data of that kind somewhere in AppData
, so I started spelunking around there, looking for something that might be promising.
After looking around a bit, I found a subdirectory named AppData\Local\NuGet\v3-cache
. This directory contained a handful of subdirectories obviously named with GUIDs. Each of these contained a multitude of .dat
files. The names of those files, however, looked promising:
list_antlr_index.dat list_autofac.dat list_autofac.extensions.dependencyinjection.dat list_autofixture.automoq.dat list_autofixture.automoq_index.dat list_autofixture.automoq_range_2.0.0-3.6.7.dat list_autofixture.automoq_range_3.30.3-3.50.5.dat list_autofixture.automoq_range_3.50.6-4.17.0.dat list_autofixture.automoq_range_3.6.8-3.30.2.dat list_autofixture.dat ...
and so on.
These names were clearly(?) named list_[package-name].dat
or list_[package-name]_index.dat
, so I started looking around for one named after the package I was looking for (Unquote).
Often, both files are present, which was also the case for Unquote.
$ ls list_unquote* -l -rw-r--r-- 1 mark 197609 348 Oct 1 18:38 list_unquote.dat -rw-r--r-- 1 mark 197609 42167 Sep 23 21:29 list_unquote_index.dat
As you can tell, list_unquote_index.dat
is much larger than list_unquote.dat
. Since I didn't know what the format of these files were, I decided to look at the smallest one first. It had this content:
{ "versions": [ "1.3.0", "2.0.0", "2.0.1", "2.0.2", "2.0.3", "2.1.0", "2.1.1", "2.2.0", "2.2.1", "2.2.2", "3.0.0", "3.1.0", "3.1.1", "3.1.2", "3.2.0", "4.0.0", "5.0.0", "6.0.0-rc.1", "6.0.0-rc.2", "6.0.0-rc.3", "6.0.0", "6.1.0" ] }
A list of versions. Sterling. It looked as though version 6.1.0 was the most recent one on my machine, so I tried to add that one to my code base:
$ dotnet add package unquote --version 6.1.0 Determining projects to restore... Writing C:\Users\mark\AppData\Local\Temp\tmp815D.tmp info : X.509 certificate chain validation will use the default trust store selected by .NET. info : Adding PackageReference for package 'unquote' into project '[redacted]'. info : Restoring packages for [redacted]... info : Package 'unquote' is compatible with all the specified frameworks in project '[redacted]'. info : PackageReference for package 'unquote' version '6.1.0' added to file '[redacted]'. info : Generating MSBuild file [redacted]. info : Writing assets file to disk. Path: [redacted] log : Restored [redacted] (in 397 ms).
Jolly good! That worked.
This way I managed to install all the NuGet packages I needed. This was fortunate, because I had so little time to transfer to my connecting flight that I never got to open the laptop before I was airborne again - in another E190 without WiFi, and another session of offline programming.
Comments
A postscript to your detective story might note that the primary NuGet cache lives at
%userprofile%\.nuget\packages
on Windows and~/.nuget/packages
on Mac and Linux. The folder names there are much easier to decipher than the folders and files in the http cache.