Skip to content

Obsolete the command-string overloads - #45

Merged
matt-edmondson merged 1 commit into
mainfrom
chore/41-obsolete-string-overloads
Aug 19, 2026
Merged

Obsolete the command-string overloads#45
matt-edmondson merged 1 commit into
mainfrom
chore/41-obsolete-string-overloads

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #41.

Stacked on #44 (feat/40-environment-variables), which is itself stacked on #43. Review those first; this PR's diff is against #44.

Problem

The overloads taking a single command string split on the first space to separate the executable from its arguments, so an executable path containing a space is split mid-path and the call fails with Win32Exception. On Windows that covers the default install location of most software:

await RunCommand.ExecuteAsync(@"C:\Program Files\Git\bin\git.exe --version");
// filename  = C:\Program
// arguments = Files\Git\bin\git.exe --version

Quoting does not rescue it, because the split happens before any quote handling — "C:\Program is what the filename becomes.

Approach

Option 3 from the issue. The string form is inherently ambiguous: no parse handles every combination of spaces and quotes without adopting a shell's full grammar, and a half-grammar (handling only a leading quoted segment) moves the surprise rather than removing it. So all eleven command-string overloads are marked [Obsolete], pointing at the argument-list overloads that already exist and avoid the whole class of problem.

Behaviour is unchanged — the overloads still work exactly as before, they just warn.

Breaking-change note

Deliberate, and worth calling out: downstream consumers building with TreatWarningsAsErrors will fail to build on upgrade until they migrate. That is the intended force of this option rather than an oversight, but it ships as [minor], so it is worth being comfortable with that before merging.

The README gains a migration table mapping each obsolete overload to its replacement, and every example now uses the argument-list form.

Suppression scope

Per the repo's rule on targeted suppressions:

  • The tests that exist to cover the command-string overloads keep calling them — those overloads still have to work until removal — under one #pragma warning disable CS0618 region that ends after the last such test rather than covering the file.
  • ExecuteAsyncShouldFailWhenArgumentWithSpacesIsPassedAsOneString pins the very mis-split that motivates this change, so it carries its own one-line suppression with that justification.
  • ExecuteAsyncShouldThrowWhenTokenIsAlreadyCancelled used a command string only incidentally, so it was migrated to the argument-list form rather than suppressed.

No suppression was needed in the library itself — Roslyn does not report obsolete usage from within an obsolete member, so the synchronous overloads delegating to the asynchronous ones stay clean.

Testing

Full suite: 32 passed, 2 skipped (elevated tests self-skip to avoid a UAC prompt). Builds clean across all eight target frameworks.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LwKTWcmxe5mh6DsLRj5NGQ

The overloads taking a single command string separate the executable
from its arguments by splitting on the first space, so an executable
path containing a space is split in the middle and the call fails. On
Windows that covers the default install location of most software.
Quoting does not help, because the split happens before any quote
handling, leaving a filename with a leading quote and a truncated path.

The string form is inherently ambiguous: no parse handles every
combination of spaces and quotes without adopting a shell's full
grammar, and a half-grammar moves the surprise rather than removing it.
Deprecate the eleven affected overloads in favour of the argument-vector
ones, which have no such ambiguity because the executable is passed
separately.

Tests that exercise the obsolete overloads keep calling them, since
those overloads still have to work until they are removed; the
suppression is scoped to exactly those tests rather than the file. The
cancellation test used a command string only incidentally, so it moves
to the argument-vector form instead.

Fixes #41
@matt-edmondson
matt-edmondson force-pushed the feat/40-environment-variables branch from f59f8a1 to ed58449 Compare August 19, 2026 10:43
@matt-edmondson
matt-edmondson force-pushed the chore/41-obsolete-string-overloads branch from 295a537 to 956659b Compare August 19, 2026 10:43
@sonarqubecloud

Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
C Maintainability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Base automatically changed from feat/40-environment-variables to main August 19, 2026 10:59
@matt-edmondson
matt-edmondson merged commit 96c9f7f into main Aug 19, 2026
4 of 5 checks passed
@matt-edmondson
matt-edmondson deleted the chore/41-obsolete-string-overloads branch August 19, 2026 10:59
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.

Execute(string command) splits on the first space, breaking executable paths that contain spaces

1 participant