Skip to content

chore: adopt Central Package Management so a bump is one file (#684) - #689

Merged
mforce merged 1 commit into
mainfrom
chore/central-package-management
Sep 4, 2026
Merged

mforce merged 1 commit into
mainfrom
chore/central-package-management

Conversation

@mforce

@mforce mforce commented Sep 4, 2026 •

Copy link
Copy Markdown
Owner

Closes #684.

What

Every NuGet version moves out of the nine .csproj files into a new Directory.Packages.props at the repo root. The csproj files keep bare PackageReference elements (child PrivateAssets/IncludeAssets untouched). Directory.Build.props is not repurposed; it still only sets RestorePackagesWithLockFile.

This is a refactor: no version changes hands. 33 distinct packages, zero cross-project disagreements (re-verified by script on 62107f2c), every floating range and every pin kept exactly. No GlobalPackageReference.

Acceptance evidence

Lock files are not byte-identical, and cannot be under CPM. Enabling ManagePackageVersionsCentrally makes NuGet write lock format "version": 2 and relabel transitive packages that carry a PackageVersion entry as CentralTransitive. That reclassification is the whole diff. Per-entry comparison of old vs new locks, all nine projects:

Check Result
Lock entries compared 443
Entries with different resolved, contentHash or dependencies 0
Package set per TFM identical
Type changes other than Transitive → CentralTransitive 0
Lock version 1 → 2 in all 9

Script used (run from the branch, compares against HEAD~1 = main):

import json,subprocess,glob
for f in sorted(glob.glob('src/*/packages.lock.json')+glob.glob('tests/*/packages.lock.json')):
    old=json.loads(subprocess.check_output(['git','show','HEAD~1:'+f])); new=json.load(open(f))
    for tfm in old['dependencies']:
        o,n=old['dependencies'][tfm],new['dependencies'][tfm]
        assert set(o)==set(n)
        for p in o:
            for k in ('resolved','contentHash','dependencies'):
                assert o[p].get(k)==n[p].get(k),(f,p,k)
            assert o[p]['type']==n[p]['type'] or (o[p]['type'],n[p]['type'])==('Transitive','CentralTransitive'),(f,p)
print("ok")
  • No .csproj contains Version= on a PackageReference
  • dotnet build Cluckwork.sln: 0 warnings, 0 errors
  • dotnet test Cluckwork.sln: 2278 passed (Domain 365, AppHost 10, Application 234, Api.Integration 1669), matching baseline
  • dotnet restore Cluckwork.sln --locked-mode passes on the committed locks

Two traps not in the brief, both handled here

  1. NU1011. CPM rejects floating ranges (10.*, 1.*, ...) unless CentralPackageFloatingVersionsEnabled=true. Set in the props file with a comment; the committed locks, not the ranges, pin what restores.
  2. Restore inputs named explicitly elsewhere. These all named the csproj or Directory.Build.props and now name Directory.Packages.props too:
    • src/Cluckwork.Api/Dockerfile restore layer (COPY before dotnet restore --locked-mode); docker build --target build verified locally.
    • ci.yml Restore NuGet packages in Docker with committed lock files and locked mode #315 drift guard: the sed now perturbs the PackageVersion in the props file. Verified locally that the mutant fails with NU1004.
    • ci.yml setup-dotnet cache-dependency-path; ci.yml and e2e-smoke.yml image-layer hashFiles keys.
    • dependency-submission.yml and e2e-smoke.yml path filters.
    • .githooks/pre-commit trigger.

Docs: AGENTS.md (CI gates section), CONTRIBUTING.md dependencies, decision 146; the Microsoft.OpenApi and SSH.NET pin rationales are mirrored beside their PackageVersion entries.

Known and not this PR's: the weekly Dependabot nuget job may still show red from the pre-#685 CodeAnalysis.Common conflict.

Summary by CodeRabbit

  • New Features

    • Added centralized NuGet package version management, including pinned versions for selected dependencies.
    • Updated package lock files to support centralized dependency resolution.
  • CI & Developer Experience

    • CI, dependency scanning, caching, Docker restores, and pre-commit checks now recognize centralized package configuration changes.
    • Added contributor guidance for updating package versions and restoring dependencies.
  • Documentation

    • Documented centralized package management, floating versions, and lock-file behavior.

Move every NuGet version out of the nine .csproj files into a new
Directory.Packages.props at the repo root. The csproj files keep bare
PackageReference elements, including the PrivateAssets/IncludeAssets
children where they existed. Directory.Build.props is untouched; it still
does exactly one thing, RestorePackagesWithLockFile.

No version changes hands. 33 distinct packages, zero cross-project
disagreements, every floating range kept as it was. CPM rejects floating
ranges with NU1011 unless CentralPackageFloatingVersionsEnabled is set, so
it is set; the committed lock files pin what restores, not the ranges.

The lock files are not byte-identical and cannot be: CPM emits lock format
version 2 and reclassifies transitives that carry a PackageVersion as
CentralTransitive. A per-entry comparison of all 443 entries across the nine
locks shows identical resolved version, contentHash and dependencies for
every package, so the resolved graph is the same one main had.

Restore inputs that named the csproj or Directory.Build.props explicitly now
name the props file too: the Dockerfile restore layer, the #315 locked-mode
drift guard in ci.yml, the setup-dotnet cache key, the image-layer hashFiles
keys, the dependency-submission and e2e-smoke path filters, and the
pre-commit hook trigger.
@coderabbitai

coderabbitai Bot commented Sep 4, 2026 •

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: f2a386cc-c306-4e3a-989e-72e0a0580c76

📥 Commits

Reviewing files that changed from the base of the PR and between 62107f2 and 4658d3c.

📒 Files selected for processing (26)
  • .githooks/pre-commit
  • .github/workflows/ci.yml
  • .github/workflows/dependency-submission.yml
  • .github/workflows/e2e-smoke.yml
  • AGENTS.md
  • CONTRIBUTING.md
  • Directory.Packages.props
  • docs/decisions/146-ci-security-gates.md
  • src/Cluckwork.Api/Cluckwork.Api.csproj
  • src/Cluckwork.Api/Dockerfile
  • src/Cluckwork.Api/packages.lock.json
  • src/Cluckwork.AppHost/Cluckwork.AppHost.csproj
  • src/Cluckwork.AppHost/packages.lock.json
  • src/Cluckwork.Application/Cluckwork.Application.csproj
  • src/Cluckwork.Application/packages.lock.json
  • src/Cluckwork.Domain/packages.lock.json
  • src/Cluckwork.Infrastructure/Cluckwork.Infrastructure.csproj
  • src/Cluckwork.Infrastructure/packages.lock.json
  • tests/Cluckwork.Api.IntegrationTests/Cluckwork.Api.IntegrationTests.csproj
  • tests/Cluckwork.Api.IntegrationTests/packages.lock.json
  • tests/Cluckwork.AppHost.Tests/Cluckwork.AppHost.Tests.csproj
  • tests/Cluckwork.AppHost.Tests/packages.lock.json
  • tests/Cluckwork.Application.Tests/Cluckwork.Application.Tests.csproj
  • tests/Cluckwork.Application.Tests/packages.lock.json
  • tests/Cluckwork.Domain.Tests/Cluckwork.Domain.Tests.csproj
  • tests/Cluckwork.Domain.Tests/packages.lock.json

Included review availability: Your plan provides up to 8 included reviews per hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

The repository adopts Central Package Management through Directory.Packages.props. Project references remove inline versions, lock files move to format 2, and restore, caching, validation, hooks, and documentation now track the central package file.

Changes

Central NuGet package management

Layer / File(s) Summary
Central versions and project references
Directory.Packages.props, src/*/*.csproj, tests/*/*.csproj, src/Cluckwork.Api/Dockerfile
Package versions are centralized. Project references omit Version attributes. Docker restore includes Directory.Packages.props.
Central Package Management lock files
src/*/packages.lock.json, tests/*/packages.lock.json
Lock files use format version 2 and record centrally managed dependencies with requested ranges and resolved metadata.
Restore automation and repository guidance
.githooks/pre-commit, .github/workflows/*, AGENTS.md, CONTRIBUTING.md, docs/decisions/*
Hooks, workflows, documentation, and contributor guidance track central package-management changes.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Merge Risk: ⚪ Minimal · up to 4658d

The Central Package Management migration preserves the resolved dependency graph and updates restore and CI inputs consistently. No merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The PR satisfies the CPM migration, centralized version management, bare PackageReferences, version preservation, and regression-verification requirements in #684. It does not satisfy the issue's expl… Update issue #684 to explicitly allow CPM-required lock-file format and classification changes when resolved versions, hashes, dependencies, and package sets remain identical, or provide byte-identical lock files that satisfy the current ac…
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adopting Central Package Management so package version bumps use one file.
Description check ✅ Passed The description explains the change, rationale, verification steps, acceptance evidence, and linked issue. It provides equivalent content for the verification and checklist sections, although it does …
Out of Scope Changes check ✅ Passed The changes are related to the CPM migration. They update package references, lock files, restore inputs, CI safeguards, workflow triggers, Docker restore inputs, and related documentation. No unrelat…
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
Full details: Linked Issues check

Explanation

The PR satisfies the CPM migration, centralized version management, bare PackageReferences, version preservation, and regression-verification requirements in #684. It does not satisfy the issue's explicit requirement that every packages.lock.json remain byte-identical; all nine locks change from format 1 to format 2 and reclassify entries. The PR explains this change and proves graph equivalence, but the stated acceptance criterion remains unmet.

Resolution

Update issue #684 to explicitly allow CPM-required lock-file format and classification changes when resolved versions, hashes, dependencies, and package sets remain identical, or provide byte-identical lock files that satisfy the current acceptance criterion before merging.

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/central-package-management

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

chore: adopt Central Package Management so a version bump is one file, not eight

1 participant