A dotnet tool that updates packages for all solutions in a directory.
See Milestones for release notes.
- .net SDK 10 is required. https://dotnet.microsoft.com/en-us/download
- Only solutions using Central Package Management (CPM) are supported.
https://nuget.org/packages/PackageUpdate/
Ensure dotnet CLI is installed.
Install PackageUpdate
dotnet tool install -g PackageUpdate73 seconds for the following scenario
- 50 solutions
- 2 NuGet sources
- 384 nuget packages
- Network (Mbps): 94 up / 35 down. 17ms ping
packageupdate C:\Code\TargetDirectoryIf no directory is passed the current directory will be used.
packageupdate C:\Code\TargetDirectorypackageupdate -t C:\Code\TargetDirectorypackageupdate --target-directory C:\Code\TargetDirectoryThe package name to update. If not specified, all packages will be updated.
packageupdate -p packageNamepackageupdate --package packageNameBuild the solution after the update
packageupdate -bpackageupdate --build- Recursively scan the target directory for all directories containing a
.slnfile. - Perform a dotnet restore on the directory.
- Recursively scan the directory for
*.csprojfiles. - Call dotnet list package to get the list of pending packages.
- Call dotnet add package with the package and version.
When processing multiple directories, it is sometimes desirable to "always ignore" certain directories. This can be done by adding a PackageUpdateIgnores environment variable:
setx PackageUpdateIgnores "AspNetCore,EntityFrameworkCore"
The value is comma separated.
Use context-menu.reg to add PackageUpdate to the Windows Explorer context menu.
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Shell]
@="none"
[HKEY_CLASSES_ROOT\Directory\shell\packageupdate]
"MUIVerb"="run packageupdate"
"Position"="bottom"
[HKEY_CLASSES_ROOT\Directory\Background\shell\packageupdate]
"MUIVerb"="run packageupdate"
"Position"="bottom"
[HKEY_CLASSES_ROOT\Directory\shell\packageupdate\command]
@="cmd.exe /c packageupdate \"%V\""
[HKEY_CLASSES_ROOT\Directory\Background\shell\packageupdate\command]
@="cmd.exe /c packageupdate \"%V\""To use authenticated feed, add the packageSourceCredentials to the global nuget config:
<packageSourceCredentials>
<feedName>
<add key="Username" value="username" />
<add key="ClearTextPassword" value="api key" />
</feedName>
</packageSourceCredentials>prevent specific packages from being automatically updated by adding the Pinned="true" attribute to package entries in the Directory.Packages.props file.
<Project>
<ItemGroup>
<PackageVersion Include="System.ValueTuple" Version="4.5.0" Pinned="true" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>In this example:
System.ValueTuplewill remain at version4.5.0and will not be updatedNewtonsoft.Jsonwill be updated to the latest version when running the updater
<Project>
<ItemGroup>
<PackageVersion Include="System.ValueTuple" Version="4.5.0" Pinned="true" />
<PackageVersion Include="Microsoft.AspNetCore.App" Version="6.0.0" Pinned="true" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>It's good practice to add comments explaining why a package is pinned:
<Project>
<ItemGroup>
<!-- Pinned: v4.6+ breaks compatibility with .NET Framework 4.6.1 -->
<PackageVersion Include="System.ValueTuple" Version="4.5.0" Pinned="true" />
<!-- Pinned: Newer versions require EF Core migration -->
<PackageVersion Include="Microsoft.EntityFrameworkCore" Version="6.0.10" Pinned="true" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>dotnet run -- update- All packages without
Pinned="true"will be checked for updates - Pinned packages are skipped entirely
dotnet run -- update --package System.ValueTuple- Even when explicitly targeting a pinned package, it will not be updated
- The pin is always respected, regardless of how the updater is invoked
Pin packages when newer versions introduce breaking changes you're not ready to handle:
<PackageVersion Include="AutoMapper" Version="10.1.1" Pinned="true" />Pin packages that have specific framework version requirements:
<!-- Required for .NET Framework 4.7.2 compatibility -->
<PackageVersion Include="System.Memory" Version="4.5.4" Pinned="true" />Pin to a specific patched version while waiting for a proper migration:
<!-- Pinned to security patch - v8.x requires major refactoring -->
<PackageVersion Include="IdentityServer4" Version="4.1.2" Pinned="true" />Pin when a newer version causes performance issues:
<!-- v6.x has known performance regression in our scenario -->
<PackageVersion Include="Dapper" Version="2.0.123" Pinned="true" />Pin packages that must match versions used by third-party SDKs:
<!-- Must match version used by Acme.ThirdPartySDK -->
<PackageVersion Include="Newtonsoft.Json" Version="12.0.3" Pinned="true" />To allow a package to be updated again, remove the Pinned="true" attribute:
<!-- Before -->
<PackageVersion Include="System.ValueTuple" Version="4.5.0" Pinned="true" />
<!-- After -->
<PackageVersion Include="System.ValueTuple" Version="4.5.0" />The next time you run the updater, it will update to the latest version.
- The
Pinnedattribute is a custom attribute used by this updater tool - It has no effect on NuGet's normal package resolution
- The attribute follows MSBuild conventions (similar to how
Pinnedworks in project files) - Comments and formatting around pinned packages are preserved during updates
PackageUpdate automatically detects and migrates deprecated NuGet packages to their recommended alternatives. When a package is marked as deprecated on NuGet.org with an alternative package specified, the tool will automatically replace it during updates.
When updating packages, PackageUpdate:
- Checks if the current version of each package is marked as deprecated
- If an alternative package is specified in the deprecation metadata:
- Verifies the alternative package exists in configured NuGet sources
- Checks that the alternative doesn't already exist in
Directory.Packages.props - Replaces the package reference with the alternative
- Sets the version to the latest available version of the alternative
- Logs the migration with the deprecation reason
Before:
<Project>
<ItemGroup>
<PackageVersion Include="WindowsAzure.Storage" Version="9.3.3" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>
</Project>After running packageupdate:
<Project>
<ItemGroup>
<PackageVersion Include="Azure.Storage.Common" Version="12.26.0" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup>
</Project>Console output:
Migrated WindowsAzure.Storage -> Azure.Storage.Common (Version: 12.26.0) [Deprecated: Legacy]
Updated Newtonsoft.Json: 13.0.1 -> 13.0.3
Migrations happen automatically without requiring any flags or configuration. The tool detects deprecated packages and migrates them seamlessly.
If a package is pinned, it will not be migrated even if it's deprecated:
<PackageVersion Include="WindowsAzure.Storage" Version="9.3.3" Pinned="true" />This package will remain unchanged.
If the alternative package already exists in Directory.Packages.props, the migration is skipped:
<Project>
<ItemGroup>
<PackageVersion Include="WindowsAzure.Storage" Version="9.3.3" />
<PackageVersion Include="Azure.Storage.Common" Version="12.0.0" />
</ItemGroup>
</Project>Output:
Package WindowsAzure.Storage is deprecated with alternative Azure.Storage.Common, but alternative already exists
Both packages remain in the file, and only Azure.Storage.Common gets updated to the latest version.
If a package is deprecated but has no alternative specified, the tool logs a warning and continues with normal version update:
Package SomeDeprecatedPackage is deprecated but has no alternative. Reasons: Legacy
The tool only migrates if the current version you're using is deprecated. If you're on an older, non-deprecated version, and only newer versions are deprecated, no migration occurs. This prevents unnecessary migrations when you're deliberately staying on an older version.
The migration feature works with the --package flag:
packageupdate --package WindowsAzure.StorageIf WindowsAzure.Storage is deprecated with an alternative, it will be migrated automatically.
Many older Azure SDK packages have been deprecated in favor of the new Azure SDK:
WindowsAzure.Storage→Azure.Storage.CommonorAzure.Storage.BlobsMicrosoft.Azure.Storage.Blob→Azure.Storage.BlobsMicrosoft.Azure.DocumentDB→Microsoft.Azure.Cosmos
These migrations happen automatically when you run packageupdate.
If you want to prevent migration of a deprecated package (e.g., you're not ready to migrate yet), pin the package:
<!-- Pinned: Not ready to migrate to Azure.Storage.Blobs yet -->
<PackageVersion Include="WindowsAzure.Storage" Version="9.3.3" Pinned="true" />After automatic migration, you may want to:
- Review the changes in
Directory.Packages.props - Update your code to use the new package's API (if breaking changes exist)
- Test thoroughly before committing
The migration updates the package reference but doesn't modify your source code.
Migrated WindowsAzure.Storage -> Azure.Storage.Common (Version: 12.26.0) [Deprecated: Legacy]
Package WindowsAzure.Storage is deprecated with alternative Azure.Storage.Common, but alternative already exists
Package MyOldPackage is deprecated but has no alternative. Reasons: Legacy, Critical Bugs
Package OldPackage is deprecated with alternative NewPackage, but alternative not found in sources
- Migration uses NuGet's official deprecation metadata API (
PackageDeprecationMetadata) - The
AlternatePackageinformation comes directly from package authors via NuGet.org - File formatting, comments, and XML structure are preserved during migration
- Migrations are logged distinctly from version updates for clarity
Update by Andy Miranda from The Noun Project.