2019-03-30

Using a NuGet Lock File for Reproducible Builds

"Reproducible builds are a set of software development practices that create an independently-verifiable path from source to binary code." For .NET projects, I want a git commit to produce the exact same NuGet package, byte-for-byte. The C# compiler with the -deterministic option enabled is able to "produce an assembly whose byte-for-byte output is identical across compilations for identical inputs". Its description lists a pretty long list of inputs that must be the same. Practically, it means that I use a Docker container for my build environment and I specify the specific version. Currently it is the latest stable .NET Docker image of mcr.microsoft.com/dotnet/core/sdk:2.2.105. I've also adopted Nerdbank.GitVersioning for deterministic version numbers.

Locking Dependencies

NuGet 4.9 released in November enabled "repeatable package restores using a lock file". This first implementation actually adds a lock file (packages.lock.json) for each project. To enable the lock file to be generated, set RestorePackagesWithLockFile to true and set RestoreLockedMode as well to make sure it is used on restore. At least that is how I've interpreted it so far. I added the properties to a Directory.Build.props so it is applied to all the projects in the solution.
Directory.Build.props loading ...

NuGet Sources

A list of sources used should be specified and the implicit NuGetFallbackFolder source should be disabled. Believe it or not, the NuGet content checksum hashes for several packages in the
NuGetFallbackFolder do not match NuGet.org for the same package versions. It is a good idea to disable the fallback folder and specify the sources:
NuGet.Config loading ...

Recreating the Lock file

This is what worked for me. I would clear out all the nuget caches, everything not committed, remove the lock files, then do a restore to recreate them. There may be smaller hammers.
recreate-lock.sh loading ...
Lock files are required for reproduce builds, but also reduce build time. It is something that Paket pioneered for .NET. I'm happy that it is finally available for NuGet clients too.