Enabling Reproducible builds when building NuGet packages
Reproducible builds are important when building NuGet packages from public sources. Indeed, it gives your consumers confidence in your packages by allowing them to validate the package has actually been built using the public sources. To be able to reproduce a build, you need the source files, the referenced DLLs, the compiler version, and the compiler options (language version, defines, nullables, etc.)
All these information are stored in the pdb file. Note that you quickly find them using NuGet Package Explorer:
NuGet Package Explorer shows the compilation information stored in the pdb file
NuGet Package Explorer shows the list of files stored in the pdb file
#How to create a reproducible build in .NET
To create a reproducible build in .NET, you need to use a recent version of the compiler and set a few options in the project file. You could do it manually or just use the NuGet package DotNet.ReproducibleBuilds. This package does many things for you:
- Ensure MSBuild 16.10 or above is used
- Enable SourceLink for GitHub, GitLab, Azure DevOps, BitBucket
- Set
ContinuousIntegrationBuild
to true if it detects a known build environment (GitHub Actions, Azure Pipelines, AWS CodeBuild, GitLab, AppVeyor, etc.) - Set
PublishRepositoryUrl
to true to publish the repository URL and the commit in the NuGet package - Set
EmbedUntrackedSources
to true to include generated files in the NuGet package - Set
DebugType
to embedded if not already set to another mode
You can add the DotNet.ReproducibleBuilds package to your project file:
<Project>
<!-- Enabling reproducible builds -->
<ItemGroup>
<PackageReference Include="DotNet.ReproducibleBuilds" Version="0.1.66">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
</ItemGroup>
<!-- Optional but recommended: NuGet Package configuration -->
<PropertyGroup>
<Authors>Meziantou</Authors>
<PackageIcon>icon.png</PackageIcon>
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageReadmeFile>readme.md</PackageReadmeFile>
</PropertyGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)\icon.png" Pack="true" PackagePath="" Visible="false" />
<None Include="$(MSBuildThisFileDirectory)\LICENSE.txt" Pack="true" PackagePath="" Visible="false" />
<None Include="$(MSBuildThisFileDirectory)\readme.md" Pack="true" PackagePath="" />
</ItemGroup>
</Project>
If you use dotnet pack
to build the package on the CI server, you should get a valid NuGet package. You can valide everything is ok by using NuGet Package Explorer:
NuGet Package Explorer shows the package is valid
You can also use the dotnet-validate tool to validate a package
dotnet tool update -g dotnet-validate --version 0.0.1-preview.169
dotnet validate package local package.nupkg
Result of dotnet-validate
#Additional resources
- Ensuring best practices for NuGet packages
- Publishing a NuGet package following best practices using GitHub
- GitHub - dotnet/reproducible-builds
Do you have a question or a suggestion about this post? Contact me!