Skip to content

refactor: redesign CLI with positional tool names#197

Merged
shenxianpeng merged 9 commits into
mainfrom
bugfix/auto-detect-tool-name-with-version
Jun 19, 2026
Merged

refactor: redesign CLI with positional tool names#197
shenxianpeng merged 9 commits into
mainfrom
bugfix/auto-detect-tool-name-with-version

Conversation

@shenxianpeng

@shenxianpeng shenxianpeng commented Jun 18, 2026

Copy link
Copy Markdown
Member

Description

Redesigns the CLI interface based on the design review of #194. Fixes #194 and #195

Motivation

The old CLI had several design problems (see detailed review):

  • Fragile positional arg detection: used _is_version_like() to guess whether the positional arg is a version or tool name
  • Inconsistent default behavior: clang-tools install 18 installed 2 tools (via --tool default), but clang-tools install clang-format installed 1
  • Two syntaxes with different code paths: install 18 --tool clang-format and install clang-format --version 18 did different things
  • --binary/ --wheel naming: ambiguous about what each does
  • Uninstall asymmetry: version was positional, tools were flags

New Design

Install

# Positional args are always tool names
clang-tools install clang-format
clang-tools install clang-format clang-tidy

# Version is always via --version
clang-tools install clang-format --version 18

# Explicit backend via --backend
clang-tools install clang-format --version 18 
clang-tools install clang-format

# Auto-detect (default): binary first (if --version given), fall back to wheel
clang-tools install clang-include-cleaner --version 22

Uninstall

# Tools are positional, --version is required
clang-tools uninstall clang-format --version 12
clang-tools uninstall clang-format clang-tidy --version 12

Key Changes

Old New
install <version> install <tool> [<tool> ...] --version <VER>
--tool flag Removed (tools are positional args)
uninstall <version> -t <tool> uninstall <tool> --version <VER>
_is_version_like() heuristic Removed (no ambiguity)

Tests

  • Updated all existing tests for the new CLI syntax
  • Added tests for: multiple tools, unsupported tool in auto-detect

All 155 tests pass.

Summary by CodeRabbit

  • New Features

    • Install and uninstall commands now accept multiple tools as arguments.
    • Auto-detection now attempts binary installation first, with automatic fallback to wheel if binary fails.
  • Refactor

    • Install command interface redesigned for multi-tool support and explicit version handling.
    • Uninstall command now requires --version as an explicit argument instead of positional parameter.
  • Tests

    • Expanded test coverage for CLI parser behavior and backend selection logic.
    • Added tests for auto-detection fallback scenarios and multi-tool installations.

@github-actions github-actions Bot added the bug Something isn't working label Jun 18, 2026
@codecov

codecov Bot commented Jun 18, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (c9990c0) to head (efaba39).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##              main      #197    +/-   ##
==========================================
  Coverage   100.00%   100.00%            
==========================================
  Files            9         9            
  Lines         1072       944   -128     
==========================================
- Hits          1072       944   -128     

☔ View full report in Codecov by Harness.
📢 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.

@coderabbitai

coderabbitai Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

clang_tools/main.py is refactored to replace boolean --binary/--wheel flags with a unified --backend {auto,binary,wheel} option. The CLI parser now accepts multiple tools positional arguments and routes through dedicated backend handlers: binary validates semantic versions and calls install_clang_tools; wheel validates tool names against WHEEL_TOOLS and calls _wheel_install; auto attempts binary-first with wheel fallback when --version is explicit, otherwise wheel-only. Tests comprehensively cover parser changes, all backend paths, error cases, and multi-tool scenarios.

Changes

Backend-handler CLI architecture with auto-detect routing

Layer / File(s) Summary
CLI parser refactoring
clang_tools/main.py, tests/test_main.py
Module documentation rewritten with new CLI examples. install subcommand now accepts positional tools, --backend {auto,binary,wheel}, and optional --version; uninstall takes positional tools and requires --version. Import cleanup removes deprecated _is_version_like. Parser assertions updated to cover multiple tools, all backend modes, and new flag options.
Backend handler implementation and dispatcher
clang_tools/main.py
Three dedicated handlers replace prior logic: _install_binary validates semantic version and delegates to install_clang_tools, converting (OSError, ValueError) to failure or fallback; _install_wheel validates tools against WHEEL_TOOLS and calls _wheel_install; _install_auto routes binary-first with fallback on error when --version is explicit, otherwise wheel-only. Dispatcher _handle_install routes by backend choice. main() updated to pass multi-tool arguments.
Binary backend integration and error tests
tests/test_main.py
Integration test validates --backend binary success with versioned binary output. Error tests ensure missing --version and invalid semver (0.0.0) both exit with nonzero code and appropriate stderr messages. Download error test forces binary failure and asserts "Binary install failed" message.
Wheel backend validation and integration tests
tests/test_main.py
Tests validate successful --backend wheel install and tool validation against WHEEL_TOOLS. Rejection of unknown tools and acceptance of wheel-only tools (e.g., clang-include-cleaner) verified. Failure propagation and correct version/tool argument passing to _wheel_install asserted.
Auto-detection with binary-first and wheel fallback
tests/test_main.py
Tests cover: no-version case routing to wheel; binary success with real file creation; forced binary failure via invalid binary_repo URL with "falling back to wheel" stderr message and wheel install; out-of-range and bad-semver versions triggering wheel fallback; unknown-tool errors; multi-tool binary installation producing versioned binaries for each tool.
Uninstall command and test cleanup
tests/test_main.py
Uninstall tests validate multi-tool removal via new positional tools argument and required --version. Old test blocks removed; mocked resolver updated for multi-tool mixed-success scenarios.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • cpp-linter/clang-tools-pip#179: Modifies the same auto-detection/install dispatch in clang_tools/main.py for the "binary first, wheel fallback" routing architecture.
  • cpp-linter/clang-tools-pip#185: Expands WHEEL_TOOLS and adjusts _wheel_install/wheel resolver behavior that the refactored CLI backend handler now validates and exercises.
  • cpp-linter/clang-tools-pip#193: Establishes the positional tool-name argument pattern with --version that this PR's backend/auto-detect logic builds upon.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 71.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: refactoring CLI design to use positional tool names and a --backend flag.
Linked Issues check ✅ Passed The PR successfully addresses issue #194 by eliminating the fragile version detection heuristic and enabling straightforward tool installation via positional arguments.
Out of Scope Changes check ✅ Passed All changes are directly aligned with CLI redesign objectives: positional tool names, --version flag, --backend flag, and unified command structures.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_main.py`:
- Around line 356-375: The test function
test_main_install_auto_detect_tool_name_with_version_fallback can skip the
download path if the tool is already installed in the default directory, making
the test non-deterministic and potentially not exercising the mocked invalid
binary_repo. Add command line arguments to the sys.argv list to specify the
install directory using tmp_path as an isolated location, and change the working
directory to tmp_path (using monkeypatch or a context manager) to ensure the
test always exercises the fallback code path regardless of what is cached on the
system.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d1edaa8a-420c-4ca9-9b50-85e3186875a2

📥 Commits

Reviewing files that changed from the base of the PR and between c9990c0 and 5e7dab6.

📒 Files selected for processing (2)
  • clang_tools/main.py
  • tests/test_main.py

Comment thread tests/test_main.py Outdated
@shenxianpeng shenxianpeng changed the title fix: auto-detect supports tool name as positional arg with --version refactor: redesign CLI with positional tool names and --backend flag Jun 19, 2026
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Jun 19, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/test_main.py`:
- Around line 610-632: The test_main_install_backend_binary_download_error test
is not hermetic because it uses the default installation directory which may
already contain the binary, causing the test to skip the download path and
produce non-deterministic results. Add the tmp_path pytest fixture parameter to
the function signature and modify the sys.argv list to include an --install-dir
argument that points to this temporary directory. This ensures a clean, isolated
directory is used for each test run and guarantees the download failure path is
always exercised.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 179743a7-0a94-46c5-aca3-42f71a7ed153

📥 Commits

Reviewing files that changed from the base of the PR and between 5e7dab6 and 2fb0baf.

📒 Files selected for processing (2)
  • clang_tools/main.py
  • tests/test_main.py

Comment thread tests/test_main.py Outdated
Comment on lines +610 to +632
def test_main_install_backend_binary_download_error(
monkeypatch: pytest.MonkeyPatch, capsys
):
"""``--backend binary`` that fails reports the error and returns 1."""
monkeypatch.setattr("clang_tools.install.binary_repo", "not-a-valid-url")
monkeypatch.setattr(
sys,
"argv",
["clang-tools", "install", "0.0.0"],
[
"clang-tools",
"install",
"clang-format",
"--version",
"12",
"--backend",
"binary",
"--no-progress-bar",
],
)
exit_code = main()
result = capsys.readouterr()
assert exit_code == 1
assert "not a semantic" in result.err
assert "Binary install failed" in result.err

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make the binary download error test hermetic by using an isolated install directory.

This test can skip the download path if clang-format-12 is already installed in the default directory. The binary install might succeed (returning 0) or fail differently during SHA verification, making the test non-deterministic.

Use tmp_path as the install directory to ensure the download failure path is always exercised.

Suggested fix
 def test_main_install_backend_binary_download_error(
-    monkeypatch: pytest.MonkeyPatch, capsys
+    monkeypatch: pytest.MonkeyPatch, capsys, tmp_path
 ):
     """``--backend binary`` that fails reports the error and returns 1."""
+    monkeypatch.chdir(tmp_path)
     monkeypatch.setattr("clang_tools.install.binary_repo", "not-a-valid-url")
     monkeypatch.setattr(
         sys,
         "argv",
         [
             "clang-tools",
             "install",
             "clang-format",
             "--version",
             "12",
             "--backend",
             "binary",
+            "--directory",
+            str(tmp_path),
             "--no-progress-bar",
         ],
     )
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_main.py` around lines 610 - 632, The
test_main_install_backend_binary_download_error test is not hermetic because it
uses the default installation directory which may already contain the binary,
causing the test to skip the download path and produce non-deterministic
results. Add the tmp_path pytest fixture parameter to the function signature and
modify the sys.argv list to include an --install-dir argument that points to
this temporary directory. This ensures a clean, isolated directory is used for
each test run and guarantees the download failure path is always exercised.

@shenxianpeng shenxianpeng added breaking Breaking change and removed documentation Improvements or additions to documentation labels Jun 19, 2026
@shenxianpeng shenxianpeng merged commit e7a5a43 into main Jun 19, 2026
72 checks passed
@shenxianpeng shenxianpeng deleted the bugfix/auto-detect-tool-name-with-version branch June 19, 2026 01:29
@shenxianpeng shenxianpeng changed the title refactor: redesign CLI with positional tool names and --backend flag refactor: redesign CLI with positional tool names Jun 19, 2026
@sonarqubecloud

Copy link
Copy Markdown

@shenxianpeng shenxianpeng restored the bugfix/auto-detect-tool-name-with-version branch June 19, 2026 01:32
@shenxianpeng shenxianpeng added minor A minor version bump and removed bug Something isn't working labels Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking Breaking change minor A minor version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Failed to install clang-include-cleaner

1 participant