Skip to content

A beautiful CLI tool to automatically migrate .NET solutions to NuGet Central Package Management (CPM). Handles version conflicts, backups, and dry-runs.

License

Notifications You must be signed in to change notification settings

georgepwall1991/CPMigrate

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

170 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

CPMigrate — .NET Central Package Management Migration, Analysis & Update Tool

CPMigrate Logo — .NET NuGet Central Package Management CLI Tool
CPMigrate Banner — Migrate, Analyze, and Update NuGet Packages

.NET License: MIT NuGet Downloads

Migrate to NuGet Central Package Management | Analyze dependency health | Update packages safely | Auto-fix issues

CPMigrate Interactive Wizard


Why CPMigrate?

Managing NuGet dependencies across large .NET solutions is painful. Version drift, duplicated references, transitive conflicts, and security vulnerabilities accumulate silently until they break your build or compromise your supply chain.

CPMigrate is a .NET global tool that automates the migration to NuGet Central Package Management (CPM), analyzes your entire dependency graph, auto-fixes common issues, and keeps packages up to date — with automatic rollback when tests fail.

What it does

  • Migrates any .NET solution to CPM by generating Directory.Packages.props and cleaning .csproj files
  • Analyzes dependency health: version inconsistencies, duplicates, redundant references, transitive conflicts, framework alignment, and security vulnerabilities
  • Auto-fixes detected issues with a single command
  • Updates NuGet packages to latest versions, runs dotnet test, and rolls back automatically on failure
  • Unifies repeated project properties into Directory.Build.props
  • Batch processes monorepos with dozens of solutions in parallel
  • Supports .sln and the new .slnx format (Visual Studio 17.10+)

Table of Contents


Installation

.NET Global Tool (Recommended)

Requires .NET SDK 8.0 or later. Targets .NET 10 with LatestMajor roll-forward.

dotnet tool install --global CPMigrate

Update to the latest version:

cpmigrate --update

Or via the .NET CLI:

dotnet tool update --global CPMigrate

Note: NuGet indexing may take up to 15 minutes after a new release. Clear your HTTP cache if updates aren't found immediately: dotnet nuget locals http-cache --clear

From Source

git clone https://github.com/georgepwall1991/CPMigrate.git
cd CPMigrate
dotnet build

Quick Start

Interactive Mode (Recommended for first-time users)

cpmigrate

Launches the Mission Control dashboard — a step-by-step wizard that guides you through migration, analysis, package updates, rollback, and more. The wizard adapts to your environment: when CPM is already enabled, it offers Update NuGet Packages as a quick action with prompts for transitive dependencies, pre-release versions, and dry-run mode.

Migrate a solution to CPM

cpmigrate -s ./MySolution.sln

Preview changes without modifying files

cpmigrate -s ./MySolution.sln --dry-run

Analyze dependency health

cpmigrate --analyze

Update all packages to latest versions

cpmigrate --update-packages

# Include transitive dependencies
cpmigrate --update-packages --transitive

60-Second Quickstart

# 1) Scan for issues (CI-safe exit codes)
cpmigrate --analyze --audit --outdated --deprecated --output Json --quiet > analysis.json

# 2) Preview migration
cpmigrate -s ./MySolution.sln --dry-run

# 3) Migrate to CPM
cpmigrate -s ./MySolution.sln

# 4) Safely update packages with rollback protection
cpmigrate --update-packages --dry-run

Features

CPM Migration

Scans your .sln or .slnx file, extracts all <PackageReference> entries from .csproj / .fsproj / .vbproj files, resolves version conflicts, and generates a centralized Directory.Packages.props.

# Standard migration
cpmigrate -s ./MySolution.sln

# Merge into an existing Directory.Packages.props
cpmigrate -s ./MySolution.sln --merge

# Fail if any version conflicts exist (strict mode)
cpmigrate -s ./MySolution.sln --conflict-strategy Fail

# Prompt for each conflict interactively
cpmigrate -s ./MySolution.sln --interactive-conflicts

Conflict resolution strategies:

Strategy Behavior
Highest Use the highest version found across projects (default)
Lowest Use the lowest version found
Fail Exit with error if any package has conflicting versions

Dependency Analysis

Run 7 built-in analyzers without modifying any files:

cpmigrate --analyze
Analyzer What it detects
Version Inconsistencies Same package with different versions across projects
Duplicate Packages Same package referenced with different casing (e.g., Newtonsoft.Json vs newtonsoft.json)
Redundant References Same package referenced multiple times within a single project
Transitive Conflicts Transitive dependencies with divergent versions across projects
Framework Alignment Projects targeting different TargetFramework values
Redundant Direct References Explicit references already provided transitively (lifting candidates)
Security Vulnerabilities Known CVEs in direct and transitive dependencies (requires --audit)
# Include transitive dependencies in analysis
cpmigrate --analyze --transitive

# Include security vulnerability scanning
cpmigrate --analyze --audit

# Full analysis with all checks
cpmigrate --analyze --transitive --audit

# Include outdated + deprecated checks
cpmigrate --analyze --outdated --deprecated

Auto-Fix

Automatically fix detected issues:

# Fix all auto-fixable issues
cpmigrate --analyze --fix

# Preview what fixes would be applied
cpmigrate --analyze --fix-dry-run

Available fixers:

Fixer What it fixes
Version Inconsistency Fixer Standardizes package versions across projects using the configured conflict strategy
Duplicate Package Casing Fixer Normalizes package name casing to the most common variant
Redundant Reference Fixer Removes duplicate <PackageReference> entries within the same project
Transitive Conflict Pinner Pins divergent transitive dependencies in Directory.Packages.props

Package Updates with Test Verification

New in v3.0. Update all NuGet packages to their latest versions with automatic test verification and rollback. v3.2 adds full support in the interactive wizard — run cpmigrate -i and select "Update NuGet packages" from the maintenance menu.

# Preview available updates
cpmigrate --update-packages --dry-run

# Update packages, run tests, rollback on failure
cpmigrate --update-packages

# Include pre-release versions
cpmigrate --update-packages --include-prerelease

# Or use the interactive wizard (v3.2+)
cpmigrate -i

Transitive Dependency Pinning (v3.1)

New in v3.1. Add --transitive to also scan and pin transitive (indirect) dependencies:

# Preview direct + transitive updates
cpmigrate --update-packages --transitive --dry-run

# Update direct packages and pin transitive deps
cpmigrate --update-packages --transitive

# With pre-release versions
cpmigrate --update-packages --transitive --include-prerelease

When --transitive is enabled, CPMigrate:

  • Scans all projects via dotnet list package --include-transitive
  • Deduplicates across projects (picks the highest resolved version)
  • Excludes transitive deps already managed as direct dependencies
  • Queries NuGet for the latest version of each transitive dep
  • Shows separate DIRECT UPDATES and TRANSITIVE UPDATES sections
  • Pins accepted transitive updates as new <PackageVersion> entries in Directory.Packages.props

Per-project scan failures are logged and skipped gracefully. If all scans fail, the tool continues with direct-only updates.

How it works:

  1. Reads current versions from Directory.Packages.props
  2. Queries the NuGet API for latest versions (8 concurrent lookups)
  3. Optionally scans transitive dependencies (--transitive)
  4. Shows a table of available updates (separate sections for direct and transitive)
  5. For major version bumps, prompts you interactively: accept or skip
  6. Minor/patch updates are auto-accepted
  7. Creates a backup of Directory.Packages.props
  8. Applies version updates and transitive pins atomically
  9. Runs dotnet restore then dotnet test
  10. Tests pass — keeps updates, cleans up backup
  11. Tests fail — rolls back all changes (including transitive pins) automatically

Requires CPM to be enabled. If Directory.Packages.props doesn't exist, run cpmigrate first to migrate. Transitive scanning requires dotnet restore to have been run beforehand.


Directory.Build.props Unification

Promote repeated properties and items from individual project files into a shared Directory.Build.props:

# Preview what would be unified
cpmigrate --unify-props --dry-run

# Apply unification
cpmigrate --unify-props

# Skip confirmation prompt
cpmigrate --unify-props --force

Identifies properties and items present in at least 60% of projects with the same value (e.g., TargetFramework, ImplicitUsings, Nullable, Authors) and migrates them to the root-level file. Individual project files are cleaned up automatically.


Batch Processing

Process multiple solutions in a monorepo:

# Sequential processing
cpmigrate --batch /path/to/repo

# Parallel processing (uses all CPU cores)
cpmigrate --batch /path/to/repo --batch-parallel

# Continue on failure
cpmigrate --batch /path/to/repo --batch-parallel --batch-continue

Recursively discovers .sln and .slnx files, excluding common non-project directories (node_modules, bin, obj, .git, etc.). Each solution gets an isolated backup directory to prevent collisions.


Backup & Rollback

Every migration creates a timestamped backup. Roll back at any time:

# Rollback to previous state
cpmigrate --rollback

# List all backups
cpmigrate --list-backups

# Prune old backups, keeping the last 3
cpmigrate --prune-backups --retention 3

# Delete all backups
cpmigrate --prune-all

Backups are stored in .cpmigrate_backup/ with a JSON manifest for reliable restoration. Use --add-gitignore to automatically add the backup directory to .gitignore.


Configuration File

Create a .cpmigrate.json in your repository root to set default options:

{
  "$schema": "https://raw.githubusercontent.com/georgepwall1991/CPMigrate/main/schemas/cpmigrate.schema.json",
  "ConflictStrategy": "Highest",
  "Backup": true,
  "BackupDir": ".",
  "AddGitignore": true,
  "MergeExisting": false,
  "OutputFormat": "Terminal",
  "Retention": {
    "Enabled": true,
    "MaxBackups": 5
  }
}

The config file is discovered by walking up from the current directory. CLI arguments always take precedence over config file values.


CLI Reference

Migration & Core

Option Short Default Description
--solution -s . Path to .sln / .slnx file or directory
--project -p Path to a specific project file
--output-dir -o . Output directory for Directory.Packages.props
--dry-run -d false Preview changes without modifying files
--merge false Merge into existing Directory.Packages.props
--conflict-strategy Highest Version conflict resolution: Highest, Lowest, Fail
--interactive-conflicts false Prompt for each version conflict
--keep-attrs -k false Keep Version attributes in project files
--interactive -i false Launch the interactive Mission Control wizard (migration, analysis, package updates, rollback, batch, backups)

Analysis & Auto-Fix

Option Short Default Description
--analyze -a false Run dependency health analysis
--transitive false Include transitive dependencies
--audit false Include security vulnerability scanning
--outdated false Include outdated package checks
--deprecated false Include deprecated package checks
--fix false Apply auto-fixes (requires --analyze)
--fix-dry-run false Preview auto-fixes without applying

Package Updates (v3.0+)

Option Default Description
--update-packages false Update all packages to latest, run tests, rollback on failure
--transitive false Also scan and pin transitive dependencies (v3.1)
--include-prerelease false Include pre-release versions when updating

Modernization

Option Default Description
--unify-props false Migrate common properties to Directory.Build.props
--force false Skip confirmation prompts

Batch Processing

Option Default Description
--batch Directory to scan recursively for solutions
--batch-parallel false Process solutions in parallel
--batch-continue false Continue even if a solution fails

Backup & Rollback

Option Short Default Description
--rollback -r false Restore from most recent backup
--no-backup -n false Disable backup creation
--backup-dir . Backup directory location
--list-backups false List all available backups
--prune-backups false Delete old backups based on --retention
--prune-all false Delete all backups
--retention 5 Number of backups to keep when pruning
--add-gitignore false Add backup directory to .gitignore

Output & Logging

Option Short Default Description
--output Terminal Output format: Terminal or Json
--output-file Write JSON output to a file
--quiet -q false Suppress non-essential output
--verbose -v false Enable diagnostic logging to cpmigrate.log

Self-Update

Option Default Description
--update false Check for and install the latest version of CPMigrate

Exit Codes

Code Name Meaning
0 Success Operation completed successfully
1 ValidationError Invalid command-line options
2 FileOperationError File I/O or permission failure
3 VersionConflict Unresolvable version conflict (with --conflict-strategy Fail)
4 NoProjectsFound No .csproj / .fsproj / .vbproj files discovered
5 AnalysisIssuesFound Analysis detected issues (useful for CI gates)
6 UnexpectedError Unhandled exception
7 TestFailure Tests failed after package update (rollback performed)

CI/CD Integration

Strict JSON Contract Mode

Use --output Json --quiet to guarantee JSON-only stdout (no banners/preamble), which is ideal for CI parsing.

# analyze
cpmigrate --analyze --audit --outdated --deprecated --output Json --quiet > analyze.json

# migrate
cpmigrate -s ./MySolution.sln --dry-run --output Json --quiet > migrate.json

# rollback
cpmigrate --rollback --backup-dir . --output Json --quiet > rollback.json

# update-packages
cpmigrate --update-packages --dry-run --output Json --quiet > update-packages.json

GitHub Actions Example

- name: Install CPMigrate
  run: dotnet tool install --global CPMigrate

- name: Check dependency health
  run: cpmigrate --analyze --audit --output Json --output-file analysis.json --quiet

- name: Fail on issues
  run: |
    EXIT_CODE=$(cpmigrate --analyze --audit --quiet; echo $?)
    if [ $EXIT_CODE -eq 5 ]; then
      echo "::error::Dependency issues detected"
      exit 1
    fi

JSON Output

Use --output Json to produce machine-readable output for CI/CD pipelines:

cpmigrate --analyze --output Json --output-file report.json

Examples & Benchmarks

  • Starter example: examples/small-solution/
  • Monorepo example: examples/monorepo/
  • Benchmark table: docs/benchmarks.md

These sample repositories are designed for onboarding, CI templates, and reproducible before/after conversion demos.


Release Cadence

  • Stable releases: weekly, versioned and changeloged
  • Release candidates (RC): published for fast feedback before stable promotion
  • Change log source of truth: CHANGELOG.md
  • Policy details: docs/release-cadence.md

Telemetry (Opt-in)

CPMigrate supports privacy-first telemetry that is disabled by default.

  • Enable by setting CPMIGRATE_TELEMETRY_OPT_IN=true
  • Captures only command-level metrics (operation, duration, exit code category, high-level flags)
  • Captures no project paths, package names, file contents, or source code
  • Stores local events at ~/.cpmigrate/telemetry/events.ndjson

Community Growth

  • Enable GitHub Discussions and pin:
    • Start here (first-run flow + CI JSON mode)
    • Success stories (before/after migration outcomes)
  • Use Discussions as the primary intake for usage feedback and roadmap voting.

Gallery

Mission Control Dashboard

CPMigrate Interactive The interactive wizard assessing migration risk and guiding you through each step.

Risk Analysis & Dry Run

CPMigrate Demo Previewing changes safely before committing.

Security & Package Analysis

CPMigrate Analyze Scanning for vulnerabilities and redundant dependencies.


Contributing

Contributions are welcome. To get started:

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/my-feature)
  3. Write tests for your changes
  4. Ensure all 487+ tests pass (dotnet test)
  5. Open a Pull Request

License

Distributed under the MIT License. See LICENSE for more information.

Author

George Wall@georgepwall1991

About

A beautiful CLI tool to automatically migrate .NET solutions to NuGet Central Package Management (CPM). Handles version conflicts, backups, and dry-runs.

Topics

Resources

License

Code of conduct

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •