{"id":17182393,"url":"https://github.com/simoncropp/packageupdate","last_synced_at":"2026-02-18T01:11:11.282Z","repository":{"id":38106866,"uuid":"193602305","full_name":"SimonCropp/PackageUpdate","owner":"SimonCropp","description":"A dotnet tool that updates packages for all solutions in a directory.","archived":false,"fork":false,"pushed_at":"2025-04-09T00:43:29.000Z","size":794,"stargazers_count":28,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-12T03:25:08.942Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SimonCropp.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"license.txt","code_of_conduct":"code_of_conduct.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"SimonCropp"}},"created_at":"2019-06-25T00:29:27.000Z","updated_at":"2025-04-09T00:41:28.000Z","dependencies_parsed_at":"2023-12-26T00:26:47.562Z","dependency_job_id":"ac3e1bc2-5d84-43de-a9e4-3a0b55c2096b","html_url":"https://github.com/SimonCropp/PackageUpdate","commit_stats":{"total_commits":763,"total_committers":5,"mean_commits":152.6,"dds":"0.49934469200524245","last_synced_commit":"944bc2b4e7d434549332efdee775a0e6a47b7e1d"},"previous_names":[],"tags_count":14,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FPackageUpdate","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FPackageUpdate/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FPackageUpdate/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SimonCropp%2FPackageUpdate/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SimonCropp","download_url":"https://codeload.github.com/SimonCropp/PackageUpdate/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248741771,"owners_count":21154372,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-15T00:36:58.778Z","updated_at":"2026-02-18T01:11:11.277Z","avatar_url":"https://github.com/SimonCropp.png","language":"C#","funding_links":["https://github.com/sponsors/SimonCropp"],"categories":[],"sub_categories":[],"readme":"\u003c!--\nGENERATED FILE - DO NOT EDIT\nThis file was generated by [MarkdownSnippets](https://github.com/SimonCropp/MarkdownSnippets).\nSource File: /readme.source.md\nTo change this file edit the source file and then run MarkdownSnippets.\n--\u003e\n\n# \u003cimg src=\"/src/icon.png\" height=\"30px\"\u003e PackageUpdate\n\n[![Build status](https://img.shields.io/appveyor/build/SimonCropp/PackageUpdate)](https://ci.appveyor.com/project/SimonCropp/PackageUpdate)\n[![NuGet Status](https://img.shields.io/nuget/v/PackageUpdate.svg)](https://www.nuget.org/packages/PackageUpdate/)\n\nA [dotnet tool](https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools) that updates packages for all solutions in a directory.\n\n**See [Milestones](../../milestones?state=closed) for release notes.**\n\n\n## Requirements/Caveats\n\n * .net SDK 10 is required. https://dotnet.microsoft.com/en-us/download\n * Only solutions using [Central Package Management (CPM)](https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management) are supported.\n\n\n## NuGet package\n\nhttps://nuget.org/packages/PackageUpdate/\n\n\n## Installation\n\nEnsure [dotnet CLI is installed](https://docs.microsoft.com/en-us/dotnet/core/tools/).\n\nInstall [PackageUpdate](https://nuget.org/packages/PackageUpdate/)\n\n```ps\ndotnet tool install -g PackageUpdate\n```\n\n## Performance characteristics\n\n73 seconds for the following scenario\n\n * 50 solutions\n * 2 NuGet sources\n * 384 nuget packages\n * Network (Mbps): 94 up / 35 down. 17ms ping\n\n\n\n## Usage\n\n```ps\npackageupdate C:\\Code\\TargetDirectory\n```\n\nIf no directory is passed the current directory will be used.\n\n\n### Arguments\n\n\n#### Target Directory\n\n```ps\npackageupdate C:\\Code\\TargetDirectory\n```\n\n```ps\npackageupdate -t C:\\Code\\TargetDirectory\n```\n\n```ps\npackageupdate --target-directory C:\\Code\\TargetDirectory\n```\n\n\n#### Package\n\nThe package name to update. If not specified, all packages will be updated.\n\n```ps\npackageupdate -p packageName\n```\n\n```ps\npackageupdate --package packageName\n```\n\n\n#### Build\n\nBuild the solution after the update\n\n```ps\npackageupdate -b\n```\n\n```ps\npackageupdate --build\n```\n\n\n### Behavior\n\n * Recursively scan the target directory for all directories containing a `.sln` file.\n * Perform a [dotnet restore](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-restore) on the directory.\n * Recursively scan the directory for `*.csproj` files.\n * Call [dotnet list package](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-list-package) to get the list of pending packages.\n * Call [dotnet add package](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package) with the package and version.\n\n\n## PackageUpdateIgnores\n\nWhen processing multiple directories, it is sometimes desirable to \"always ignore\" certain directories. This can be done by adding a `PackageUpdateIgnores` environment variable:\n\n```\nsetx PackageUpdateIgnores \"AspNetCore,EntityFrameworkCore\"\n```\n\nThe value is comma separated.\n\n\n## Add to Windows Explorer\n\nUse [context-menu.reg](/src/context-menu.reg) to add PackageUpdate to the Windows Explorer context menu.\n\n\u003c!-- snippet: context-menu.reg --\u003e\n\u003ca id='snippet-context-menu.reg'\u003e\u003c/a\u003e\n```reg\nWindows Registry Editor Version 5.00\n[HKEY_CLASSES_ROOT\\Directory\\Shell]\n@=\"none\"\n[HKEY_CLASSES_ROOT\\Directory\\shell\\packageupdate]\n\"MUIVerb\"=\"run packageupdate\"\n\"Position\"=\"bottom\"\n[HKEY_CLASSES_ROOT\\Directory\\Background\\shell\\packageupdate]\n\"MUIVerb\"=\"run packageupdate\"\n\"Position\"=\"bottom\"\n[HKEY_CLASSES_ROOT\\Directory\\shell\\packageupdate\\command]\n@=\"cmd.exe /c packageupdate \\\"%V\\\"\"\n[HKEY_CLASSES_ROOT\\Directory\\Background\\shell\\packageupdate\\command]\n@=\"cmd.exe /c packageupdate \\\"%V\\\"\"\n```\n\u003csup\u003e\u003ca href='/src/context-menu.reg#L1-L13' title='Snippet source file'\u003esnippet source\u003c/a\u003e | \u003ca href='#snippet-context-menu.reg' title='Start of snippet'\u003eanchor\u003c/a\u003e\u003c/sup\u003e\n\u003c!-- endSnippet --\u003e\n\n\n## Authenticated feed\n\nTo use authenticated feed, add the [packageSourceCredentials](https://docs.microsoft.com/en-us/nuget/reference/nuget-config-file#packagesourcecredentials) to the global nuget config:\n\n```xml\n\u003cpackageSourceCredentials\u003e\n\u003cfeedName\u003e\n    \u003cadd key=\"Username\" value=\"username\" /\u003e\n    \u003cadd key=\"ClearTextPassword\" value=\"api key\" /\u003e\n\u003c/feedName\u003e\n\u003c/packageSourceCredentials\u003e\n```\n\n\n## Package Version Pinning\n\n\n### Overview\n\nprevent specific packages from being automatically updated by adding the `Pinned=\"true\"` attribute to package entries in the `Directory.Packages.props` file.\n\n\n### Usage\n\n\n#### Pin a Single Package\n\n```xml\n\u003cProject\u003e\n  \u003cItemGroup\u003e\n    \u003cPackageVersion Include=\"System.ValueTuple\" Version=\"4.5.0\" Pinned=\"true\" /\u003e\n    \u003cPackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.1\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\nIn this example:\n\n- `System.ValueTuple` will remain at version `4.5.0` and will **not** be updated\n- `Newtonsoft.Json` will be updated to the latest version when running the updater\n\n\n#### Pin Multiple Packages\n\n```xml\n\u003cProject\u003e\n  \u003cItemGroup\u003e\n    \u003cPackageVersion Include=\"System.ValueTuple\" Version=\"4.5.0\" Pinned=\"true\" /\u003e\n    \u003cPackageVersion Include=\"Microsoft.AspNetCore.App\" Version=\"6.0.0\" Pinned=\"true\" /\u003e\n    \u003cPackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.1\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\n\n#### Document Why a Package is Pinned\n\nIt's good practice to add comments explaining why a package is pinned:\n\n```xml\n\u003cProject\u003e\n  \u003cItemGroup\u003e\n    \u003c!-- Pinned: v4.6+ breaks compatibility with .NET Framework 4.6.1 --\u003e\n    \u003cPackageVersion Include=\"System.ValueTuple\" Version=\"4.5.0\" Pinned=\"true\" /\u003e\n    \n    \u003c!-- Pinned: Newer versions require EF Core migration --\u003e\n    \u003cPackageVersion Include=\"Microsoft.EntityFrameworkCore\" Version=\"6.0.10\" Pinned=\"true\" /\u003e\n    \n    \u003cPackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.1\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\n\n### Behavior\n\n\n#### When Running Update All Packages\n\n```bash\ndotnet run -- update\n```\n\n- All packages **without** `Pinned=\"true\"` will be checked for updates\n- Pinned packages are skipped entirely\n\n\n#### When Running Update Specific Package\n\n```bash\ndotnet run -- update --package System.ValueTuple\n```\n\n- Even when explicitly targeting a pinned package, it will **not** be updated\n- The pin is always respected, regardless of how the updater is invoked\n\n\n### Common Use Cases\n\n\n#### 1. Breaking Changes\n\nPin packages when newer versions introduce breaking changes you're not ready to handle:\n\n```xml\n\u003cPackageVersion Include=\"AutoMapper\" Version=\"10.1.1\" Pinned=\"true\" /\u003e\n```\n\n\n#### 2. Framework Constraints\n\nPin packages that have specific framework version requirements:\n\n```xml\n\u003c!-- Required for .NET Framework 4.7.2 compatibility --\u003e\n\u003cPackageVersion Include=\"System.Memory\" Version=\"4.5.4\" Pinned=\"true\" /\u003e\n```\n\n\n#### 3. Security Fixes\n\nPin to a specific patched version while waiting for a proper migration:\n\n```xml\n\u003c!-- Pinned to security patch - v8.x requires major refactoring --\u003e\n\u003cPackageVersion Include=\"IdentityServer4\" Version=\"4.1.2\" Pinned=\"true\" /\u003e\n```\n\n\n#### 4. Performance Regressions\n\nPin when a newer version causes performance issues:\n\n```xml\n\u003c!-- v6.x has known performance regression in our scenario --\u003e\n\u003cPackageVersion Include=\"Dapper\" Version=\"2.0.123\" Pinned=\"true\" /\u003e\n```\n\n\n#### 5. Vendor Dependencies\n\nPin packages that must match versions used by third-party SDKs:\n\n```xml\n\u003c!-- Must match version used by Acme.ThirdPartySDK --\u003e\n\u003cPackageVersion Include=\"Newtonsoft.Json\" Version=\"12.0.3\" Pinned=\"true\" /\u003e\n```\n\n\n### Unpinning a Package\n\nTo allow a package to be updated again, remove the `Pinned=\"true\"` attribute:\n\n```xml\n\u003c!-- Before --\u003e\n\u003cPackageVersion Include=\"System.ValueTuple\" Version=\"4.5.0\" Pinned=\"true\" /\u003e\n\n\u003c!-- After --\u003e\n\u003cPackageVersion Include=\"System.ValueTuple\" Version=\"4.5.0\" /\u003e\n```\n\nThe next time you run the updater, it will update to the latest version.\n\n\n### Technical Details\n\n- The `Pinned` attribute is a custom attribute used by this updater tool\n- It has no effect on NuGet's normal package resolution\n- The attribute follows MSBuild conventions (similar to how `Pinned` works in project files)\n- Comments and formatting around pinned packages are preserved during updates\n\n\n## Automatic Package Migration\n\n\n### Overview\n\nPackageUpdate 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.\n\n\n### How It Works\n\nWhen updating packages, PackageUpdate:\n\n1. Checks if the **current version** of each package is marked as deprecated\n2. If an alternative package is specified in the deprecation metadata:\n   - Verifies the alternative package exists in configured NuGet sources\n   - Checks that the alternative doesn't already exist in `Directory.Packages.props`\n   - Replaces the package reference with the alternative\n   - Sets the version to the latest available version of the alternative\n3. Logs the migration with the deprecation reason\n\n\n### Example Migration\n\n**Before:**\n\n```xml\n\u003cProject\u003e\n  \u003cItemGroup\u003e\n    \u003cPackageVersion Include=\"WindowsAzure.Storage\" Version=\"9.3.3\" /\u003e\n    \u003cPackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.1\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\n**After running `packageupdate`:**\n\n```xml\n\u003cProject\u003e\n  \u003cItemGroup\u003e\n    \u003cPackageVersion Include=\"Azure.Storage.Common\" Version=\"12.26.0\" /\u003e\n    \u003cPackageVersion Include=\"Newtonsoft.Json\" Version=\"13.0.3\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\nConsole output:\n\n```\nMigrated WindowsAzure.Storage -\u003e Azure.Storage.Common (Version: 12.26.0) [Deprecated: Legacy]\nUpdated Newtonsoft.Json: 13.0.1 -\u003e 13.0.3\n```\n\n\n### Migration Behavior\n\n\n#### Automatic by Default\n\nMigrations happen automatically without requiring any flags or configuration. The tool detects deprecated packages and migrates them seamlessly.\n\n\n#### Pinned Packages Are Never Migrated\n\nIf a package is pinned, it will not be migrated even if it's deprecated:\n\n```xml\n\u003cPackageVersion Include=\"WindowsAzure.Storage\" Version=\"9.3.3\" Pinned=\"true\" /\u003e\n```\n\nThis package will remain unchanged.\n\n\n#### When Alternative Already Exists\n\nIf the alternative package already exists in `Directory.Packages.props`, the migration is skipped:\n\n```xml\n\u003cProject\u003e\n  \u003cItemGroup\u003e\n    \u003cPackageVersion Include=\"WindowsAzure.Storage\" Version=\"9.3.3\" /\u003e\n    \u003cPackageVersion Include=\"Azure.Storage.Common\" Version=\"12.0.0\" /\u003e\n  \u003c/ItemGroup\u003e\n\u003c/Project\u003e\n```\n\nOutput:\n\n```\nPackage WindowsAzure.Storage is deprecated with alternative Azure.Storage.Common, but alternative already exists\n```\n\nBoth packages remain in the file, and only `Azure.Storage.Common` gets updated to the latest version.\n\n\n#### When No Alternative is Available\n\nIf a package is deprecated but has no alternative specified, the tool logs a warning and continues with normal version update:\n\n```\nPackage SomeDeprecatedPackage is deprecated but has no alternative. Reasons: Legacy\n```\n\n\n#### Current Version Check\n\nThe 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.\n\n\n#### Specific Package Flag\n\nThe migration feature works with the `--package` flag:\n\n```bash\npackageupdate --package WindowsAzure.Storage\n```\n\nIf `WindowsAzure.Storage` is deprecated with an alternative, it will be migrated automatically.\n\n\n### Common Scenarios\n\n\n#### Scenario 1: Microsoft Azure SDK Packages\n\nMany older Azure SDK packages have been deprecated in favor of the new Azure SDK:\n\n- `WindowsAzure.Storage` → `Azure.Storage.Common` or `Azure.Storage.Blobs`\n- `Microsoft.Azure.Storage.Blob` → `Azure.Storage.Blobs`\n- `Microsoft.Azure.DocumentDB` → `Microsoft.Azure.Cosmos`\n\nThese migrations happen automatically when you run `packageupdate`.\n\n\n#### Scenario 2: Preventing Migration\n\nIf you want to prevent migration of a deprecated package (e.g., you're not ready to migrate yet), pin the package:\n\n```xml\n\u003c!-- Pinned: Not ready to migrate to Azure.Storage.Blobs yet --\u003e\n\u003cPackageVersion Include=\"WindowsAzure.Storage\" Version=\"9.3.3\" Pinned=\"true\" /\u003e\n```\n\n\n#### Scenario 3: Manual Review After Migration\n\nAfter automatic migration, you may want to:\n\n1. Review the changes in `Directory.Packages.props`\n2. Update your code to use the new package's API (if breaking changes exist)\n3. Test thoroughly before committing\n\nThe migration updates the package reference but doesn't modify your source code.\n\n\n### Logging\n\n\n#### Successful Migration\n\n```\nMigrated WindowsAzure.Storage -\u003e Azure.Storage.Common (Version: 12.26.0) [Deprecated: Legacy]\n```\n\n\n#### Migration Skipped (Alternative Exists)\n\n```\nPackage WindowsAzure.Storage is deprecated with alternative Azure.Storage.Common, but alternative already exists\n```\n\n\n#### Migration Skipped (No Alternative)\n\n```\nPackage MyOldPackage is deprecated but has no alternative. Reasons: Legacy, Critical Bugs\n```\n\n\n#### Migration Skipped (Alternative Not Found)\n\n```\nPackage OldPackage is deprecated with alternative NewPackage, but alternative not found in sources\n```\n\n\n### Technical Details\n\n- Migration uses NuGet's official deprecation metadata API (`PackageDeprecationMetadata`)\n- The `AlternatePackage` information comes directly from package authors via NuGet.org\n- File formatting, comments, and XML structure are preserved during migration\n- Migrations are logged distinctly from version updates for clarity\n\n\n## Icon\n\n[Update](https://thenounproject.com/search/?q=update\u0026i=2060555) by [Andy Miranda](https://thenounproject.com/andylontuan88) from [The Noun Project](https://thenounproject.com/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Fpackageupdate","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimoncropp%2Fpackageupdate","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimoncropp%2Fpackageupdate/lists"}