refactor: centralize build config and pin the SDK (#156) (#182)

Adds global.json (10.0 SDK, rollForward latestFeature) so local dev
resolves the same SDK line CI pins in five workflows, instead of
floating to whatever is installed.

Adds Directory.Build.props for LangVersion/Nullable, the two
properties identical across all three csproj (TargetFramework stays
per-project since the test project multi-targets net10.0;net48).

Adds Directory.Packages.props with central package management, moving
every PackageReference version out of the three csproj into one file.
Newtonsoft.Json bumped 13.0.3 -> 13.0.4 in the single place instead of
two independent declarations that could drift. xunit.runner.visualstudio
stays at 4.0.0 (it runs xUnit v1/v2/v3 per its own description; nothing
in the issue's stated xunit 2.9.3 pairing required a downgrade).

Removes the self-referential $(NoWarn) from the Core csproj, a no-op.

Adds an explicit System.Memory 4.6.3 PackageReference to the net48
leg of the test project, which resolves the MSB3277 conflict between
System.Memory 4.0.1.2 and 4.0.5.0 (110 warnings -> 0). Scoped to net48
only per the issue, which names only the net48 test target.

Adds PackageVersionCentralizationTests, an xUnit test asserting none
of the three csproj declare a PackageReference Version attribute
outside Directory.Packages.props.

Co-authored-by: goodolclint-claude[bot] <323206664+goodolclint-claude[bot]@users.noreply.github.com>
This commit is contained in:
goodolclint-claude[bot]
2026-09-02 20:44:12 +00:00
committed by GitHub
parent 716ecd100d
commit 4f9ee05edf
7 changed files with 96 additions and 16 deletions
+8
View File
@@ -0,0 +1,8 @@
<Project>
<PropertyGroup>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
+19
View File
@@ -0,0 +1,19 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="PowerShellStandard.Library" Version="5.1.1" />
<PackageVersion Include="SharpCompress" Version="0.50.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="4.0.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="coverlet.collector" Version="10.0.1" />
<PackageVersion Include="System.Memory" Version="4.6.3" />
</ItemGroup>
</Project>
+6
View File
@@ -0,0 +1,6 @@
{
"sdk": {
"version": "10.0.100",
"rollForward": "latestFeature"
}
}
+2 -5
View File
@@ -2,12 +2,9 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>PSProxmoxVE.Core</RootNamespace>
<AssemblyName>PSProxmoxVE.Core</AssemblyName>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn)</NoWarn>
</PropertyGroup>
<ItemGroup>
@@ -17,8 +14,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="SharpCompress" Version="0.50.4" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="SharpCompress" />
</ItemGroup>
</Project>
+2 -4
View File
@@ -2,8 +2,6 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<RootNamespace>PSProxmoxVE</RootNamespace>
<AssemblyName>PSProxmoxVE</AssemblyName>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
@@ -11,8 +9,8 @@
<ItemGroup>
<ProjectReference Include="..\PSProxmoxVE.Core\PSProxmoxVE.Core.csproj" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="PowerShellStandard.Library" Version="5.1.1" PrivateAssets="all" />
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="PowerShellStandard.Library" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
@@ -2,21 +2,19 @@
<PropertyGroup>
<TargetFrameworks>net10.0;net48</TargetFrameworks>
<LangVersion>10.0</LangVersion>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.9.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="4.0.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="coverlet.collector" Version="10.0.1">
<PackageReference Include="Moq" />
<PackageReference Include="coverlet.collector">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
@@ -28,6 +26,7 @@
<ItemGroup Condition="'$(TargetFramework)' == 'net48'">
<Reference Include="System.Net.Http" />
<PackageReference Include="System.Memory" />
</ItemGroup>
<ItemGroup>
@@ -0,0 +1,53 @@
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Xunit;
namespace PSProxmoxVE.Core.Tests
{
public class PackageVersionCentralizationTests
{
private static readonly string[] ProjectPaths =
{
"src/PSProxmoxVE.Core/PSProxmoxVE.Core.csproj",
"src/PSProxmoxVE/PSProxmoxVE.csproj",
"tests/PSProxmoxVE.Core.Tests/PSProxmoxVE.Core.Tests.csproj",
};
[Fact]
public void PackageReferences_DoNotPinVersionsPerProject()
{
var repoRoot = FindRepoRoot();
foreach (var relativePath in ProjectPaths)
{
var doc = XDocument.Load(Path.Combine(repoRoot, relativePath));
var pinned = doc.Descendants("PackageReference")
.Where(e => e.Attribute("Version") != null)
.Select(e => e.Attribute("Include")?.Value)
.ToList();
Assert.True(pinned.Count == 0,
$"{relativePath} pins a version directly instead of going through Directory.Packages.props: {string.Join(", ", pinned)}");
}
}
private static string FindRepoRoot()
{
var dir = new DirectoryInfo(AppContext.BaseDirectory);
while (dir != null && !File.Exists(Path.Combine(dir.FullName, "PSProxmoxVE.sln")))
{
dir = dir.Parent;
}
if (dir == null)
{
throw new InvalidOperationException(
"Could not locate repository root (PSProxmoxVE.sln) from " + AppContext.BaseDirectory);
}
return dir.FullName;
}
}
}