Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .centralconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"$schema": "https://raw.githubusercontent.com/TarasKovalenko/CentralConfigGenerator/main/schemas/centralconfig.schema.json",
"conflictStrategy": "Highest",
"backup": true,
"addGitignore": true,
"failOn": "High",
"retention": { "enabled": true, "maxBackups": 5 },
"rules": {
"OutdatedPackage": "Low",
"PropertyDrift": "none"
},
"buildProperties": [
"ImplicitUsings",
"Nullable",
"LangVersion",
"InvariantGlobalization"
]
}
115 changes: 99 additions & 16 deletions .github/workflows/dotnet.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,111 @@
# This workflow will build a .NET project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net

name: .NET

on:
push:
branches: [ "main" ]
branches: ["main"]
pull_request:
branches: [ "main" ]
branches: ["main"]

permissions:
contents: read

jobs:
build:
name: Build & test (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Cache NuGet packages
uses: actions/cache@v4
with:
path: ~/.nuget/packages
key: ${{ runner.os }}-nuget-${{ hashFiles('**/Directory.Packages.props') }}
restore-keys: ${{ runner.os }}-nuget-

- name: Restore
run: dotnet restore

- name: Build
run: dotnet build --no-restore --configuration Release

- name: Test
run: dotnet test --no-build --configuration Release --verbosity normal

self-check:
name: Dependency health (dogfood)
runs-on: ubuntu-latest
needs: build
permissions:
contents: read
security-events: write

steps:
- uses: actions/checkout@v4
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Restore dependencies
run: dotnet restore
- name: Build
run: dotnet build --no-restore
- name: Test
run: dotnet test --no-build --verbosity normal
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 10.0.x

- name: Build the tool
run: dotnet build CentralConfigGenerator/CentralConfigGenerator.csproj -f net10.0 -c Release

# The tool analyses its own repository, so a regression in the analyzers shows up here first.
- name: Analyse dependencies
run: |
dotnet run --project CentralConfigGenerator/CentralConfigGenerator.csproj -f net10.0 -c Release --no-build -- \
analyze --audit --deprecated --licenses \
--output Sarif --output-file centralconfig.sarif \
--fail-on Critical --quiet

- name: Publish findings
if: always()
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: centralconfig.sarif
category: centralconfig

- name: Job summary
if: always()
run: |
dotnet run --project CentralConfigGenerator/CentralConfigGenerator.csproj -f net10.0 -c Release --no-build -- \
analyze --audit --deprecated --output Markdown --fail-on Never --quiet >> "$GITHUB_STEP_SUMMARY"

pack:
name: Pack
runs-on: ubuntu-latest
needs: build

steps:
- uses: actions/checkout@v4

- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Pack
run: dotnet pack CentralConfigGenerator/CentralConfigGenerator.csproj -c Release -o ./artifacts

- name: Upload package
uses: actions/upload-artifact@v4
with:
name: nupkg
path: ./artifacts/*.nupkg
87 changes: 87 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: release

on:
push:
tags: ['v*']
workflow_dispatch:
inputs:
version:
description: 'Version to publish, without the leading v (e.g. 1.0.0)'
required: true
type: string

permissions:
contents: write

jobs:
publish:
runs-on: ubuntu-latest
env:
CI: true
DOTNET_NOLOGO: true
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0

# The tool multi-targets net8.0/net9.0/net10.0, so every SDK has to be present.
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
dotnet-version: |
8.0.x
9.0.x
10.0.x

- name: Resolve version
id: version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
version="${{ inputs.version }}"
else
version="${GITHUB_REF_NAME#v}"
fi
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "Publishing $version"

- name: Restore
run: dotnet restore CentralConfigGenerator.sln

- name: Build
run: dotnet build CentralConfigGenerator.sln -c Release --no-restore -p:Version=${{ steps.version.outputs.version }}

- name: Test
run: dotnet test CentralConfigGenerator.sln -c Release --no-build

- name: Pack
run: |
dotnet pack CentralConfigGenerator/CentralConfigGenerator.csproj \
-c Release --no-build \
-p:Version=${{ steps.version.outputs.version }} \
-o artifacts/packages

- uses: actions/upload-artifact@v4
with:
name: packages-${{ steps.version.outputs.version }}
path: artifacts/packages/*.*nupkg

# --skip-duplicate keeps a re-run from failing when a package version is already live.
- name: Push to NuGet
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: |
if [ -z "$NUGET_API_KEY" ]; then
echo "::error::NUGET_API_KEY is not set. Add it with: gh secret set NUGET_API_KEY"
exit 1
fi
dotnet nuget push "artifacts/packages/*.nupkg" \
--api-key "$NUGET_API_KEY" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate

- name: Create GitHub release
if: github.event_name == 'push'
env:
GH_TOKEN: ${{ github.token }}
run: gh release create "${GITHUB_REF_NAME}" artifacts/packages/*.nupkg --generate-notes
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -399,4 +399,7 @@ FodyWeavers.xsd

# JetBrains Rider
*.sln.iml
.idea/
.idea/

# CentralConfigGenerator backups
.centralconfig-backups/
58 changes: 58 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Changelog

## 2.0.0

A rewrite around one idea: migrating to Central Package Management should be provable, reversible, and only the beginning of keeping dependencies healthy.

### New commands

- `verify` — migrates, then diffs the resolved package graph before and after to prove NuGet still resolves identical packages. Rolls back automatically on drift (exit code `9`).
- `analyze` — 16 dependency-health rules with a 0–100 score, severity overrides, baselines, and `--fix` / `--fix-dry-run` auto-fixes.
- `update` — updates central versions, runs the tests, and rolls back on failure. `--bisect` keeps the largest subset that still passes and names what it held back.
- `revert` — undoes CPM, writing versions back into project files.
- `doctor` — environment diagnostics: SDK, solution layout, feed reachability, config, backups, `.gitignore`.
- `status` — workspace dashboard plus the single most useful next action.
- `tree` — resolved dependency graph as an ASCII tree.
- `batch` — runs the migration across every solution in a monorepo, optionally in parallel.
- `init` — scaffolds `.centralconfig.json` (JSON schema published under `schemas/`).
- `explain` — documents any analysis rule.
- `backups list | restore | prune` — inspect and roll back any change the tool made.
- `completions` — bash, zsh, fish and PowerShell completion scripts.

### Migration improvements

- **Format-preserving edits.** Encoding, BOM, line endings, comments, indentation and attribute order all survive; only what must change changes. `--encoding` and `--linewrap` override when you want normalisation instead.
- **Dry-run with unified diffs** (`--dry-run --diff`), planned entirely in memory so the preview and the write share one code path.
- **Backups before every write**, with restore, retention and pruning. Files the operation created are recorded too, so restoring removes them.
- `--merge` into an existing `Directory.Packages.props` without disturbing its comments or layout.
- Conflict strategies `Highest`, `Lowest`, `MostCommon`, `Fail`, plus `--interactive-conflicts`.
- `--transitive-pinning`, `--ignore-prerelease`, `--version-comparison`, `--keep-attrs`.
- F# and VB projects alongside C#; `.sln`, `.slnx` and `.slnf` scoping; `--exclude-dirs`.
- Versions declared as child elements (`<Version>1.0.0</Version>`) are handled, as are `GlobalPackageReference` and `VersionOverride`.

### Directory.Build.props

- Property hoisting now requires **unanimity**: a property moves up only when every project declaring it uses the same value. Conditioned `PropertyGroup`s and identity properties (`AssemblyName`, `RootNamespace`, …) are never touched.
- Configurable candidate list via `--property` or `buildProperties` in the config file.

### Reporting and CI

- Output as Terminal, JSON, SARIF 2.1.0, Markdown or CSV, to stdout or a file.
- Stable exit codes 0–9 so CI can branch on the outcome.
- `--fail-on`, `--rules Rule=Severity`, `--baseline` and `--write-baseline` for gradual adoption.

### Platform

- Multi-targets `net8.0`, `net9.0` and `net10.0`.
- Private feeds honoured through `nuget.config`.
- 320+ tests.

### Breaking changes

- `packages-enhanced` is now an alias for `migrate`; the enhanced analysis is the default path.
- The CLI moved from `System.CommandLine` to `Spectre.Console.Cli`. Command names are unchanged and `packages` / `convert` alias `migrate`.
- `-d` is `--directory`; use `-n` or `--dry-run` for dry runs.

## 1.1.1 and earlier

See the git history.
Loading
Loading