Paketversionen zentral verwalten mit Directory.Packages.props
- Matt
In einer Projektmappe mit fünfzehn Projekten steht dieselbe Paketreferenz an fünfzehn Stellen, und nach ein paar Monaten in drei verschiedenen Versionen. Meistens fällt das erst auf, wenn zur Laufzeit eine Methode fehlt, die es in der einen Version gibt und in der anderen nicht.
Dagegen gibt es Central Package Management. Die Versionen wandern in eine einzige Datei, die Projektdateien nennen nur noch die Paketnamen.
Einrichten
Neben die Solution-Datei kommt eine Directory.Packages.props:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="FluentValidation" Version="11.11.0" />
</ItemGroup>
</Project>
In den .csproj-Dateien entfällt das Version-Attribut ersatzlos:
<ItemGroup>
<PackageReference Include="Serilog" />
<PackageReference Include="FluentValidation" />
</ItemGroup>
Bleibt irgendwo eine Version stehen, bricht der Build mit NU1008 ab. Das ist unbequem, aber genau der Sinn der Sache: Es gibt nur noch eine Stelle, an der Versionen stehen.
Ausnahmen
Manchmal braucht genau ein Projekt eine andere Version, etwa weil ein Altsystem an einer alten API hängt. Dafür gibt es VersionOverride:
<PackageReference Include="Serilog" VersionOverride="3.1.1" />
Das ist absichtlich sperrig formuliert. Man sieht beim Lesen der Projektdatei sofort, dass hier jemand ausgeschert ist, und kann nach dem Warum fragen.
Transitive Abhängigkeiten festnageln
Der Teil, den ich anfangs übersehen hatte:
<PropertyGroup>
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
Damit gelten die zentralen Versionen auch für Pakete, die man gar nicht selbst referenziert, sondern die über andere Pakete hereinkommen. Praktisch, wenn eine Sicherheitsmeldung zu einer Bibliothek erscheint, die nur indirekt im Projekt hängt: Eine Zeile in einer Datei, statt in jedem Projekt ein Pseudo-Verweis, um die Version hochzuziehen.
Was man sich einhandelt
Zwei Dinge stören im Alltag.
Erstens funktioniert “Paket in diesem Projekt aktualisieren” im NuGet-Dialog von Visual Studio nicht mehr wie gewohnt, weil es dort nichts mehr zu aktualisieren gibt. Man landet in der Props-Datei, was richtig ist, aber die Gewohnheit ändert sich.
Zweitens wird die zentrale Datei mit der Zeit lang und enthält irgendwann auch Pakete, die kein Projekt mehr benutzt. Aufräumen muss man selbst, es gibt keine Warnung für unbenutzte PackageVersion-Einträge.
Bei zwei Projekten lohnt sich das nicht. Ab drei würde ich es nicht mehr anders machen, und dabei zählen Testprojekte mit. Die vergisst man beim Zählen gern, obwohl gerade dort die Versionen auseinanderlaufen.