Enabling Reproducible builds when building NuGet packages

 
 
  • Gérald Barré

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:

csproj (MSBuild 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

PowerShell
dotnet tool update -g dotnet-validate --version 0.0.1-preview.169
dotnet validate package local package.nupkg

Result of dotnet-validate

#Additional resources

Do you have a question or a suggestion about this post? Contact me!

Follow me:
Enjoy this blog?Buy Me A Coffee💖 Sponsor on GitHub