Skip to content

Use valid CLI version probes - #4027

Merged
thomhurst merged 6 commits into
mainfrom
issue-3996-cli-version-probes-20260823
Aug 24, 2026
Merged

Use valid CLI version probes#4027
thomhurst merged 6 commits into
mainfrom
issue-3996-cli-version-probes-20260823

Conversation

@thomhurst

@thomhurst thomhurst commented Aug 23, 2026

Copy link
Copy Markdown
Owner

Part of #3996.

  • make availability checks honor each scraper's VersionArguments
  • use supported version subcommands for ArgoCD, eksctl, Cosign, Go, Kubectl, and Kustomize
  • cover all probes with focused regression tests

Validation:

  • OptionsGenerator solution Release build: 0 warnings, 0 errors
  • OptionsGenerator tests: 831/831 passed

Summary by CodeRabbit

  • Improvements

    • CLI availability checks now use each tool’s appropriate version command.
    • Added support for tool-specific probes, including client version checks for Kubernetes-related tools.
    • Improved version detection for Liquibase output containing lengthy banners or varied formats.
    • Availability checks retain a help-command fallback when version commands are unsupported.
  • Tests

    • Expanded coverage for CLI probing, version parsing, fallback behavior, and command execution.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a798ed7-994c-497b-b962-3c0c11b216e4

📥 Commits

Reviewing files that changed from the base of the PR and between 13fd094 and 18ef588.

📒 Files selected for processing (18)
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/Cli/LiquibaseCliScraperTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/CliVersionProbeTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/HelmCliScraperTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/KubectlCliScraperTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/TypeDetection/ProcessCliCommandExecutorTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/TypeDetection/ResilientCliCommandExecutorTests.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/ArgoCdCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/CliScraperBase.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/CosignCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/EksctlCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/GoCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/HelmCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/KubectlCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/KustomizeCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/LiquibaseCliScraper.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/TypeDetection/ICliCommandExecutor.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/TypeDetection/ProcessCliCommandExecutor.cs
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/TypeDetection/ResilientCliCommandExecutor.cs
💤 Files with no reviewable changes (1)
  • tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/HelmCliScraper.cs

Included review availability: Your plan provides up to 4 included reviews per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

CLI scrapers now pass tool-specific version arguments to availability probes. Executor implementations support argument-aware checks and fallback behavior. Liquibase parsing now extracts versions from multiple banner formats.

Changes

CLI version detection

Layer / File(s) Summary
Argument-aware executor probes
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/TypeDetection/..., tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/TypeDetection/...
ICliCommandExecutor and its implementations support availability checks with explicit arguments. Process execution retains version-to-help fallback behavior. Tests cover version probes and resilient delegation.
Scraper version command wiring
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/..., tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/...
The base scraper passes configured version arguments. Argo CD and kubectl use version --client. Eksctl, Cosign, Kustomize, Go, and Helm use version. Tests record the selected probes.
Liquibase banner parsing
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/LiquibaseCliScraper.cs, tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/Cli/LiquibaseCliScraperTests.cs
Liquibase parses multiple banner formats and returns the matched version. Parameterized tests validate extraction of 5.0.3 from banner output.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Merge Risk: ⚪ Minimal · up to 18ef5

The PR updates CLI availability checks to use supported version probes and adds focused regression coverage; no actionable merge-blocking risk remains after normal checks and review.

Sequence Diagram(s)

sequenceDiagram
  participant CliScraperBase
  participant ICliCommandExecutor
  participant CLI
  CliScraperBase->>ICliCommandExecutor: IsAvailableAsync(command, VersionArguments)
  ICliCommandExecutor->>CLI: Execute version arguments
  CLI-->>ICliCommandExecutor: Return exit status and output
  ICliCommandExecutor-->>CliScraperBase: Return availability result
Loading

Poem

I’m a rabbit checking versions bright,
version --client hops into sight.
Banners fade, patterns read,
Executors test each command thread.
One clean probe, then off I go!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: replacing invalid CLI version probes with valid ones.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-3996-cli-version-probes-20260823

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.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026

Copy link
Copy Markdown

Greptile Summary

This PR updates CLI availability checks to use each scraper’s supported version arguments and adds focused regression coverage.

  • Adds argument-aware availability probes through the executor abstraction and resilient decorator.
  • Configures supported version commands for ArgoCD, eksctl, Cosign, Go, Kubectl, and Kustomize.
  • Normalizes Liquibase’s banner-heavy version output into a stable version string.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/CliScraperBase.cs Availability checks now pass each scraper’s version arguments into the executor.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/TypeDetection/ProcessCliCommandExecutor.cs Adds argument-aware probing while retaining the existing help fallback.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/TypeDetection/ResilientCliCommandExecutor.cs Forwards argument-aware availability checks directly to the inner executor.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator/Scrapers/Cli/LiquibaseCliScraper.cs Extracts a stable semantic version from supported Liquibase banner formats.
tools/ModularPipelines.OptionsGenerator/src/ModularPipelines.OptionsGenerator.Tests/Scrapers/CliVersionProbeTests.cs Verifies that affected scrapers use their supported version subcommands.

Sequence Diagram

sequenceDiagram
    participant S as CLI Scraper
    participant R as Resilient Executor
    participant P as Process Executor
    participant C as CLI Binary
    S->>R: IsAvailableAsync(executable, VersionArguments)
    R->>P: Forward tool-specific probe
    P->>C: Execute VersionArguments
    alt probe succeeds
        C-->>P: Exit code 0
        P-->>S: Available
    else probe fails
        P->>C: Execute --help
        C-->>P: Help result
        P-->>S: Availability result
    end
Loading

Reviews (5): Last reviewed commit: "test(generator): cover real version fall..." | Re-trigger Greptile

@claude

claude Bot commented Aug 23, 2026

Copy link
Copy Markdown

Code review

Summary: This PR fixes GetVersionAsync/IsAvailableAsync for six CLI scrapers (ArgoCD, eksctl, Cosign, Go, Kubectl, Kustomize) that don't support a bare --version flag, by adding a per-scraper VersionArguments override and a new ICliCommandExecutor.IsAvailableAsync(command, arguments, ct) overload that probes with the tool-specific arguments. Test coverage for the six tools themselves (CliVersionProbeTests, KubectlCliScraperTests, updated HelmCliScraperTests, ResilientCliCommandExecutorTests) is solid and correctly asserts the new probe arguments and invocation counts.

Architectural concern: availability-check fallback silently removed for every other scraper

CliScraperBase.IsAvailableAsync() now unconditionally calls the new 3-arg overload:

public virtual async Task<bool> IsAvailableAsync(CancellationToken cancellationToken = default)
{
return await Executor.IsAvailableAsync(
ExecutablePath,
VersionArguments,
cancellationToken);
}

and ProcessCliCommandExecutor's implementation of that overload is a single probe requiring ExitCode == 0, with no fallback:

public async Task<bool> IsAvailableAsync(
string command,
string arguments,
CancellationToken cancellationToken = default)
{
var result = await ExecuteAsync(command, arguments, cancellationToken);
return result.Success;
}

That replaces the old 2-arg IsAvailableAsync(command, ct) path (still present, now effectively dead for every scraper except GitCliScraper), which tried --version and then fell back to --help, accepting any exit code short of a spawn failure:

public async Task<bool> IsAvailableAsync(string command, CancellationToken cancellationToken = default)
{
try
{
// Try to get version/help to check if command exists
var result = await ExecuteAsync(command, "--version", cancellationToken);
if (result.Success)
{
return true;
}
// Some commands don't support --version, try --help
result = await ExecuteAsync(command, "--help", cancellationToken);
return result.ExitCode != -1; // -1 indicates execution failure (command not found)
}
catch
{
return false;
}
}

VersionArguments still defaults to "--version" (CliScraperBase.cs:86), and only the six scrapers touched in this PR override it. That means all ~45 other CLI scrapers (Docker, Terraform, AWS, Az, npm, Gradle, Maven, Vault, Trivy, etc.) are now routed through the strict single-probe path instead of the old resilient one — a behavior change with a blast radius far beyond the six tools this PR is fixing. For any of those tools where --version exits non-zero (some CLIs use -v/version and return a non-zero code, or don't support --version at all but do support --help), the scraper will now be reported unavailable and skipped entirely (CliScraperBase.ScrapeAsync does yield break — see CliScraperBase.cs:252), where previously it would have been detected via the --help fallback and only the version string (a GetVersionAsync best-effort, already null-safe) would have been missing.

Why this matters: the fix for 6 specific tools was implemented by changing a shared code path that every scraper flows through, and the resilience behavior that used to protect the other 45 was dropped as a side effect rather than a deliberate choice. Nothing in the diff or tests exercises "tool installed, --version fails, --help succeeds" for an untouched scraper, so this regression wouldn't be caught by the existing suite.

Suggested approach: preserve the old fallback behavior for scrapers that haven't opted into a tool-specific probe. For example, make ProcessCliCommandExecutor.IsAvailableAsync(command, arguments, ct) fall back to the existing --help-tolerant check when the initial probe fails (mirroring the fallback that already exists in the 2-arg overload), or only bypass the resilient default when VersionArguments has actually been overridden away from "--version". That keeps the fix scoped to the six tools that need it without weakening availability detection everywhere else.

No CLAUDE.md compliance issues — there's no nested CLAUDE.md under tools/ModularPipelines.OptionsGenerator/, and the changes don't touch anything governed by the root CLAUDE.md (build/test commands, solution structure).

@thomhurst

Copy link
Copy Markdown
Owner Author

@claude Please re-review exact head \

@thomhurst

Copy link
Copy Markdown
Owner Author

@claude Please re-review exact head 0d52298. The argument-aware overload now preserves the legacy tolerant --help fallback, the two-argument overload delegates to it, and a cross-platform regression covers --version failure followed by successful --help. Validation: ProcessCliCommandExecutorTests 18/18, CliVersionProbeTests 5/5, ResilientCliCommandExecutorTests 1/1.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review (re-review at 0d52298)

Previous architectural concern is resolved. The earlier review flagged that routing CliScraperBase.IsAvailableAsync() through the new 3-arg IsAvailableAsync(command, arguments, ct) dropped the --help fallback for the ~45 scrapers that don't override VersionArguments. That's fixed: ProcessCliCommandExecutor.IsAvailableAsync(command, arguments, ct) now falls back to --help when the initial probe fails (ProcessCliCommandExecutor.cs:508-516) and treats any exit code other than -1 as "available," matching the old 2-arg overload's tolerance. ResilientCliCommandExecutorTests and the new Argument_Aware_IsAvailableAsync_Falls_Back_To_Help test cover this path. Good fix, and thanks for pointing me at the exact head to re-check.

Two smaller things worth a look before merge:

1. HelmCliScraper's strict availability check is now gone

HelmCliScraper.cs previously overrode IsAvailableAsync to require Executor.ExecuteAsync(ExecutablePath, VersionArguments, ct).Success directly — i.e., helm version itself had to succeed. That override is deleted in this PR, so Helm now falls through to CliScraperBase's default, which uses the new tolerant IsAvailableAsync(command, arguments, ct) (probe version, fall back to --help, accept any exit code short of -1).

Net effect: a Helm install where helm version fails (corrupted plugin index, broken config, non-zero exit) but helm --help still exits 0 now reports available, whereas before it correctly reported unavailable. ScrapeAsync will then proceed into full command discovery for a tool whose version probe is broken, with GetVersionAsync() silently returning null.

This may well be intentional cleanup — the old strict override combined with the base class's own probing was actually causing Helm's executor to be invoked twice with "version" (see the HelmCliScraperTests diff, which changes the expected call list from ["version", "version"] to a single ["version"]), so removing the duplicate-triggering override is reasonable. But the side effect is a real loosening of Helm's specific availability semantics that isn't mentioned in the PR description. Worth either a one-line callout that this is deliberate (Helm now matches everyone else's tolerance) or, if the stricter check was actually load-bearing for Helm, keeping a narrower override that still uses the shared fallback machinery but requires the primary probe (not just --help) to succeed.

2. The new ICliCommandExecutor.IsAvailableAsync(command, arguments, ct) default-interface-method silently degrades unaware implementers

Task<bool> IsAvailableAsync(
    string command,
    string arguments,
    CancellationToken cancellationToken = default) =>
    IsAvailableAsync(command, cancellationToken);

Both production implementers (ProcessCliCommandExecutor, ResilientCliCommandExecutor) correctly override this, so today's behavior is fine. But because the default silently ignores arguments and delegates to the old single-probe overload, any future ICliCommandExecutor implementation (a decorator, a mock, a test double) that forgets to override the 3-arg version will silently drop every scraper's tool-specific VersionArguments with no compiler warning — quietly defeating the whole point of this PR for that implementer. Since this interface only has two implementers in-tree, consider making the 3-arg member abstract (no default body) so the compiler forces every implementer to make an explicit choice, rather than leaning on a default that's easy to forget to override correctly.

Neither of these blocks the fix for the six tools this PR targets (ArgoCD, eksctl, Cosign, Go, Kubectl, Kustomize) — the new CliVersionProbeTests and KubectlCliScraperTests correctly verify those probes. They're worth a decision (intentional vs. oversight) before merge given the fallback-loosening theme is exactly what the previous round of review was about.

@thomhurst

Copy link
Copy Markdown
Owner Author

@claude Please review exact head c62c59f. It preserves the legacy availability fallback and now extracts machine-readable Liquibase versions after the CLI banner. Validation: ProcessCliCommandExecutorTests 18/18, CliVersionProbeTests 5/5, ResilientCliCommandExecutorTests 1/1, LiquibaseCliScraperTests 13/13.

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code review (re-review at c62c59f5)

Re-checked against the specific request: this head adds LiquibaseCliScraper.ParseVersionOutput (extracting a machine-readable version from the CLI banner) on top of the availability-fallback fix from the previous round. Validation numbers check out against the diff (LiquibaseCliScraperTests cover both the "Liquibase Version:" and "Liquibase Community X.X.X by Liquibase" banner shapes).

1. Previous Helm finding is still unaddressed

The prior round flagged that HelmCliScraper's custom IsAvailableAsync override (which required helm version itself to succeed) was deleted, and asked for a decision: was this an intentional loosening of Helm's availability semantics, or an oversight from deduplicating the double "version" invocation? At c62c59f5, HelmCliScraper.cs still has no IsAvailableAsync override — it falls through to the shared tolerant path (probe version, fall back to --help, accept anything but ExitCode == -1). That's not wrong, but since it was called out for a decision and the PR description/commits since then don't mention it, it's worth an explicit "yes, intentional" before merge rather than leaving it as a silent side effect of the dedup fix.

2. LiquibaseVersionPattern only recognizes the Community edition banner as a standalone match

[GeneratedRegex(
    @"(?:Liquibase Version:|Liquibase (?:Community|'community' version))\s*(?<version>\d+(?:\.\d+)+(?:[-+][0-9A-Za-z.-]+)?)",
    RegexOptions.IgnoreCase)]
private static partial Regex LiquibaseVersionPattern();

The banner-only alternative hardcodes Community/'community' version. SupplementalGlobalOptions earlier in this same file references "Liquibase Secure" as an edition, so the scraper is already aware paid editions exist (e.g. Liquibase Pro X.X.X by Liquibase, Liquibase Secure X.X.X by Liquibase). In practice this is low risk — real Liquibase output pairs the banner with a separate Liquibase Version: X.X.X line, which the first alternative already matches — but if a probe ever only surfaces the edition banner without that line, ParseVersionOutput returns null and logs a warning instead of extracting a version, which is a narrower result than before (the previous default ParseVersionOutput at least returned the raw combined output). If you want this fully robust rather than "works for the shapes we've observed," consider widening the second alternative to Liquibase\s+\S+ (any single edition word) instead of enumerating specific editions, so a future edition name doesn't silently fall through to the null/warning path again.

Neither of these blocks the Liquibase version-parsing fix this round targets — both are pre-existing/edge-case concerns, not regressions introduced by this commit. Worth a quick "yes, intentional" on Helm and an optional regex widening for Liquibase before merge.

@thomhurst

Copy link
Copy Markdown
Owner Author

Addressed at exact head b12f64ed5d.

  • Helm availability loosening is intentional: the shared executor treats the tool as installed when version fails but --help succeeds; version metadata remains unavailable unless the version probe itself succeeds.
  • Liquibase banner parsing now accepts arbitrary single-word editions while preserving the legacy 'community' version form. Added Pro and Secure regressions; LiquibaseCliScraperTests: 15/15 passed.

@claude Please review exact head b12f64ed5db349fef4af3ac30457fc67f876e503.

@thomhurst

Copy link
Copy Markdown
Owner Author

@claude Please review exact head 18ef5889b84e89ee4774728b7908533e3badced2.

@thomhurst
thomhurst merged commit 314f429 into main Aug 24, 2026
15 checks passed
@thomhurst
thomhurst deleted the issue-3996-cli-version-probes-20260823 branch August 24, 2026 04:38
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.

1 participant