Feature/cpm migration#95
Conversation
Plan to consolidate dependency management by migrating NtoLib.csproj from packages.config to PackageReference and adopting Central Package Management via Directory.Packages.props. Tests project then sees NtoLib's transitive graph, eliminating the binding-redirect drift that caused PR #94 to fail. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Consolidate dependency management so the Tests project sees NtoLib's transitive package graph, eliminating the binding-redirect drift that caused PR #94 to fail. - Add Directory.Packages.props with the centralised PackageVersion list enabling CPM (ManagePackageVersionsCentrally) for both projects. - Convert NtoLib.csproj from packages.config to PackageReference; remove packages.config and strip versions from PackageReference entries so CPM is active end-to-end. - Replace the standalone Merge.ps1 + nuget.exe ILRepack invocation with ILRepack.Lib.MSBuild.Task driven by NtoLib/ILRepack.targets, wired into the build via PackageReference. - Drop Tests/App.config binding-redirect band-aid (no longer needed once Tests resolves the same versions as NtoLib via CPM). - Simplify Build/Package.ps1 and Build/deploy.ps1: remove Merge.ps1 invocations now that ILRepack runs as part of the build. - Simplify CI restore step in .github/workflows/ci.yml (no more packages.config so plain dotnet restore suffices). - Fix dependabot.yml directory entry to track the new layout. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- AGENTS.md / CLAUDE.md: document the new dependency-management workflow (Directory.Packages.props authoring, no per-project versions, ILRepack via MSBuild task) so contributors understand the post-migration layout. - Build/readme.md: update build pipeline description to reflect the removal of Merge.ps1 and the move to ILRepack.Lib.MSBuild.Task. - .github/instructions/csharp.instructions.md: align Copilot guidance with CPM (no inline package versions) and the new ILRepack target. - Docs/known_issues/02-dll-merge-constraints.md: refresh with the current ILRepack invocation and remove the stale Merge.ps1 references. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds nuget.config at the repo root with <clear /> + nuget.org. Eliminates the NU1507 warnings raised by CPM whenever the developer machine has the VS-bundled "C:\Program Files (x86)\Microsoft SDKs\NuGetPackages" fallback feed configured globally. Now every contributor and CI runner restores from a single, explicit source. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR migrates the repo to Central Package Management (CPM) and modernizes the NuGet/build pipeline by converting NtoLib to SDK-style PackageReference while integrating ILRepack as an MSBuild task (gated via RunILRepack=true) to preserve test build behavior.
Changes:
- Adopt CPM with
Directory.Packages.props, removing per-project NuGet version pins and deletingNtoLib/packages.config. - Convert
NtoLibto SDK-style (net48) with explicit item includes (default globs disabled) and move dependency merging toILRepack.Lib.MSBuild.TaskviaNtoLib/ILRepack.targets. - Simplify CI restore (remove
nuget restore), addnuget.config, and update Dependabot scope to repo root.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
nuget.config |
Adds explicit NuGet feed configuration (single source). |
Directory.Packages.props |
Introduces CPM as the single source of NuGet package versions. |
Tests/Tests.csproj |
Removes Version= attributes from PackageReference to comply with CPM. |
NtoLib/NtoLib.csproj |
Migrates to SDK-style net48, disables default item globs, switches to PackageReference, wires ILRepack hook property. |
NtoLib/ILRepack.targets |
Adds gated MSBuild-driven ILRepack merge target (RunILRepack=true). |
NtoLib/app.config |
Updates a binding redirect entry (System.Resources.Extensions). |
NtoLib/packages.config |
Deletes legacy packages.config-based dependency list. |
Build/Package.ps1 |
Replaces Merge.ps1 invocation with dotnet build -p:RunILRepack=true step. |
Build/deploy.ps1 |
Replaces Merge.ps1 invocation with dotnet build -p:RunILRepack=true step. |
Build/tools/Merge.ps1 |
Removes legacy ILRepack PowerShell wrapper. |
Build/readme.md |
Updates build documentation to reflect MSBuild-based merge and new restore model. |
Docs/plans/completed/20260425-cpm-migration.md |
Adds detailed completed migration plan/record. |
Docs/known_issues/02-dll-merge-constraints.md |
Documents RunILRepack gate rationale with InternalsVisibleTo constraint. |
.github/workflows/ci.yml |
Simplifies restore to dotnet restore and adds CPM file to path filters. |
.github/dependabot.yml |
Updates NuGet ecosystem scan scope to repo root for CPM. |
.github/instructions/csharp.instructions.md |
Updates repo guidance for SDK-style + CPM + gated ILRepack behavior. |
CLAUDE.md |
Updates repo conventions and build instructions for SDK-style + CPM + gated ILRepack. |
AGENTS.md |
Mirrors updated conventions and build instructions for SDK-style + CPM + gated ILRepack. |
| <ILRepackInputAssemblies Include="$(OutputPath)NtoLib.dll" /> | ||
| <ILRepackInputAssemblies Include="$(OutputPath)*.dll" | ||
| Exclude="$(OutputPath)NtoLib.dll; | ||
| $(OutputPath)System.Resources.Extensions.dll; | ||
| $(OutputPath)System.Text.Json.dll; | ||
| $(OutputPath)System.Text.Encodings.Web.dll" /> |
There was a problem hiding this comment.
ILRepackInputAssemblies currently excludes only a few framework DLLs. The previous Build/tools/Merge.ps1 also excluded vendor/host assemblies (FB/InSAT*/MasterSCADA*/ICSharpCode*/OpcUaClient/Opc.Ua.Core/etc.). Relying solely on <Private>False</Private> means stale files in bin/ (from older builds or manual copies) could still be picked up by the *.dll glob and merged accidentally. To make the merge robust, consider explicitly excluding the vendor/host DLL name patterns here (or cleaning the output directory before running ILRepack).
| <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled> | ||
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <!-- NtoLib runtime dependencies (currently in NtoLib/packages.config) --> |
There was a problem hiding this comment.
This comment refers to NtoLib/packages.config, but that file is deleted as part of this migration. Suggest rewording to avoid pointing readers at a non-existent file (e.g., “NtoLib runtime dependencies”).
| <!-- NtoLib runtime dependencies (currently in NtoLib/packages.config) --> | |
| <!-- NtoLib runtime dependencies --> |
| dotnet build NtoLib.sln # un-merged DLL (used by tests) | ||
| dotnet build NtoLib.sln -p:RunILRepack=true # merged DLL (used for deployment) | ||
| Build/Package.ps1 # build + test + ILRepack merge + archive | ||
| Build/Deploy.ps1 # build + merge + copy to target machine |
There was a problem hiding this comment.
Docs reference Build/Deploy.ps1, but the repository file is Build/deploy.ps1 (lowercase). On case-sensitive setups this path won’t resolve; consider updating the docs to match the actual filename.
| Build/Deploy.ps1 # build + merge + copy to target machine | |
| Build/deploy.ps1 # build + merge + copy to target machine |
| dotnet build NtoLib.sln # un-merged DLL (used by tests) | ||
| dotnet build NtoLib.sln -p:RunILRepack=true # merged DLL (used for deployment) | ||
| Build/Package.ps1 # build + test + ILRepack merge + archive | ||
| Build/Deploy.ps1 # build + merge + copy to target machine |
There was a problem hiding this comment.
Docs reference Build/Deploy.ps1, but the repository file is Build/deploy.ps1 (lowercase). On case-sensitive setups this path won’t resolve; consider updating the docs to match the actual filename.
| Build/Deploy.ps1 # build + merge + copy to target machine | |
| Build/deploy.ps1 # build + merge + copy to target machine |
| - **Build:** `dotnet build` produces an un-merged `NtoLib.dll` for unit tests. Merging | ||
| is gated on `/p:RunILRepack=true` (because `[InternalsVisibleTo("Tests")]` would | ||
| otherwise cause CS0433 ambiguity in the test project) and runs only via | ||
| `Build/Package.ps1` / `Build/Deploy.ps1` after the test step. `netreg.exe` registers |
There was a problem hiding this comment.
This document references Build/Deploy.ps1, but the repository file is Build/deploy.ps1 (lowercase). Consider updating the path so it matches the actual script name.
| `Build/Package.ps1` / `Build/Deploy.ps1` after the test step. `netreg.exe` registers | |
| `Build/Package.ps1` / `Build/deploy.ps1` after the test step. `netreg.exe` registers |
| System.Text.Json + System.Text.Encodings.Web (kept external; bundled | ||
| separately by Archive.ps1 / Copy.ps1). |
There was a problem hiding this comment.
The comment says System.Text.Json / System.Text.Encodings.Web are “bundled separately by Archive.ps1 / Copy.ps1”, but those scripts currently only copy NtoLib.dll (+ optionally System.Resources.Extensions.dll). If these assemblies are expected to be present at runtime, consider updating the packaging/deploy pipeline to include them; otherwise, update this comment to reflect the real source (e.g., provided by the host environment).
| System.Text.Json + System.Text.Encodings.Web (kept external; bundled | |
| separately by Archive.ps1 / Copy.ps1). | |
| System.Text.Json + System.Text.Encodings.Web (kept external and expected | |
| to be supplied by the host/runtime environment or another deployment step). |
| ILRepack-merged DLL surface. Currently no entry below is transitive-only, | ||
| but several near-duplicates exist (Microsoft.Extensions.* family is split | ||
| across 9.0.0 and 9.0.8) and pinning keeps that explicit. |
There was a problem hiding this comment.
The comment says there are currently no transitive-only entries, but System.Runtime.Numerics is listed here without any corresponding <PackageReference> in the repo. Either add the missing PackageReference (if it’s required) or remove the PackageVersion / update the comment so it stays accurate.
| ILRepack-merged DLL surface. Currently no entry below is transitive-only, | |
| but several near-duplicates exist (Microsoft.Extensions.* family is split | |
| across 9.0.0 and 9.0.8) and pinning keeps that explicit. | |
| ILRepack-merged DLL surface. Some entries may be transitive-only, and | |
| several near-duplicates exist (Microsoft.Extensions.* family is split | |
| across 9.0.0 and 9.0.8) so pinning keeps that explicit. |
| paths: | ||
| - ".github/workflows/ci.yml" | ||
| - "NtoLib/**" | ||
| - "Tests/**" | ||
| - "Resources/**" | ||
| - "NtoLib.sln" | ||
| - "Directory.Packages.props" | ||
| - "global.json" | ||
| - ".editorconfig" | ||
| - "!**.md" | ||
| pull_request: | ||
| paths: | ||
| - ".github/workflows/ci.yml" | ||
| - "NtoLib/**" | ||
| - "Tests/**" | ||
| - "Resources/**" | ||
| - "NtoLib.sln" | ||
| - "Directory.Packages.props" | ||
| - "global.json" | ||
| - ".editorconfig" | ||
| - "!**.md" |
There was a problem hiding this comment.
The workflow paths: filter was updated to include Directory.Packages.props, but it still doesn’t include the new repo-root nuget.config. Since restore behavior now depends on nuget.config (and it can break dotnet restore), changes to that file should also trigger CI. Consider adding nuget.config to both push.paths and pull_request.paths.
|
@copilot review pr |
All 8 review findings from
|
Summary
NtoLib.csprojfrom old-style +packages.configto SDK-style +PackageReference(no wildcards:EnableDefault*Items=false, explicit<Compile Include>rule preserved).Directory.Packages.propsat the repo root — every NuGet version lives in one file and applies to NtoLib, Tests, Installer.Build/tools/Merge.ps1PowerShell wrapper withILRepack.Lib.MSBuild.Taskintegrated throughNtoLib/ILRepack.targets. Merge is gated on-p:RunILRepack=trueso plaindotnet build(and thetest build) stay un-merged — required because
[InternalsVisibleTo("Tests")]would otherwise produce CS0433 collisions.nuget.configwith a single explicit feed (eliminates NU1507 across all dev machines)./NtoLib→/) so it watchesDirectory.Packages.props. CIpaths:filter triggers on it too.Tests.dll.configgets correct binding redirects auto-generated. TheTests/App.configband-aid is nolonger needed.
Why
PR #94 (28-package dependabot bump) failed CI with 115 broken tests. The trigger was a
FileNotFoundExceptionforPeterO.Numbers, Version=1.8.0.0insideMathSstatic ctor —Tests.dll.configlacked abinding redirect because the format split (NtoLib on packages.config, Tests on PackageReference) isolated the two graphs. Adding redirects to
Tests/App.configwould have unblocked PR #94, but the underlyingdrift would recur on every future bump. CPM is the structural fix.
Test plan
dotnet build NtoLib.sln -c Release— 0 errorsdotnet test NtoLib.sln -c Release— 225 / 225 passedBuild/Package.ps1 -Configuration Release— produces 5.46 MB mergedNtoLib.dll; archive zip written. Verified viailspycmd -l classthat AngouriMath/Polly/Serilog/YamlDotNet/Microsoft.Extensions.*are internalised.
dotnet format NtoLib.sln --verify-no-changes— cleanFollow-ups (not in this PR)
packages.config+ deleted<Reference HintPath=..\packages\..>blocks) is obsolete. Dependabot will re-open a small PR proposing version bumps inDirectory.Packages.propsonly.Build/Deploy.ps1against a MasterSCADA target machine; confirmnetreg.exe NtoLib.dll /showerrorregisters cleanly. Catalogued in the migration plan's Post-Completion section.-p:RunILRepack=truenot set in workflow). Adding a Cecil-based smoke test on the merged artifact is a separate enhancement.