SmrtHub Central Package Version Policy v1.0¶
Authoritative governance for NuGet dependency version management across all SmrtHub .NET projects.
1. Purpose¶
Establish a single, deterministic, auditable mechanism for specifying and evolving third‑party and first‑party NuGet package versions. Prevents version drift, reduces restore unpredictability, and enables safe, batched upgrades.
2. Scope¶
Applies to every C#/.NET project (.csproj) in the repository (applications, libraries, tools, test projects) that consumes NuGet packages. Excludes Python dependencies, native toolchain assets, or ad‑hoc local project references.
3. Core Mechanism¶
SmrtHub uses .NET Central Package Management (CPM):
- A single root file: Directory.Packages.props (repository root)
- Exact pins via <PackageVersion Include="Package" Version="X.Y.Z" />
- Project files list <PackageReference Include="Package" /> without Version attributes
- The property <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> lives in the central file
- Any attempt to add a Version inside a project triggers NU1008 (enforcement signal)
4. Design Principles¶
| Principle | Explanation |
|---|---|
| Determinism First | Exact versions (no floating ranges) for reproducible builds & diagnostics. |
| Stable Before Preview | Prefer latest stable unless a preview/rc is required (platform constraints). |
| Minimal Noise | No redundant imports; rely on MSBuild automatic discovery at repo root. |
| Explicit Exceptions | Pre-release packages documented inline until replaced. |
| Single Source of Truth | Only one authoritative location for versions. |
| Commented Intent | Lower-bound intent (future range considerations) captured as comments, never enforced via ranges today. |
5. File Location & Discovery¶
/Directory.Packages.props must reside at the repository root so MSBuild auto‑loads it without manual <Import> statements. Manual re‑imports cause MSB4011 warnings and are prohibited.
6. Allowed Version Forms¶
| Form | Allowed | Rationale |
|---|---|---|
| Exact numeric (e.g. 13.0.4) | Yes | Deterministic pin |
| Pre-release semantic (e.g. 10.0.0-rc.1.25451.107) | Yes (document) | Platform / dependency wave alignment |
Version ranges [a,b) |
No (for now) | Increases complexity / non-determinism |
Floating * |
Never | Breaks reproducibility |
Relative / auto (e.g. 8.*) |
Never | Hidden drift |
7. Update Workflow¶
| Step | Action |
|---|---|
| 1 | Identify candidate package & target version (prefer stable) |
| 2 | Edit Directory.Packages.props – change the Version attribute |
| 3 | Run dotnet restore (or build script) locally |
| 4 | Address any new analyzer / API warnings introduced |
| 5 | Run test suite (unit + integration where applicable) |
| 6 | Commit with message: deps: bump <PackageName> to <Version> |
| 7 | Include changelog / security notes if major or security-related |
8. Adding a New Dependency¶
- Add a new
<PackageVersion Include="Package" Version="X.Y.Z" />in sorted alphabetical order (case-insensitive) within the central file. - Add
<PackageReference Include="Package" />to the consuming project(s). - Restore & build.
- If transitive conflicts arise, resolve by explicitly adding other required
PackageVersionpins.
9. Removing a Dependency¶
- Remove all
<PackageReference Include="Package" />entries. - Delete its
<PackageVersion>line from the central file. - Restore & confirm no other project reintroduces it transitively (use
dotnet list <project>.csproj package --include-transitive).
10. Pre-release Governance¶
| Scenario | Guideline |
|---|---|
| Required platform (e.g., WinAppSDK wave) | Pin pre-release with full qualifier |
| Experimental adoption | Justify in PR description + plan to revisit stable replacement |
| Aging pre-release (> 2 stable cycles) | Review and migrate or drop |
11. Enforcement Signals¶
| Error / Warning | Meaning | Action |
|---|---|---|
| NU1008 | Inline Version present while CPM enabled |
Remove Version attribute |
| MSB4011 | Duplicate import of central file | Remove manual <Import> |
| NU1604 | No inclusive lower bound (historical issue) | Caused by CPM not applied – ensure root file in place |
| Vulnerability warnings (NU19xx) | Known CVE in pinned version | Upgrade or document temporary hold reason |
12. Anti-Patterns (Prohibited)¶
| Anti-Pattern | Why Disallowed |
|---|---|
Multiple Directory.Packages.props |
Fragmentation / ambiguity |
| Project-specific downgrades | Silent divergence, potential runtime mismatch |
| Floating or wildcard versions | Non-repeatable builds |
| Manual import once discovery works | Redundant noise (MSB4011) |
| Ranges for "future proofing" | Encourages unnoticed drift |
13. Comment Conventions¶
Lower-bound intent is recorded as:
These comments are informational only; tooling may later parse them if range mode is adopted.14. SmrtHub Runtime Identifier (RID) Guard Interaction¶
The SmrtHub project enforces explicit -r usage through the build system. This is orthogonal to CPM but influences build invocation. Building SmrtHub requires an explicit RID (win-x64 or win-arm64). The build system handles this automatically, but for manual builds:
# Using build script (recommended)
.\Tools\Clean-Build\BuildApps.ps1 -Configuration Debug -RuntimeIdentifier win-x64
# Or direct dotnet command
dotnet build <your-app>.csproj -r win-x64 -c Debug
15. Bulk Upgrade Playbook¶
- Export current dependency inventory:
- Update one or a logical cluster (e.g., Serilog family) at a time.
- Restore & run tests.
- Compare new tree:
- Diff for unexpected additions / version regressions.
- Commit with a cohesive message.
16. Risk Mitigation & Rollback¶
| Risk | Mitigation | Rollback |
|---|---|---|
| Breaking API | Use feature branches & tests | Revert the specific <PackageVersion> line |
| Transitive vulnerability | Upgrade direct dep first | Pin explicit transitive version if required |
| Hidden drift (manual version added) | NU1008 stops build | Remove offending attribute |
17. Tooling Enhancements (Future Consideration)¶
- Pre-commit hook to block inline
Version="additions - Script to validate alphabetical ordering & duplicate detection
- Report generator diffing previous vs current dependency graph
18. Change Management¶
| Change Type | Action | Version Bump of This Policy |
|---|---|---|
| Add package | Update central file | Patch if doc adjusted |
| Replace pre-release with stable | Update entry | Patch |
| Introduce ranges | Major (would alter determinism stance) | Major |
| Add enforcement tooling | Document | Minor |
19. Example Directory.Packages.props (Extract)¶
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<!-- Logging -->
<PackageVersion Include="Serilog" Version="4.3.0" /> <!-- lower-bound: [4.3.0,) -->
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" /> <!-- lower-bound: [6.0.0,) -->
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" /> <!-- lower-bound: [7.0.0,) -->
<!-- Windows UI -->
<PackageVersion Include="Microsoft.WindowsAppSDK" Version="1.4.231115000" />
<PackageVersion Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.2428" />
<!-- Testing -->
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageVersion Include="xunit" Version="2.6.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" />
</ItemGroup>
</Project>
20. Verification Checklist¶
- No
Version="in any<PackageReference> -
Directory.Packages.propsloads once (no MSB4011) - New/changed versions justified in PR description
- Tests pass locally
- Vulnerability warnings (NU19xx) acknowledged or resolved
Maintainers: Build / Runtime Engineering Initial Release: v1.0