CPMigrate
CI/CD

Use CPMigrate in CI with JSON-only output and stable exit codes

Infrastructure teams do not need another hand-parsed CLI. CPMigrate already supports JSON output, quiet mode, dependency analysis, and exit codes suitable for gates and scheduled checks.

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

GitHub Actions example

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

- name: Analyze dependency health
  run: cpmigrate --analyze --audit --outdated --deprecated --output Json --quiet > analysis.json

Gate a migration PR on the resolved graph

A migration pull request is sixty changed project files, and git diff cannot answer the only question a reviewer has: does this change what we ship? It can. --conflict-strategy Highest is the default, and when two projects disagree about a package version one of them is silently upgraded.

--verify restores before and after the migration, diffs the fully-resolved package graph per project and target framework, and attributes every version that moved to the decision that caused it. Anything unaccounted for exits 9 and rolls the migration back, so a failed job leaves the tree as it found it.

- name: Migrate and verify
  id: migrate
  run: |
    set +e
    cpmigrate -s ./MySolution.sln --verify --force --output Markdown --quiet > receipt.md
    echo "exit_code=$?" >> "$GITHUB_OUTPUT"

- name: Publish the receipt
  if: always() && hashFiles('receipt.md') != ''
  run: cat receipt.md >> "$GITHUB_STEP_SUMMARY"

- name: Require an accounted-for graph
  run: |
    code="${{ steps.migrate.outputs.exit_code }}"
    [ "$code" = "0" ] || { echo "::error::resolved graph moved unexplained (exit $code)"; exit 1; }

Add --verify-strict when the migration must be a literal no-op — then any graph change fails, even one the receipt explains. Verification costs two full solution restores, which is why it is opt-in.