Skip to content

chore: bump System.CommandLine from 2.0.0+beta.5 to 2.0.0#313

Open
Seddryck wants to merge 1 commit intomainfrom
chore/bump-System.CommandLine-from-2.0.0+beta.5-to-2.0.0
Open

chore: bump System.CommandLine from 2.0.0+beta.5 to 2.0.0#313
Seddryck wants to merge 1 commit intomainfrom
chore/bump-System.CommandLine-from-2.0.0+beta.5-to-2.0.0

Conversation

@Seddryck
Copy link
Copy Markdown
Owner

@Seddryck Seddryck commented Nov 13, 2025

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced command-line error messages and argument parsing validation for clearer user feedback.
  • Chores

    • Upgraded System.CommandLine to stable 2.0.0 release for improved reliability.
    • Updated test infrastructure and dependencies.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Nov 13, 2025

Walkthrough

Updates System.CommandLine package to stable release 2.0.0, refactors CLI command invocation to use a parse-before-invoke approach, modernizes option definition syntax across RenderOptions and RenderCommand, and updates test infrastructure to align with the new parsing and invocation patterns.

Changes

Cohort / File(s) Summary
Project Dependencies
src/Didot.Cli/Didot.Cli.csproj, testing/Didot.Core.Testing/Didot.Core.Testing.csproj
Updated System.CommandLine from beta 2.0.0-beta4 to stable 2.0.0; bumped Microsoft.NET.Test.Sdk from 18.0.0 to 18.0.1.
CLI Core Implementation
src/Didot.Cli/Program.cs
Changed command invocation to parse arguments before invoking root command, altering the execution flow for argument binding and error handling.
Command Handler
src/Didot.Cli/RenderCommand.cs
Replaced explicit dictionary defaults with DefaultValueFactory, switched to AddError for validation, migrated from SetHandler to SetAction, and updated option value retrieval to use GetRequiredValue/GetValue.
Option Definitions
src/Didot.Cli/RenderOptions.cs
Modernized option initialization syntax to tuple-based names, added explicit Description and Required flags, replaced parseArgument with CustomParser for value parsing, and updated error handling to use AddError.
CLI Tests
testing/Didot.Cli.Testing/ProgramTests.cs, testing/Didot.Cli.Testing/RenderOptions/*.cs
Refactored test scaffolding to use direct RenderCommand.Parse(args) instead of intermediate Parser/InvocationContext, updated assertions to use result.GetValue() and result.Errors, and adjusted expected error messages from short-form to long-form flags.

Sequence Diagram

sequenceDiagram
    autonumber
    participant Caller
    participant Program as Program.Main
    participant RenderCommand
    participant ParseResult

    rect rgb(200, 220, 255)
    note right of Program: New Flow: Parse-First Approach
    Caller->>Program: Invoke with args
    Program->>RenderCommand: Parse(args)
    RenderCommand->>ParseResult: Create result
    ParseResult-->>Program: Return parsed result
    Program->>RenderCommand: Invoke(result)
    RenderCommand-->>Program: Return exit code
    Program-->>Caller: int exit code
    end

    rect rgb(220, 200, 255)
    note right of Program: Old Flow: Direct Invocation
    Caller->>Program: Invoke with args
    Program->>RenderCommand: InvokeAsync(args)
    RenderCommand-->>Program: int exit code
    Program-->>Caller: int exit code
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Focus areas requiring attention:
    • RenderCommand.cs and RenderOptions.cs logic changes for option parsing, validation, and invocation patterns—ensure CustomParser replacements preserve original behavior
    • Program.cs parse-then-invoke sequence to verify error handling consistency
    • Test migration pattern consistency across 8 test files; verify all ParseResult accessors properly map to new direct Parse API
    • Error message assertions updated to expect long-form flags instead of short-form

Suggested labels

dependency-update

Poem

🐰 The CLI hops with grace so new,
System.CommandLine stable shines through,
Parse before invoke—a cleaner way,
Tests aligned with modern play,
Refactored code, the future's bright! ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: upgrading System.CommandLine from a beta prerelease to the stable 2.0.0 release, which aligns with the primary objective and the package update visible in the csproj file.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch chore/bump-System.CommandLine-from-2.0.0+beta.5-to-2.0.0

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sonarqubecloud
Copy link
Copy Markdown

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
testing/Didot.Cli.Testing/RenderOptions/StdinTests.cs (1)

10-10: Remove unused and suspicious import.

The import using static System.Runtime.InteropServices.JavaScript.JSType; appears unrelated to CLI testing and is not used anywhere in this file. This looks like it may have been accidentally added.

Apply this diff to remove the unused import:

-using static System.Runtime.InteropServices.JavaScript.JSType;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7154a91 and fc7e90a.

📒 Files selected for processing (14)
  • src/Didot.Cli/Didot.Cli.csproj (2 hunks)
  • src/Didot.Cli/Program.cs (1 hunks)
  • src/Didot.Cli/RenderCommand.cs (1 hunks)
  • src/Didot.Cli/RenderOptions.cs (3 hunks)
  • testing/Didot.Cli.Testing/ProgramTests.cs (1 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/EngineExtensionTests.cs (3 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/OutputTests.cs (1 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/ParserExtensionTests.cs (3 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/ParserParamsTests.cs (3 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/ParserTests.cs (1 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/SourceTests.cs (5 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/StdinTests.cs (1 hunks)
  • testing/Didot.Cli.Testing/RenderOptions/TemplateTests.cs (1 hunks)
  • testing/Didot.Core.Testing/Didot.Core.Testing.csproj (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (10)
testing/Didot.Cli.Testing/RenderOptions/ParserTests.cs (3)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
testing/Didot.Cli.Testing/RenderOptions/EngineExtensionTests.cs (2)
  • Test (13-22)
  • Test (24-41)
src/Didot.Cli/RenderOptions.cs (1)
  • RenderOptions (12-130)
testing/Didot.Cli.Testing/RenderOptions/OutputTests.cs (3)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
testing/Didot.Cli.Testing/RenderOptions/EngineExtensionTests.cs (2)
  • Test (13-22)
  • Test (24-41)
src/Didot.Cli/RenderOptions.cs (1)
  • RenderOptions (12-130)
testing/Didot.Cli.Testing/RenderOptions/StdinTests.cs (2)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
src/Didot.Cli/RenderOptions.cs (1)
  • RenderOptions (12-130)
testing/Didot.Cli.Testing/RenderOptions/EngineExtensionTests.cs (1)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
testing/Didot.Cli.Testing/RenderOptions/ParserParamsTests.cs (1)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
src/Didot.Cli/RenderOptions.cs (2)
src/Didot.Cli/RenderCommandHandler.cs (1)
  • Dictionary (93-114)
src/Didot.Core/StringExtensions.cs (1)
  • NormalizeExtension (11-17)
testing/Didot.Cli.Testing/RenderOptions/ParserExtensionTests.cs (1)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
testing/Didot.Cli.Testing/RenderOptions/SourceTests.cs (1)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
src/Didot.Cli/RenderCommand.cs (1)
src/Didot.Cli/RenderCommandHandler.cs (3)
  • RenderCommandHandler (13-143)
  • RenderCommandHandler (17-18)
  • Execute (20-76)
testing/Didot.Cli.Testing/RenderOptions/TemplateTests.cs (3)
src/Didot.Cli/RenderCommand.cs (2)
  • RenderCommand (13-83)
  • RenderCommand (15-82)
testing/Didot.Cli.Testing/RenderOptions/EngineExtensionTests.cs (2)
  • Test (13-22)
  • Test (24-41)
src/Didot.Cli/RenderOptions.cs (1)
  • RenderOptions (12-130)
🔇 Additional comments (24)
testing/Didot.Core.Testing/Didot.Core.Testing.csproj (1)

3-3: Verify the scope of this bump—PR title references System.CommandLine, but this file updates Microsoft.NET.Test.Sdk.

This file shows a bump to Microsoft.NET.Test.Sdk (18.0.0 → 18.0.1), which is unrelated to the PR's stated objective of upgrading System.CommandLine to 2.0.0.

While the patch-level bump itself is safe and recommended, this change introduces scope beyond the PR title. Clarify whether this is:

  • A collateral update required by the System.CommandLine upgrade (e.g., new test infrastructure patterns)
  • An intentional secondary update to address test SDK issues
  • Scope creep that should be split into a separate PR

If this bump is necessary for the System.CommandLine upgrade, consider updating the PR description to document this dependency.

testing/Didot.Cli.Testing/ProgramTests.cs (1)

189-189: LGTM! Error message updated to reflect long-form option name.

The updated assertion now expects the long-form option name (--template) in the error message instead of the short-form (-t), which is more descriptive for users and aligns with the System.CommandLine 2.0.0 stable release behavior.

testing/Didot.Cli.Testing/RenderOptions/OutputTests.cs (2)

22-25: LGTM! Tests successfully migrated to System.CommandLine 2.0.0 API.

The test now uses the direct RenderCommand.Parse(args) pattern and accesses values via result.GetValue and result.Errors, which aligns with the new System.CommandLine 2.0.0 stable API.


34-37: LGTM! Consistent migration pattern applied.

The test correctly uses the new parsing API and verifies the absence of the output option when not provided.

testing/Didot.Cli.Testing/RenderOptions/ParserParamsTests.cs (1)

19-21: LGTM! Parser parameter tests migrated successfully.

All test methods consistently use the new RenderCommand.Parse(args) API pattern with proper result inspection via result.GetValue and result.Errors.

Also applies to: 33-36, 53-56

testing/Didot.Cli.Testing/RenderOptions/EngineExtensionTests.cs (1)

19-21: LGTM! Engine extension tests properly migrated.

The tests now use the direct parsing pattern and properly verify engine extension configurations using the new API.

Also applies to: 33-36, 52-55

src/Didot.Cli/Program.cs (1)

48-48: LGTM! Execution flow updated to parse-then-invoke pattern.

The change from InvokeAsync(args) to Parse(args).InvokeAsync() adopts System.CommandLine 2.0.0's explicit parsing step, which provides better control over the command execution lifecycle and aligns with the API's design patterns.

testing/Didot.Cli.Testing/RenderOptions/SourceTests.cs (2)

3-3: LGTM! Added necessary using directive.

The System.CommandLine namespace is now imported to support the direct RenderCommand usage in the tests.


20-22: LGTM! Source option tests successfully migrated.

All test methods have been updated to use the RenderCommand.Parse(args) pattern with proper error and value inspection. The migration maintains test coverage while adopting the new API.

Also applies to: 36-39, 59-62, 79-83

testing/Didot.Cli.Testing/RenderOptions/TemplateTests.cs (2)

19-21: LGTM! Template validation test migrated successfully.

The test now uses the direct parsing pattern and properly validates the template value.


30-33: LGTM! Error message updated to long-form option name.

The test now expects the long-form --template in error messages, which is more descriptive and consistent with System.CommandLine 2.0.0 behavior.

src/Didot.Cli/Didot.Cli.csproj (1)

29-29: No issues found. The package upgrade is verified as safe.

System.CommandLine version 2.0.0 is confirmed as available on NuGet, and no security vulnerabilities exist for this package version. The upgrade from the beta release to 2.0.0 stable is appropriate and safe to proceed with.

testing/Didot.Cli.Testing/RenderOptions/ParserTests.cs (2)

22-26: LGTM: Test migration to System.CommandLine 2.0.0 API.

The test correctly uses the new RenderCommand.Parse(args) API and accesses values through result.GetValue(options.Parser) instead of the deprecated approach. The pattern is consistent with other test files in the PR.


34-37: LGTM: Consistent test pattern for optional option.

The test correctly validates that an unprovided optional parser option returns null, using the same API pattern as the required option test above.

testing/Didot.Cli.Testing/RenderOptions/StdinTests.cs (2)

23-27: LGTM: StdIn option tests correctly migrated.

The tests properly use the new RenderCommand.Parse(args) API and validate both the presence and absence of the stdin option through result.GetValue(options.StdIn).

Also applies to: 35-39, 48-52


60-64: LGTM: Error validation tests correctly updated.

The error validation tests now correctly access result.Errors and validate error messages using the new API. The error messages match those defined in RenderCommand.cs validators.

Also applies to: 85-89, 110-113

src/Didot.Cli/RenderCommand.cs (3)

18-21: LGTM: DefaultValueFactory correctly initialized.

The use of DefaultValueFactory = _ => [] for dictionary/collection options aligns with System.CommandLine 2.0.0 patterns, ensuring empty collections are returned when options are not provided.


23-31: LGTM: Options registered with new API.

The migration from AddOption(...) to Options.Add(...) correctly uses the System.CommandLine 2.0.0 stable API.


73-81: LGTM: SetAction correctly uses new invocation pattern.

The migration to SetAction with GetRequiredValue for required options and GetValue with null-coalescing for optional options correctly implements the System.CommandLine 2.0.0 invocation pattern.

testing/Didot.Cli.Testing/RenderOptions/ParserExtensionTests.cs (1)

19-22: LGTM: Parser extension tests correctly migrated.

All test cases (empty, single, and multiple parser extensions) correctly use the new RenderCommand.Parse(args) API and validate results through result.GetValue(options.ParserExtensions) and result.Errors.

Also applies to: 33-41, 55-65

src/Didot.Cli/RenderOptions.cs (4)

14-19: LGTM: Template option correctly defined.

The option definition uses the new System.CommandLine 2.0.0 constructor syntax with explicit name, aliases, description, and required/arity properties.


21-26: LGTM: CustomParser correctly extracts and trims token values.

The Engine and Parser options use CustomParser to extract the first token value and trim it, returning an empty string if no tokens are present. This correctly handles the optional string parsing in System.CommandLine 2.0.0.

Also applies to: 56-63


28-36: LGTM: Dictionary options use CustomParser with key-value parsing.

All dictionary-based options (EngineExtensions, Sources, ParserExtensions, ParserParams) correctly use CustomParser to delegate to the ParseKeyValuePairs helper method with appropriate separators and normalization functions.

Also applies to: 38-46, 65-73, 75-83


93-126: LGTM: ParseKeyValuePairs correctly uses new error reporting API.

The helper method correctly uses result.AddError(...) (lines 109, 120) instead of the deprecated error mechanism, aligning with System.CommandLine 2.0.0 patterns.

Comment on lines 37 to +42
try
{
sourcesProvided = result.GetValueForOption(options.Sources) is not null
&& result.GetValueForOption(options.Sources)!.Any();
sourcesProvided = (result.GetValue(options.Sources)?.Any() ?? false);
}
catch { }
catch
{ }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

Review empty catch blocks in validators.

The validators use empty catch blocks around GetValue(options.Sources). While this appears defensive, silently swallowing exceptions could hide unexpected issues during parsing.

Consider whether GetValue can actually throw for the Sources option in System.CommandLine 2.0.0, or if there's a safer pattern (e.g., checking if the option was provided before calling GetValue). If exceptions are expected here, consider logging them or at least adding a comment explaining why they're caught and ignored.

Also applies to: 62-67


🏁 Script executed:

#!/bin/bash
# Check if there are other patterns in the codebase for handling optional GetValue calls
rg -n -A2 -B2 'GetValue.*Sources' --type cs

Length of output: 2043


I'll verify whether GetValue actually throws exceptions in System.CommandLine 2.0.0 and check for other patterns in the codebase.

Let me search for more context about how the Sources option is defined in the RenderCommand:

Now let me examine the RenderCommand code to understand the context better:

Based on my verification, the review comment is accurate. The empty catch blocks around GetValue() in RenderCommand.cs are problematic for several reasons:

  1. GetValue can throw exceptions for parsing errors, when no symbol is defined, or when the parsed result cannot be cast to the expected type.

  2. Empty catch blocks defeat the purpose of exceptions—when an exception occurs, nothing happens and the program fails for an unknown reason, leaving the application in an unknown state.

  3. All official System.CommandLine documentation examples show GetValue() called without try-catch blocks, suggesting this defensive pattern is unnecessary and non-idiomatic.

  4. Empty catch blocks hide errors and make debugging difficult, and exceptions should always be logged or handled appropriately.


Remove empty catch blocks and handle exceptions appropriately.

The try-catch blocks at lines 37-42 and 62-67 silently swallow exceptions without any logging or handling. Either:

  • Verify that GetValue() won't throw given the context (e.g., by checking result.Errors beforehand) and remove the try-catch, or
  • If exceptions are genuinely expected, catch them with specific exception types, log the issue, and add a comment explaining why this is needed.

Using bare catch { } masks errors and violates C# best practices.

🤖 Prompt for AI Agents
In src/Didot.Cli/RenderCommand.cs around lines 37-42 (and similarly 62-67),
remove the empty catch blocks that silently swallow exceptions from
result.GetValue(); instead either (a) eliminate the try/catch and rely on
pre-checking result.Errors and the presence of the option/symbol before calling
GetValue(), or (b) if an exception can legitimately occur, catch specific
exception types only, log the error with context and add a comment explaining
why the exception is expected; do not use bare catch { } blocks.

@codecov-commenter
Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.89041% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.93%. Comparing base (7154a91) to head (fc7e90a).

Files with missing lines Patch % Lines
src/Didot.Cli/RenderOptions.cs 90.00% 1 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #313      +/-   ##
==========================================
+ Coverage   87.74%   87.93%   +0.18%     
==========================================
  Files          47       47              
  Lines        1069     1069              
  Branches      186      187       +1     
==========================================
+ Hits          938      940       +2     
  Misses         72       72              
+ Partials       59       57       -2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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