From 7c6fff84b681108ada3053a36143a921d0568658 Mon Sep 17 00:00:00 2001 From: lduchosal Date: Sun, 17 May 2026 16:29:12 +0200 Subject: [PATCH 1/4] feat: re-enable netstandard2.0 target on IPNetwork library ns2.0 lets the library be consumed by .NET Framework 4.6.1+ and other ns2.0-compatible runtimes. Source compat changes: - Polyfills.cs: internal NotNullWhenAttribute shim under #if NETSTANDARD2_0 - UniqueLocalAddress: SHA256.HashData is .NET 5+; widen the ComputeHash fallback from #if NETSTANDARD2_1 to #if NETSTANDARD - IPNetwork2WideSubnet: replace nnin[^1] with nnin[Length-1] (System.Index is ns2.1+) - CidrNetworkAware: Contains(char) is .NET Core 2.1+; use IndexOf and add null-forgive since string.IsNullOrWhiteSpace lacks [NotNullWhen] annotation on ns2.0 Co-Authored-By: Claude Opus 4.7 (1M context) --- src/System.Net.IPNetwork/CidrNetworkAware.cs | 4 ++-- src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs | 2 +- src/System.Net.IPNetwork/Polyfills.cs | 15 +++++++++++++++ .../System.Net.IPNetwork.csproj | 2 +- src/System.Net.IPNetwork/UniqueLocalAddress.cs | 4 ++-- src/System.Net.IPNetwork/packages.lock.json | 16 ++++++++++++++++ 6 files changed, 37 insertions(+), 6 deletions(-) create mode 100644 src/System.Net.IPNetwork/Polyfills.cs diff --git a/src/System.Net.IPNetwork/CidrNetworkAware.cs b/src/System.Net.IPNetwork/CidrNetworkAware.cs index 4b727119..37210445 100644 --- a/src/System.Net.IPNetwork/CidrNetworkAware.cs +++ b/src/System.Net.IPNetwork/CidrNetworkAware.cs @@ -69,13 +69,13 @@ public bool TryGuessCidr(string? ip, out byte cidr) // Reject if user passed a slash - this API expects a plain address. // (You can relax this if you want to honor an explicitly supplied prefix.) - if (ip.Contains('/')) + if (ip!.IndexOf('/') >= 0) return false; if (!IPAddress.TryParse(ip.Trim(), out var ipAddress)) return false; - switch (ipAddress.AddressFamily) + switch (ipAddress!.AddressFamily) { case AddressFamily.InterNetwork: cidr = GuessIpv4(ipAddress); diff --git a/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs b/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs index 5dbbb619..b99c809b 100644 --- a/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs +++ b/src/System.Net.IPNetwork/IPNetwork2WideSubnet.cs @@ -141,7 +141,7 @@ internal static bool InternalWideSubnet(bool tryWide, IPNetwork2[] ipnetworks, [ IPNetwork2 nnin0 = nnin[0]; BigInteger uintNnin0 = nnin0.ipaddress; - IPNetwork2 nninX = nnin[^1]; + IPNetwork2 nninX = nnin[nnin.Length - 1]; IPAddress ipaddressX = nninX.Last; AddressFamily family = ipnetworks[0].family; diff --git a/src/System.Net.IPNetwork/Polyfills.cs b/src/System.Net.IPNetwork/Polyfills.cs new file mode 100644 index 00000000..47e5eb3b --- /dev/null +++ b/src/System.Net.IPNetwork/Polyfills.cs @@ -0,0 +1,15 @@ +// +// Copyright (c) IPNetwork. All rights reserved. +// + +#if NETSTANDARD2_0 +namespace System.Diagnostics.CodeAnalysis; + +[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] +internal sealed class NotNullWhenAttribute : Attribute +{ + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + public bool ReturnValue { get; } +} +#endif diff --git a/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj b/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj index a4a22d3c..d8a5c767 100644 --- a/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj +++ b/src/System.Net.IPNetwork/System.Net.IPNetwork.csproj @@ -1,7 +1,7 @@  - net10.0;net9.0;net8.0;netstandard2.1 + net10.0;net9.0;net8.0;netstandard2.1;netstandard2.0 true IPNetwork2 4.0.0 diff --git a/src/System.Net.IPNetwork/UniqueLocalAddress.cs b/src/System.Net.IPNetwork/UniqueLocalAddress.cs index 293fecbc..daf8178b 100644 --- a/src/System.Net.IPNetwork/UniqueLocalAddress.cs +++ b/src/System.Net.IPNetwork/UniqueLocalAddress.cs @@ -179,7 +179,7 @@ private static byte[] GenerateGlobalIdFromMac(byte[] macAddress) byte[] timestamp = BitConverter.GetBytes(DateTimeOffset.UtcNow.ToUnixTimeSeconds()); Array.Copy(timestamp, 0, input, macAddress.Length, timestamp.Length); -#if NETSTANDARD2_1 +#if NETSTANDARD using var sha2 = SHA256.Create(); byte[] hash = sha2.ComputeHash(input); #else @@ -198,7 +198,7 @@ private static byte[] GenerateGlobalIdFromMac(byte[] macAddress) private static byte[] GenerateGlobalIdFromSeed(string seed) { byte[] seedBytes = Encoding.UTF8.GetBytes(seed); -#if NETSTANDARD2_1 +#if NETSTANDARD using var sha2 = SHA256.Create(); byte[] hash = sha2.ComputeHash(seedBytes); #else diff --git a/src/System.Net.IPNetwork/packages.lock.json b/src/System.Net.IPNetwork/packages.lock.json index ab2b1d3b..4989b6ef 100644 --- a/src/System.Net.IPNetwork/packages.lock.json +++ b/src/System.Net.IPNetwork/packages.lock.json @@ -1,6 +1,22 @@ { "version": 1, "dependencies": { + ".NETStandard,Version=v2.0": { + "NETStandard.Library": { + "type": "Direct", + "requested": "[2.0.3, )", + "resolved": "2.0.3", + "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A==" + } + }, ".NETStandard,Version=v2.1": {}, "net10.0": {}, "net8.0": {}, From 5d5c309716026620023c18e5ab7acf3298e7e918 Mon Sep 17 00:00:00 2001 From: lduchosal Date: Sun, 17 May 2026 16:29:22 +0200 Subject: [PATCH 2/4] ci: add Windows net48 job to gate netstandard2.0 build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Multi-target TestProject to net10.0;net48 so the net48 build links against the library's ns2.0 output. This is the only CI path that actually loads the ns2.0 DLL — without it, the new ns2.0 target ships untested. - TestProject.csproj: net10.0;net48; ConsoleApplication ref is net10.0-only (the CLI only targets net10.0); ArchitectureTest, ConsoleUnitTest, ConsoleJsonOutputTest excluded from net48 (they test the CLI, not the library) - IPAddressExtensionTests: SHA256.HashData fallback for non-.NET 5+ - dotnet.yml: new windows-net48 job runs TestProject.exe under net48 Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/dotnet.yml | 17 ++ src/TestProject/IPAddressExtensionTests.cs | 7 + src/TestProject/TestProject.csproj | 9 +- src/TestProject/packages.lock.json | 210 +++++++++++++++++++++ 4 files changed, 241 insertions(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 938ac77b..9e223dd8 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -39,3 +39,20 @@ jobs: with: file: coverage.lcov format: lcov + + windows-net48: + name: Test net48 (Windows) + runs-on: windows-latest + + steps: + - uses: actions/checkout@v4 + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 10.0.x + - name: Restore + run: dotnet restore ./src --verbosity m + - name: Build TestProject (net48) + run: dotnet build ./src/TestProject/TestProject.csproj -c Debug -f net48 --no-restore + - name: Run net48 tests against netstandard2.0 build + run: ./src/TestProject/bin/Debug/net48/TestProject.exe diff --git a/src/TestProject/IPAddressExtensionTests.cs b/src/TestProject/IPAddressExtensionTests.cs index 4970af29..5e45732d 100644 --- a/src/TestProject/IPAddressExtensionTests.cs +++ b/src/TestProject/IPAddressExtensionTests.cs @@ -98,7 +98,14 @@ e XOR'd with the byte count so the two sequences will be different. */ foreach (int _ in Enumerable.Range(1, 1000)) { /* Hash the current interation to get a new block of deterministic bytes. */ +#if NET5_0_OR_GREATER hashInput = SHA256.HashData(hashInput); +#else + using (var sha = SHA256.Create()) + { + hashInput = sha.ComputeHash(hashInput); + } +#endif /* Convert the first n bytes for an address. 4 will have an IPv4. 16 will make an IPv6. */ yield return new IPAddress(hashInput.Take(byteCount).ToArray()).ToString(); diff --git a/src/TestProject/TestProject.csproj b/src/TestProject/TestProject.csproj index 9c270556..613a105b 100644 --- a/src/TestProject/TestProject.csproj +++ b/src/TestProject/TestProject.csproj @@ -1,7 +1,7 @@ - net10.0 + net10.0;net48 false True true @@ -49,9 +49,14 @@ - + + + + + + diff --git a/src/TestProject/packages.lock.json b/src/TestProject/packages.lock.json index 5a5f402b..19f703b7 100644 --- a/src/TestProject/packages.lock.json +++ b/src/TestProject/packages.lock.json @@ -1,6 +1,216 @@ { "version": 1, "dependencies": { + ".NETFramework,Version=v4.8": { + "coverlet.collector": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "WFejCcOUR6k8UYyDnnR6Gk+obFYMsWrZuNqPJnsVFGVhpPSN0y20D4qbdKJnXinYGx9PQ397Hf9TnU1NBST8vA==" + }, + "coverlet.msbuild": { + "type": "Direct", + "requested": "[10.0.0, )", + "resolved": "10.0.0", + "contentHash": "pmUxktztlMnsIvgHjzxG3fHRAXpqmZSA+W8dIsbVMfwI01SnBndrBIOE3Stg6987+o7HjBa62C4q5um3Ia6d9g==" + }, + "Microsoft.CodeCoverage": { + "type": "Direct", + "requested": "[18.5.1, )", + "resolved": "18.5.1", + "contentHash": "vMFDR1ZjqzzgKmM0zrPie7Gv9Y+ZppjODB5Quzu9Eq0TlIusUfUCYFPEawO91zQuqwzvdFbJSU7WHNtjStffJQ==" + }, + "Microsoft.NET.Test.Sdk": { + "type": "Direct", + "requested": "[18.5.1, )", + "resolved": "18.5.1", + "contentHash": "SfqVaLiIqAbRWuPg5BP4QFwBIirQj/YIL8Dhxl6zntBKbXp0cQykoV480SmwG+yRMiWptxEI6NbHQuGSZ8b97w==", + "dependencies": { + "Microsoft.CodeCoverage": "18.5.1" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies": { + "type": "Direct", + "requested": "[1.0.3, )", + "resolved": "1.0.3", + "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==", + "dependencies": { + "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3" + } + }, + "MSTest.TestAdapter": { + "type": "Direct", + "requested": "[4.2.3, )", + "resolved": "4.2.3", + "contentHash": "oVV/luk0bBghnVvLaw8MwFlD7It0Cx9P2nKobeqIafmTQqFFWY69Wo801dxjeNaLzO/o9WQ84WSK84gXJuubhg==", + "dependencies": { + "MSTest.TestFramework": "4.2.3", + "Microsoft.Testing.Extensions.VSTestBridge": "2.2.3", + "Microsoft.Testing.Platform.MSBuild": "2.2.3", + "System.Threading.Tasks.Extensions": "4.5.4" + } + }, + "MSTest.TestFramework": { + "type": "Direct", + "requested": "[4.2.3, )", + "resolved": "4.2.3", + "contentHash": "9zzij59YLh+tf+FRLNqhzHjmdspR91bol+jdQxLlxxTGMAML6LDbvuyXKMGdcrE84+QpKkk6KjTVBC5PBGrDeA==", + "dependencies": { + "MSTest.Analyzers": "4.2.3" + } + }, + "NetArchTest.eNhancedEdition": { + "type": "Direct", + "requested": "[1.4.5, )", + "resolved": "1.4.5", + "contentHash": "dfWIjcLS2MBGz403OoT+vxr/4Dz/f7MnQhEwmgaf7PXlWzG39LSiUPhW7OPx9Ebg/GNWvDAttlpq5FY0mG/qkg==", + "dependencies": { + "Mono.Cecil": "0.11.6" + } + }, + "Newtonsoft.Json": { + "type": "Direct", + "requested": "[13.0.4, )", + "resolved": "13.0.4", + "contentHash": "pdgNNMai3zv51W5aq268sujXUyx7SNdE2bj1wZcWjAQrKMFZV260lbqYop1d2GM67JI1huLRwxo9ZqnfF/lC6A==" + }, + "Microsoft.ApplicationInsights": { + "type": "Transitive", + "resolved": "2.23.0", + "contentHash": "nWArUZTdU7iqZLycLKWe0TDms48KKGE6pONH2terYNa8REXiqixrMOkf1sk5DHGMaUTqONU2YkS4SAXBhLStgw==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.NETFramework.ReferenceAssemblies.net48": { + "type": "Transitive", + "resolved": "1.0.3", + "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ==" + }, + "Microsoft.Testing.Extensions.Telemetry": { + "type": "Transitive", + "resolved": "2.2.3", + "contentHash": "mLdW+JOR3kXYGTdgR/qc/UZBA0r+eCR2k6bUxTcuDj5w9WdIQ7Lol5MBUU7YOSGd9bs9bvhSYWAptgz0YtQqCA==", + "dependencies": { + "Microsoft.ApplicationInsights": "2.23.0", + "Microsoft.Testing.Platform": "2.2.3", + "System.Diagnostics.DiagnosticSource": "6.0.0" + } + }, + "Microsoft.Testing.Extensions.TrxReport.Abstractions": { + "type": "Transitive", + "resolved": "2.2.3", + "contentHash": "hntvxJEkmUAx6C2xXc/PO38DqEQl4rimzOgSvTR1hAMruMid7R4RcXOrzzF33J66gKaN7jRaQ0TMW/nNfaV9jw==", + "dependencies": { + "Microsoft.Testing.Platform": "2.2.3" + } + }, + "Microsoft.Testing.Extensions.VSTestBridge": { + "type": "Transitive", + "resolved": "2.2.3", + "contentHash": "7WlJISO8QKUK+d+WhgnANwy4ACwUrvICnviY/mthPwjZ2gVeDaSUAeBnMy2cxfzZgm8VATtGUDbYzUxsgV2CyQ==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "18.3.0", + "Microsoft.Testing.Extensions.Telemetry": "2.2.3", + "Microsoft.Testing.Extensions.TrxReport.Abstractions": "2.2.3", + "Microsoft.Testing.Platform": "2.2.3" + } + }, + "Microsoft.Testing.Platform": { + "type": "Transitive", + "resolved": "2.2.3", + "contentHash": "LhM1/Qoi8Ams5QcD4r3f09CSOono9iQr3NEJQItFtyzWB55nWTgEOsVqXqMWWWIwk3nkPqc+XfnlJmp8xUI5fg==" + }, + "Microsoft.Testing.Platform.MSBuild": { + "type": "Transitive", + "resolved": "2.2.3", + "contentHash": "Q22jJYJLx4srTinsAuoCskqmzjrBJC8YeGJMHHIcrf1dQeHoEZ7wsqDzTlENkMoke2qfufF7U+9u58nlZunH/Q==", + "dependencies": { + "Microsoft.Testing.Platform": "2.2.3" + } + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "18.3.0", + "contentHash": "AEIEX2aWdPO9XbtR96eBaJxmXRD9vaI9uQ1T/JbPEKlTAZwYx0ZrMzKyULMdh/HH9Sg03kXCoN7LszQ90o6nPQ==", + "dependencies": { + "System.Reflection.Metadata": "8.0.0" + } + }, + "Mono.Cecil": { + "type": "Transitive", + "resolved": "0.11.6", + "contentHash": "f33RkDtZO8VlGXCtmQIviOtxgnUdym9xx/b1p9h91CRGOsJFxCFOFK1FDbVt1OCf1aWwYejUFa2MOQyFWTFjbA==" + }, + "MSTest.Analyzers": { + "type": "Transitive", + "resolved": "4.2.3", + "contentHash": "dxOZt8/LWuiox7rugInJoIa5Mmu3pBmXdfaoZOx/mxx8+sUFFpjBXPlWXQXGeWzpkVPNC3x1Jf7rt2h2Zjyvvg==" + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "AurL6Y5BA1WotzlEvVaIDpqzpIPvYnnldxru8oXJU2yFxFUy3+pNXjXd1ymO+RA0rq0+590Q8gaz2l3Sr7fmqg==", + "dependencies": { + "System.Memory": "4.5.5", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "frQDfv0rl209cKm1lnwTgFPzNigy2EKk1BS3uAvHvlBVKe5cymGyHO+Sj+NLv5VF/AhHsqPIUUwya5oV4CHMUw==", + "dependencies": { + "System.Memory": "4.5.4", + "System.Runtime.CompilerServices.Unsafe": "6.0.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.5", + "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==", + "dependencies": { + "System.Buffers": "4.5.1", + "System.Numerics.Vectors": "4.5.0", + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "8.0.0", + "contentHash": "ptvgrFh7PvWI8bcVqG5rsA/weWM09EnthFHR5SCnS6IN+P4mj6rE1lBDC4U8HL9/57htKAqy4KQ3bBj84cfYyQ==", + "dependencies": { + "System.Collections.Immutable": "8.0.0", + "System.Memory": "4.5.5" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "6.0.0", + "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.3" + } + }, + "IPNetwork2": { + "type": "Project" + } + }, "net10.0": { "coverlet.collector": { "type": "Direct", From dc563030a5e2df9a3b7e86ac773b8cbbd200c1ba Mon Sep 17 00:00:00 2001 From: lduchosal Date: Sun, 17 May 2026 16:34:00 +0200 Subject: [PATCH 3/4] ci: use dotnet test for net48 job EnableMSTestRunner=true doesn't emit a TestProject.exe on net48, so the previous invocation path didn't exist. dotnet test handles both VSTest hosting on .NET Framework and the platform bridge. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 9e223dd8..701f1619 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,4 +55,4 @@ jobs: - name: Build TestProject (net48) run: dotnet build ./src/TestProject/TestProject.csproj -c Debug -f net48 --no-restore - name: Run net48 tests against netstandard2.0 build - run: ./src/TestProject/bin/Debug/net48/TestProject.exe + run: dotnet test ./src/TestProject/TestProject.csproj -c Debug -f net48 --no-build --logger "console;verbosity=normal" From f4e56e2b25faf62957a82e41df858ebbfbad01ec Mon Sep 17 00:00:00 2001 From: lduchosal Date: Sun, 17 May 2026 16:38:01 +0200 Subject: [PATCH 4/4] ci: emit TestProject.exe for net48 and run directly dotnet test no longer bridges to VSTest under Microsoft.Testing.Platform on .NET 10 SDK. Easiest path: explicit OutputType=Exe so net48 produces TestProject.exe (net10.0 still produces TestProject.dll, unaffected). Workflow runs the exe via the pwsh call operator. Co-Authored-By: Claude Opus 4.7 (1M context) --- .github/workflows/dotnet.yml | 4 +++- src/TestProject/TestProject.csproj | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 701f1619..80bfbce1 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,4 +55,6 @@ jobs: - name: Build TestProject (net48) run: dotnet build ./src/TestProject/TestProject.csproj -c Debug -f net48 --no-restore - name: Run net48 tests against netstandard2.0 build - run: dotnet test ./src/TestProject/TestProject.csproj -c Debug -f net48 --no-build --logger "console;verbosity=normal" + run: | + & .\src\TestProject\bin\Debug\net48\TestProject.exe + if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } diff --git a/src/TestProject/TestProject.csproj b/src/TestProject/TestProject.csproj index 613a105b..948c3fdd 100644 --- a/src/TestProject/TestProject.csproj +++ b/src/TestProject/TestProject.csproj @@ -16,6 +16,7 @@ 5 True true + Exe