Skip to content

[Upgrade] to .NET 10, System.CommandLine 2.0 GA, ClosedXML CVE fix#9

Merged
samatstariongroup merged 7 commits intoSTARIONGROUP:developmentfrom
fabiantax:feature/support-net10
Apr 23, 2026
Merged

[Upgrade] to .NET 10, System.CommandLine 2.0 GA, ClosedXML CVE fix#9
samatstariongroup merged 7 commits intoSTARIONGROUP:developmentfrom
fabiantax:feature/support-net10

Conversation

@fabiantax
Copy link
Copy Markdown
Contributor

@fabiantax fabiantax commented Apr 21, 2026

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following the VCD-Generator code style guidelines
  • I have provided test coverage for my change (where applicable)

Use case

We have a .NET 10 project that wants to use vcdg as a global tool. On a machine whose only installed SDK is .NET 10, dotnet tool install -g vcdg currently fails at runtime:

You must install or update .NET to run this application.
App: …/vcdg
Framework: 'Microsoft.NETCore.App', version '8.0.0'

This PR switches vcdg to net10 so it installs and runs on .NET 10 without having to side-load a net8 runtime.

Framework choice

Single LTS target: net10.0 (Nov 2025, supported until Nov 2028). Per review feedback, net8.0 is dropped entirely rather than multi-targeted — the project moves forward on net10 only.

Considered alternative: <RollForward>Major</RollForward>

Setting RollForward=Major on the net8 build would let the tool's apphost pick the net10 runtime at launch, satisfying the use case without changing the TFM. We chose against it because the tool would still ship only net8 assemblies (forgoing net10-aligned package fixes and keeping net8-line CVE liability), silent cross-major behavior changes (globalization, System.Text.Json defaults, Assembly.Location under single-file, TLS defaults) would surface only at runtime rather than at compile time, and the same config would also roll the tool forward into future unknown majors (net11, net12…) without a recompile. Moving the TFM verifies at build time that the code compiles and tests pass against the target runtime's reference assemblies — a stronger contract for a published NuGet tool with external consumers.

Changes

  1. Target net10.0 — both csprojs set <TargetFramework>net10.0</TargetFramework>.

  2. CI.github/workflows/CodeQuality.yml installs 10.0.x only, and refreshes actions/checkout, setup-java, setup-dotnet, upload-artifact to their current majors. The @v3 of upload-artifact in particular was retired by GitHub in early 2025 and no longer functions on current runners, so this refresh is required for the workflow to run at all.

  3. SecurityClosedXML 0.104.1 → 0.105.0 resolves two High severity advisories (GHSA-f32c-w444-8ppv, GHSA-qj66-m88j-hmgj) on the transitive System.IO.Packaging 8.0.0.

  4. System.CommandLine migration — 2.0.0-beta4 (Jun 2022) → 2.0.6 GA, and drops System.CommandLine.Hosting 0.4.0-alpha (7 years in alpha, no GA companion):

    • Program.cs builds the generic Host lazily inside SetAction (so --help / --version / parse errors don't pay for DI + Serilog setup) and resolves the handler from it to bind parsed option values.
    • GenerateCommand.cs exposes options as public properties and adds BindTo(Handler, ParseResult); all options use the 2.0 GA API (new Option<T>(name, aliases…) { Description, Required, DefaultValueFactory }).
    • Handler no longer implements ICommandHandler; the throwing Invoke(InvocationContext) is removed.
    • A custom SynchronousCommandLineAction preserves the ASCII-logo-on---help behavior.
    • Test fixture drops the now-obsolete Verify_that_GenerateCommand_Invoke_throws_exception (12 tests, down from 13).
  5. Package versions aligned to net10 — runtime-aligned packages move to their net10 majors:

    Package Version
    Microsoft.Extensions.Configuration.Json 10.0.6
    Microsoft.Extensions.Hosting 10.0.6
    Microsoft.Extensions.Logging.Abstractions 10.0.6
    Microsoft.Extensions.Logging.Console (tests) 10.0.6
    Serilog.Extensions.Hosting 10.0.0
    Serilog.Settings.Configuration 10.0.0
  6. Runtime-agnostic bumpSerilog 4.0.2 → 4.3.1 (required by Serilog.Extensions.Hosting 10.0.0).

  7. Copyright — header year bumped to 2026 in Program.cs and Commands/GenerateCommand.cs.

Verification

Builds clean (0 warnings / 0 errors) and tests pass:

Passed! - Failed: 0, Passed: 12, Skipped: 0, Total: 12 — VCD-Generator.Tests.dll (net10.0)

dotnet list package --vulnerable --include-transitive reports no vulnerable packages.

Out of scope (pre-existing, flagged by review)

Kept for a separate cleanup PR to keep this one focused on the net10 migration:

  • Thread.Sleep(1500) blocking calls inside the async spinner lambda (GenerateCommand.cs, in the Spectre Status.Start callback) — theatrical UX delays.
  • Repeated IEnumerable<T>.Count() on reader results (potential re-enumeration of LINQ chains).
  • Sequential disk reads of requirements file and test results (could be Task.WhenAll, currently serialized for spinner UX).
  • FileInfo.Exists / DirectoryInfo.Exists TOCTOU pre-checks in Handler.InvokeAsync.
  • Typo "nwe instance" in the Handler constructor XML doc.
  • Four-level nesting inside Handler.InvokeAsync (method → try → spinner lambda → inner try/catch).

Known CI caveat (pre-existing)

The workflow uses SONAR_TOKEN from repo secrets. GitHub does not pass secrets to pull_request workflows triggered from a fork, so the SonarCloud steps will fail on the CI run against this PR with a missing-token error. This is a pre-existing fork-PR limitation unrelated to the changes in this PR. The build/test steps run fine regardless; only the Sonar begin/end steps will be affected.

Commits

  1. [Update] multi-target net8.0 and net10.0 — initial csproj TFM change (later collapsed to net10-only in commit 7).
  2. [Update] CI to install .NET 8 and .NET 10 SDKs and refresh action versions — workflow changes (matrix trimmed to 10.0.x in commit 7).
  3. [Bump] ClosedXML to 0.105.0 to resolve high-severity advisories — security fix.
  4. [Migrate] System.CommandLine from beta to 2.0 GA, remove dead .Hosting alpha — parser migration + code rewrites.
  5. [Update] runtime-aligned packages with TFM-conditional ItemGroups — per-TFM package splitting (conditions removed in commit 7).
  6. [Simplify] defer Host build and drop redundant defaults — lazy Host build, drop redundant false factory, replace reflection-based source-directory default with AppContext.BaseDirectory.
  7. [Drop] net8.0 support, collapse to net10-only per PR review — drops net8 target, removes TFM-conditional ItemGroups, trims CI matrix to 10.0.x, bumps copyright year.

Keeps .NET 8 (LTS, supported through Nov 2026) as the default target
while adding .NET 10 (LTS, supported through Nov 2028) so that the
global tool `vcdg` can be installed on machines with the .NET 10 SDK
without requiring a fallback .NET 8 runtime to be present. .NET 9 is
intentionally skipped as STS. No dependency changes required:
Microsoft.Extensions.* 8.0.x packages are forward-compatible with
net10 via the standard TFM compatibility rules.
…sions

Instructs actions/setup-dotnet to install both 8.0.x and 10.0.x so the
multi-targeted solution can be built and tested on both TFMs. Bumps
actions/checkout, actions/setup-java, actions/setup-dotnet, and
actions/upload-artifact to the current supported majors
(checkout@v4, setup-java@v4, setup-dotnet@v4, upload-artifact@v4).
actions/upload-artifact@v3 in particular was retired by GitHub in
early 2025 and no longer functions on current runners, so this refresh
is required for the workflow to run at all.
@fabiantax fabiantax marked this pull request as draft April 21, 2026 08:20
Fixes two high-severity CVEs on the transitive System.IO.Packaging 8.0.0
pulled in by ClosedXML 0.104.1:

- GHSA-f32c-w444-8ppv
- GHSA-qj66-m88j-hmgj

dotnet list package --vulnerable --include-transitive reports no
vulnerable packages after the bump on either TFM.
…g alpha

System.CommandLine.Hosting has been alpha for ~7 years and did not ship
a GA companion alongside System.CommandLine 2.0. Drop it and migrate the
parser to 2.0.6 GA.

- Program.cs now builds the generic Host directly and uses
  rootCommand.SetAction(...) to resolve the handler from DI and bind
  parsed option values.
- GenerateCommand.cs exposes options as public properties and adds
  BindTo(Handler, ParseResult). Options use the 2.0 GA API
  (new Option<T>(name, aliases...) { Description, Required,
  DefaultValueFactory }).
- Handler no longer implements ICommandHandler; the throwing
  Invoke(InvocationContext) is removed and InvokeAsync() takes no
  parameters.
- A custom SynchronousCommandLineAction preserves the ASCII-logo-on-
  --help behaviour.
- Test fixture drops the now-obsolete
  Verify_that_GenerateCommand_Invoke_throws_exception and all
  InvocationContext plumbing (12 tests per TFM, down from 13).

Microsoft.Extensions.Hosting becomes a direct reference (was transitive
via the removed .Hosting package). Microsoft.Extensions.Configuration.Json
8.0.0 -> 8.0.1 and Microsoft.Extensions.Logging.Abstractions 8.0.1 ->
8.0.2 patch-bumps resolve the version downgrade pulled in by
Microsoft.Extensions.Hosting 8.0.1. Same patch bump for
Microsoft.Extensions.Logging.Console in the test project.
Split Microsoft.Extensions.*, Serilog.Extensions.Hosting and
Serilog.Settings.Configuration into two conditional ItemGroups so each
build resolves its own runtime-aligned major:

  Package                                      | net8.0 | net10.0
  ---------------------------------------------|--------|--------
  Microsoft.Extensions.Configuration.Json      | 8.0.1  | 10.0.6
  Microsoft.Extensions.Hosting                 | 8.0.1  | 10.0.6
  Microsoft.Extensions.Logging.Abstractions    | 8.0.2  | 10.0.6
  Microsoft.Extensions.Logging.Console (tests) | 8.0.1  | 10.0.6
  Serilog.Extensions.Hosting                   | 8.0.0  | 10.0.0
  Serilog.Settings.Configuration               | 8.0.2  | 10.0.0

Serilog bumped 4.0.2 -> 4.3.1 as a runtime-agnostic package because
Serilog.Extensions.Hosting 10.0.0 requires Serilog >= 4.3.0.
@fabiantax fabiantax changed the title [Update] Multi-target net8.0 and net10.0, refresh CI action versions [Add] .NET 10 support — multi-target, TFM-aligned packages, System.CommandLine 2.0 GA Apr 21, 2026
- Program.cs: build the generic Host inside the SetAction callback
  instead of up-front so --help, --version and parse-error paths no
  longer pay for Serilog + DI container setup.
- GenerateCommand.cs: drop DefaultValueFactory on --no-logo (bool
  already defaults to false).
- GenerateCommand.cs: replace reflection-based source-directory default
  (Assembly.GetExecutingAssembly().Location) with AppContext.BaseDirectory.
  The reflection form returns an empty string under single-file publish,
  which vcdg is a plausible target for as a global tool.
@fabiantax fabiantax marked this pull request as ready for review April 21, 2026 12:44
Comment thread .github/workflows/CodeQuality.yml Outdated
Comment thread VCD-Generator.Tests/VCD-Generator.Tests.csproj Outdated
Comment thread VCD-Generator.Tests/VCD-Generator.Tests.csproj Outdated
Comment thread VCD-Generator/Commands/GenerateCommand.cs Outdated
Comment thread VCD-Generator/Program.cs Outdated
Comment thread VCD-Generator/VCD-Generator.csproj Outdated
Removes net8.0 from target frameworks, drops the now-redundant
TFM-conditional ItemGroups, and trims the CI dotnet matrix to 10.0.x.
Also bumps copyright header year in Program.cs and GenerateCommand.cs.
@fabiantax fabiantax changed the title [Add] .NET 10 support — multi-target, TFM-aligned packages, System.CommandLine 2.0 GA [Upgrade] to .NET 10, System.CommandLine 2.0 GA, ClosedXML CVE fix Apr 23, 2026
@fabiantax
Copy link
Copy Markdown
Contributor Author

@samatstariongroup thanks for the review — pushed c6bf42f addressing all six comments:

  • Dropped net8.0 from both csprojs; TargetFrameworks collapsed to a single <TargetFramework>net10.0</TargetFramework>.
  • Removed the TFM-conditional ItemGroups (no longer needed with a single target).
  • CI matrix trimmed to 10.0.x.
  • Copyright year bumped to 2026 in Program.cs and Commands/GenerateCommand.cs.

Local verification: dotnet build clean (0 warnings / 0 errors) and all 12 tests pass on net10.0. PR description updated to reflect the net10-only framing.

@samatstariongroup samatstariongroup merged commit f5a04e2 into STARIONGROUP:development Apr 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants