Make Sitecore 10.3 → 10.4 Upgrades Easier with Central Package Management

SitecoreNuGetCentral Package ManagementUpgrade.NETDevOpsPackage ManagementSitecore 10.4
By , Senior Full-Stack EngineerUpdated 6 min read

Most of the work in a Sitecore 10.3 to 10.4.1 upgrade is version numbers. A Helix-style solution has dozens of projects, each referencing some mix of Sitecore.Kernel, Sitecore.Mvc, and the Sitecore.XA.* packages, and every one of those references carries its own version. Some live in Version attributes. Others hide in the Update pattern Sitecore projects tend to use. Miss one and the solution builds against two Sitecore versions at once, which shows up later as a binding error at runtime.

NuGet's Central Package Management (CPM) moves every version into a single Directory.Packages.props file at the solution root. Projects reference packages by name only. Once it's in place, the upgrade is a find-and-replace in one file.

Here's the setup, what changes in each project file, and what the upgrade looks like afterward.

What CPM Needs

CPM works with any project that uses <PackageReference>, including older non-SDK-style .csproj files on .NET Framework. Projects still on packages.config need migrating to PackageReference first.

Step 1: Create Directory.Packages.props

Create the file at the solution root, either by hand or with dotnet new packagesprops. It turns CPM on and declares every package version the solution uses:

<Project>
  <PropertyGroup>
    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
  </PropertyGroup>

  <ItemGroup Label="Microsoft">
    <PackageVersion Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="4.1.0" />
  </ItemGroup>

  <ItemGroup Label="Sitecore Core - keep aligned with SXA">
    <PackageVersion Include="Sitecore.Kernel" Version="10.4.1" />
    <PackageVersion Include="Sitecore.Mvc" Version="10.4.1" />
  </ItemGroup>

  <ItemGroup Label="Sitecore SXA - must match Core">
    <PackageVersion Include="Sitecore.XA.Foundation.IOC" Version="10.4.1" />
    <PackageVersion Include="Sitecore.XA.Foundation.MarkupDecorator" Version="10.4.1" />
    <PackageVersion Include="Sitecore.XA.Foundation.Multisite" Version="10.4.1" />
    <PackageVersion Include="Sitecore.XA.Foundation.Mvc" Version="10.4.1" />
    <PackageVersion Include="Sitecore.XA.Foundation.RenderingVariants" Version="10.4.1" />
    <PackageVersion Include="Sitecore.XA.Foundation.Variants.Abstractions" Version="10.4.1" />
  </ItemGroup>
</Project>

The Label attributes are optional, but they turn a long file into something you can scan. Group by vendor, and put the alignment rule in the label so the next person sees it before they bump one group without the other.

Step 2: Strip Versions From Every Project File

This is the tedious part. Every PackageReference loses its Version, and the Update entries go away entirely:

<!-- BEFORE -->
<ItemGroup>
  <PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" Version="4.1.0" />
  <PackageReference Include="Sitecore.Kernel" />
  <PackageReference Include="Sitecore.Mvc" />
  <PackageReference Include="Sitecore.XA.Foundation.Mvc" Version="10.4.1" />
</ItemGroup>
<ItemGroup>
  <!-- The Update pattern common in Sitecore projects -->
  <PackageReference Update="Sitecore.Kernel" Version="10.4.1" />
  <PackageReference Update="Sitecore.Mvc" Version="10.4.1" />
</ItemGroup>

<!-- AFTER -->
<ItemGroup>
  <PackageReference Include="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" />
  <PackageReference Include="Sitecore.Kernel" />
  <PackageReference Include="Sitecore.Mvc" />
  <PackageReference Include="Sitecore.XA.Foundation.Mvc" />
</ItemGroup>

In each project:

  • Remove every Version attribute from PackageReference elements.
  • Delete the PackageReference Update entries.
  • Add an Include reference for any package that only appeared as an Update.
  • Keep other metadata such as PrivateAssets and IncludeAssets.

On a large solution, script this. CentralisedPackageConverter does the conversion automatically, and its output is a good starting point to review by hand.

Step 3: Restore and Build

dotnet clean
dotnet restore
dotnet build

Anything you missed shows up here as a restore error, which is the point. A missed version under CPM fails loudly at restore time instead of quietly at runtime.

Expect NU1507 on a Sitecore solution

Sitecore packages come from Sitecore's own feed, so a Sitecore solution has at least two package sources. CPM logs warning NU1507 whenever more than one source is configured, and if you treat warnings as errors, that breaks the build. The fix is package source mapping in nuget.config, which tells NuGet which feed each package comes from:

<!-- nuget.config -->
<configuration>
  <packageSources>
    <clear />
    <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
    <add key="Sitecore" value="https://nuget.sitecore.com/resources/v3/index.json" />
  </packageSources>
  <packageSourceMapping>
    <packageSource key="nuget.org">
      <package pattern="*" />
    </packageSource>
    <packageSource key="Sitecore">
      <package pattern="Sitecore.*" />
    </packageSource>
  </packageSourceMapping>
</configuration>

The more specific pattern wins, so Sitecore.* packages resolve from Sitecore's feed and everything else from nuget.org. It's also a small supply-chain win: a package named like a Sitecore package can't be pulled from the wrong feed.

The Upgrade, Afterward

Without CPM, the 10.3 to 10.4.1 upgrade means opening every project file, finding each 10.3.0 (in Version attributes and Update entries), replacing it, and then chasing the restore errors from the ones you missed.

With CPM:

  1. Open Directory.Packages.props.
  2. Replace 10.3.0 with 10.4.1.
  3. Restore, build, and run your tests.

The pull request for the version bump is a one-file diff. Reviewers can see every version that moved in one place, and they can see that the Sitecore Core and SXA groups moved together. Security patches for third-party packages work the same way: change the version once, and every project picks it up.

Patterns Worth Adding

Keep all Sitecore packages on one version. Upgrade every Sitecore.* and Sitecore.XA.* package together. Mixed versions cause binding issues that are hard to trace back to their cause, and with CPM they're easy to avoid because the whole set sits in two adjacent groups.

Use GlobalPackageReference for tools every project needs. Analyzers and versioning packages can be declared once in Directory.Packages.props and NuGet applies them to every project as a development-only dependency. This needs NuGet 6.4 or later (Visual Studio 2022 17.4+).

<ItemGroup Label="Global - applied to every project">
  <GlobalPackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0" />
</ItemGroup>

Comment every deliberate pin. Some packages have to stay behind for compatibility. Say why in the file, so nobody "fixes" it during the next upgrade:

<ItemGroup Label="Pinned for compatibility">
  <!-- 3.x breaks the custom serializer in Foundation.Indexing. Revisit after the 10.4 upgrade. -->
  <PackageVersion Include="LegacyLibrary" Version="2.1.0" />
</ItemGroup>

Override a single project when you have to. A project that genuinely needs a different version can use VersionOverride on its PackageReference. Use it sparingly, since every override is a version that isn't in the central file.

Troubleshooting

NU1008: "Projects that use central package version management should not define the version on the PackageReference items." A project still has a Version attribute. Remove it and make sure the package has a PackageVersion entry.

Restore can't find a version for a package. The package has no PackageVersion entry in Directory.Packages.props. Add one, or check for a typo between the props file and the project file.

A package disappeared after the conversion. It was only referenced through an Update entry. Add an Include reference to the projects that need it.

Stale results after fixing everything. Clear the local caches with dotnet nuget locals all --clear and restore again.

Do It Before the Next Upgrade

The conversion takes some time on a big solution, and most of it is the project-file pass in Step 2. After that, every later upgrade and every security patch is a change to one file. If a Sitecore upgrade is on your roadmap, do the CPM conversion first, as its own pull request, so the upgrade itself is just the version bump.

For the full reference, Microsoft's Central Package Management documentation covers transitive pinning, per-framework versions, and the rest of the options.

Contact

Drop me a line. I read everything and reply within one business day.

Required fields are marked “(required)”.