Skip to content

Conversation

@2bndy5
Copy link
Contributor

@2bndy5 2bndy5 commented Sep 4, 2025

This new _version.py file is gitignored and generated at build time. It will be distributed to pypi though.

The generated code is used as

  1. a part of the custom User-Agent request header (cpp-linter/<version>). This should help identify REST API requests as unique requests from this software. The requests lib default user-agent value is set similarly but specific to the requests library (python-requests/X.Y.Z).
  2. the output of the new CLI subcommand cpp-linter version. This is meant to differentiate between the global --version arg used to specify a clang version.

Other changes

  • ran uv sync --all-groups -U to upgrade locked dependencies
  • update pre-commit hooks and ran them
  • updated the test about listing changed files to ensure the user-agent is set accordingly.
  • updated test about CLI parsing to ensure the new subcommand is respected (when used)
  • updated CLI docs about new subcommand

All of this is already implemented in the cpp-linter-rs port. So, this patch aligns with the future v2, disregarding the difference in --help output.

Summary by CodeRabbit

  • New Features

    • Added a CLI subcommand to print the tool’s version and exit.
    • Standardized User-Agent header for API requests to include the current version.
  • Documentation

    • CLI docs now include a Subcommands section with version badges.
  • Chores

    • Updated pre-commit tool versions.
    • Enabled reading version from an autogenerated version file and ignored that file.
  • Tests

    • Updated tests to expect the versioned User-Agent and to cover the new version subcommand.

This new `_version.py` file is gitignored and generated at build time. It will be distributed to pypi though.

The generated code is used as
1. a part of the custom `User-Agent` request header (`cpp-linter/<version>`).
   This should help identify REST API requests as unique requests from this software. The `requests` lib default user-agent value is set similarly but specific to the requests library (`python-requests/X.Y.Z`).
2. the output of the new CLI subcommand `cpp-linter version`.
   This is meant to differentiate between the global `--version` arg used to specify a clang version.

### Other changes
- ran `uv sync --all-groups -U` to upgrade locked dependencies
- update pre-commit hooks and ran them
- updated the test about listing changed files to ensure the user-agent is set accordingly.
- updated test about CLI parsing to ensure the new subcommand is respected (when used)
- updated CLI docs about new subcommand

---
All of this is already implemented in the cpp-linter-rs port.
So, this patch aligns with the future v2, disregarding the difference in `--help` output.
@2bndy5 2bndy5 added enhancement New feature or request minor A minor version bump labels Sep 4, 2025
@2bndy5
Copy link
Contributor Author

2bndy5 commented Sep 4, 2025

This is the revised branch based on #150 after #151, #152, and #153 were merged.

@coderabbitai
Copy link

coderabbitai bot commented Sep 4, 2025

Walkthrough

Adds a version CLI subcommand and early-exit printing, exposes the generated package version into modules, sets a User-Agent header for GitHub requests, documents subcommands with badges, bumps pre-commit hook revisions, updates pyproject and .gitignore for the generated version file, and adjusts tests.

Changes

Cohort / File(s) Summary
CLI subcommand & flow
cpp_linter/cli.py, cpp_linter/__init__.py
Adds Args.command and a version subcommand; parser wiring updated; main() prints and returns when command == "version".
Version exposure & request headers
cpp_linter/rest_api/__init__.py, cpp_linter/rest_api/github_api.py, cpp_linter/__init__.py, tests/list_changes/test_get_file_changes.py
Imports generated version and defines USER_AGENT = f"cpp-linter/{version}"; make_headers now sets User-Agent unconditionally; tests updated to expect the header.
Docs: subcommand documentation
docs/conf.py
Adds SUBCOMMAND_VERSIONS, typing adjustments, and emits a Subcommands section with per-subcommand descriptions and version badges.
Tooling & project metadata
.pre-commit-config.yaml, pyproject.toml
Bumps pre-commit hook revisions (pre-commit-hooks, astral-sh/ruff-pre-commit, mirrors-mypy, cspell); renames Ruff hook id ruffruff-check; constrains setuptools-scm >=8; adds version_file = "cpp_linter/_version.py"; removes [tool.codespell].
Tests: CLI parsing
tests/test_cli_args.py
Asserts args.command is None for standalone options; adds test_version_cmd verifying version subcommand parsing.
VCS ignore
.gitignore
Adds ignore entry for generated cpp_linter/_version.py with explanatory comment.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as cpp_linter CLI
  participant Parser as argparse
  participant App as cpp_linter main

  User->>CLI: Invoke `cpp-linter [options|version]`
  CLI->>Parser: parse_args()
  Parser-->>CLI: Args(command=?)
  alt command == "version"
    CLI->>User: print(version)
    CLI-->>User: exit
  else
    CLI->>App: continue normal workflow
    App-->>User: results
  end
Loading
sequenceDiagram
  autonumber
  participant Client as cpp_linter.rest_api
  participant GH as GitHub API

  Note over Client: build request headers
  Client->>Client: headers["User-Agent"] = "cpp-linter/{version}"
  opt GITHUB_TOKEN present
    Client->>Client: headers["Authorization"] = "token …"
  end
  Client->>GH: HTTP request (with headers)
  GH-->>Client: Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

dependencies

Suggested reviewers

  • shenxianpeng

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 968c894 and 7dce7a3.

📒 Files selected for processing (1)
  • cpp_linter/__init__.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • cpp_linter/init.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: test (ubuntu-latest, 10)
  • GitHub Check: test (ubuntu-latest, 17)
  • GitHub Check: test (ubuntu-latest, 13)
  • GitHub Check: test (ubuntu-latest, 9)
  • GitHub Check: test (ubuntu-latest, 15)
  • GitHub Check: test (ubuntu-latest, 11)
  • GitHub Check: test (ubuntu-latest, 12)
  • GitHub Check: test (ubuntu-latest, 19)
  • GitHub Check: test (ubuntu-latest, 20)
  • GitHub Check: test (ubuntu-latest, 16)
  • GitHub Check: test (ubuntu-latest, 21)
  • GitHub Check: test (ubuntu-latest, 14)
  • GitHub Check: test (ubuntu-latest, 18)
  • GitHub Check: test (windows-latest, 10)
  • GitHub Check: test (windows-latest, 13)
  • GitHub Check: test (windows-latest, 19)
  • GitHub Check: test (windows-latest, 16)
  • GitHub Check: test (windows-latest, 21)
  • GitHub Check: test (windows-latest, 15)
  • GitHub Check: test (windows-latest, 20)
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch generated-version-info

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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

🧹 Nitpick comments (6)
cpp_linter/__init__.py (1)

12-12: Make version import resilient when _version.py is missing (editable/dev installs).

Fall back to importlib.metadata when the generated file isn’t present to avoid ImportError during local runs or docs builds.

-from ._version import version
+try:
+    from ._version import version
+except Exception:
+    from importlib.metadata import version as _pkg_version
+    version = _pkg_version("cpp-linter")
docs/conf.py (3)

242-242: Provide a safe default for subcommand badge versions.

Prevents KeyError if a subcommand lacks an entry and eases future additions.

-SUBCOMMAND_VERSIONS = {"version": "1.11.0"}
+SUBCOMMAND_VERSIONS = {"version": "1.11.0"}  # minimal supporting versions
+DEFAULT_SUBCOMMAND_VERSION = release

293-299: Replace asserts with safe guards (ruff S101) and handle missing help text.

Avoids runtime aborts during doc build while keeping output stable.

-            assert arg.dest is not None
-            if arg.dest != "command":
-                doc.write(f"\n.. std:option:: {arg.dest.lower()}\n\n")
-                assert arg.help is not None
-                doc.write("\n    ".join(arg.help.splitlines()))
+            if arg.dest is None:
+                continue
+            if arg.dest != "command":
+                doc.write(f"\n.. std:option:: {arg.dest.lower()}\n\n")
+                help_text = arg.help or ""
+                doc.write("\n    ".join(help_text.splitlines()))
             else:
                 sub_commands = arg

327-339: Harden Subcommands section and remove assert (ruff S101).

  • Drop redundant assert.
  • Fallback version if not mapped.
  • Tolerate missing descriptions.
-        if sub_commands is not None:
-            assert sub_commands is not None
+        if sub_commands is not None:
             choices = cast(Dict[str, argparse.ArgumentParser], sub_commands.choices)
             doc.write("\n\nSubcommands\n")
             doc.write("-----------\n")
             for sub_cmd in choices:
                 doc.write(f"\n.. std:option:: {sub_cmd}\n")
-                version = SUBCOMMAND_VERSIONS[sub_cmd]
+                version = SUBCOMMAND_VERSIONS.get(sub_cmd, DEFAULT_SUBCOMMAND_VERSION)
                 doc.write(f"\n    :badge-version:`{version}`\n\n    ")
-                sub_cmd_action = choices[sub_cmd]
-                assert sub_cmd_action.description is not None
-                doc.write("\n    ".join(sub_cmd_action.description.splitlines()) + "\n")
+                sub_cmd_action = choices[sub_cmd]
+                desc = getattr(sub_cmd_action, "description", "") or ""
+                doc.write("\n    ".join(desc.splitlines()) + "\n")
tests/list_changes/test_get_file_changes.py (1)

8-8: Prefer public API/constant over importing a private, build-generated module.

Importing cpp_linter._version can break local/editable installs where _version.py isn’t present. Use the public constant already exposed by the package and remove string duplication in headers.

Apply:

-from cpp_linter._version import version
+from cpp_linter.rest_api import USER_AGENT
-                "User-Agent": f"cpp-linter/{version}",
+                "User-Agent": USER_AGENT,
-                        "User-Agent": f"cpp-linter/{version}",
+                        "User-Agent": USER_AGENT,

Also applies to: 118-126, 136-142

cpp_linter/rest_api/__init__.py (1)

16-16: Drop the version‐import fallback and set a default User-Agent on the session

  • _version.py is always generated by setuptools-scm (dynamic = ["version"] in pyproject.toml), so remove the try/except around from .._version import version.
  • In RestApiClient.__init__, after creating self.session, add:
    self.session.headers.update({"User-Agent": USER_AGENT})
    to apply the default UA globally.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d56a0ef and 425cbce.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • .pre-commit-config.yaml (3 hunks)
  • cpp_linter/__init__.py (1 hunks)
  • cpp_linter/cli.py (2 hunks)
  • cpp_linter/rest_api/__init__.py (1 hunks)
  • cpp_linter/rest_api/github_api.py (2 hunks)
  • docs/conf.py (4 hunks)
  • pyproject.toml (1 hunks)
  • tests/list_changes/test_get_file_changes.py (3 hunks)
  • tests/test_cli_args.py (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-04T02:26:17.714Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#150
File: cpp_linter/__init__.py:12-12
Timestamp: 2025-09-04T02:26:17.714Z
Learning: In the cpp-linter project, _version.py is always generated by setuptools_scm during build/install, so ImportError fallbacks for this import are not needed.

Applied to files:

  • .gitignore
  • pyproject.toml
📚 Learning: 2025-09-04T02:26:17.714Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#150
File: cpp_linter/__init__.py:12-12
Timestamp: 2025-09-04T02:26:17.714Z
Learning: In the cpp-linter project, setuptools-scm generates _version.py at install time and distributes it with both editable and binary wheels, making fallback imports unnecessary for the version module.

Applied to files:

  • pyproject.toml
📚 Learning: 2025-09-04T02:25:26.661Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#150
File: cpp_linter/rest_api/__init__.py:16-0
Timestamp: 2025-09-04T02:25:26.661Z
Learning: setuptools-scm with version_file configuration generates the _version.py file at install time, including for editable installs (pip install -e .), making direct imports from the generated version file reliable without need for fallback imports.

Applied to files:

  • pyproject.toml
📚 Learning: 2025-05-31T03:53:32.334Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#143
File: noxfile.py:10-10
Timestamp: 2025-05-31T03:53:32.334Z
Learning: In cpp-linter project, dev dependencies including `nox`, `mypy`, `ruff`, `pre-commit`, `rich`, and `types-requests` are properly configured in pyproject.toml under the `[dependency-groups]` dev section. Running `uv sync` installs all necessary dev requirements.

Applied to files:

  • pyproject.toml
🧬 Code graph analysis (3)
tests/test_cli_args.py (1)
cpp_linter/cli.py (2)
  • get_cli_parser (390-405)
  • Args (8-73)
cpp_linter/rest_api/github_api.py (1)
cpp_linter/rest_api/__init__.py (2)
  • RestApiClient (35-317)
  • RateLimitHeaders (26-32)
cpp_linter/__init__.py (1)
cpp_linter/cli.py (2)
  • get_cli_parser (390-405)
  • Args (8-73)
🪛 Ruff (0.12.2)
tests/test_cli_args.py

51-51: Use of assert detected

(S101)


58-58: Use of assert detected

(S101)

docs/conf.py

292-292: Use of assert detected

(S101)


295-295: Use of assert detected

(S101)


328-328: Use of assert detected

(S101)


337-337: Use of assert detected

(S101)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: test (windows-latest, 10)
  • GitHub Check: test (windows-latest, 14)
  • GitHub Check: test (windows-latest, 17)
  • GitHub Check: test (windows-latest, 11)
  • GitHub Check: test (windows-latest, 19)
  • GitHub Check: test (windows-latest, 18)
  • GitHub Check: test (windows-latest, 21)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (12)
.gitignore (1)

183-184: Good call: ignore generated version file.

Keeping cpp_linter/_version.py out of VCS matches the build-time generation flow and avoids churn. This aligns with the project learning that _version.py is always generated by setuptools_scm.

cpp_linter/cli.py (2)

72-74: Field addition looks right.

Args.command: Optional[str] = None cleanly captures the chosen subcommand and keeps it distinct from the clang tools --version option.


400-405: Subcommand wiring LGTM.

dest="command" with a dedicated version subparser is straightforward and keeps backward compatibility for global flags/positionals.

.pre-commit-config.yaml (1)

6-6: Pre-commit bumps and Ruff hook rename look good.

  • Updated hook revs and switched ruff -> ruff-check correctly.
  • No breaking changes expected here. Consider running pre-commit run -a once on the branch to validate locally/CI.

Also applies to: 21-26, 28-29, 39-42

tests/test_cli_args.py (2)

51-52: Nice assertion on default subcommand.

Verifies no subcommand contaminates global-flag parsing.


55-59: Subcommand parse test is concise and effective.

Covers the new version path without over-specifying behavior.

cpp_linter/__init__.py (1)

19-23: Early exit for version subcommand looks good.

Straightforward and side-effect free before any I/O.

docs/conf.py (3)

6-6: Import argparse for subcommand docs — OK.


10-10: Typing imports — OK.


284-285: Capturing the subparsers action — OK.

cpp_linter/rest_api/github_api.py (2)

32-32: Importing USER_AGENT here is correct and avoids duplication.


199-207: Setting a consistent User-Agent — good.

Header is now unconditional and also applied on per-request overrides.

@coderabbitai
Copy link

coderabbitai bot commented Sep 4, 2025

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch generated-version-info

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 3

🧹 Nitpick comments (7)
cpp_linter/cli.py (1)

400-404: Subparser for version — consider minor ergonomics

Explicitly set required=False for stability across Python versions and future-proofing. Optional: wire a handler via set_defaults(func=...) to avoid branching in main().

-    sub_parser = cli_parser.add_subparsers(
-        description="Subcommands for special behavior", dest="command"
-    )
+    sub_parser = cli_parser.add_subparsers(
+        description="Subcommands for special behavior", dest="command", required=False
+    )
-    version_help = "Show the cpp-linter version and exit"
-    sub_parser.add_parser("version", description=version_help, help=version_help)
+    version_help = "Show the cpp-linter version and exit"
+    sub_parser.add_parser("version", description=version_help, help=version_help)
+    # Optional:
+    # sub_parser.choices["version"].set_defaults(func=lambda *_: print(version))
tests/test_cli_args.py (1)

51-59: Ruff S101 in tests — silence or configure

If Bandit’s S101 is enforced on tests, add a targeted ignore or configure per-file ignores.

Option A (inline ignores):

-    assert getattr(args, attr_name) == attr_value
-    assert args.command is None
+    assert getattr(args, attr_name) == attr_value  # noqa: S101 - pytest assert in tests
+    assert args.command is None  # noqa: S101 - pytest assert in tests

Option B (preferred: repo-wide config):

  • Add to pyproject.toml:
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]
cpp_linter/__init__.py (1)

19-23: Early-exit for version — small polish optional

Current approach works. If you adopt handler-based subcommands, this branch can be removed. If kept, consider sys.exit(0) for explicit exit status (behavior is effectively the same today).

-    if args.command == "version":
-        print(version)
-        return
+    if args.command == "version":
+        print(version)
+        return  # or: sys.exit(0)
cpp_linter/rest_api/github_api.py (1)

32-32: Standardized User-Agent is correctly applied.

Importing USER_AGENT and setting it in make_headers() aligns with GitHub’s requirements and your tests.

Since __init__ seeds session.headers via self.make_headers(), you could rely on the session default UA for most calls and only override Accept per request to reduce per-call header construction. Optional.

Also applies to: 206-207

tests/list_changes/test_get_file_changes.py (2)

8-8: Avoid importing the private module; use the public constant instead.

Import USER_AGENT from cpp_linter.rest_api to decouple tests from the generated _version.py file and keep expectations DRY with production.

-from cpp_linter._version import version
+from cpp_linter.rest_api import USER_AGENT

122-123: Use USER_AGENT to build request header consistently.

Prevents future drift if the prefix changes and removes direct dependency on version in tests.

-                "User-Agent": f"cpp-linter/{version}",
+                "User-Agent": USER_AGENT,

Also applies to: 140-141

cpp_linter/rest_api/__init__.py (1)

16-16: Make version import resilient in editable/dev installs.

cpp_linter/_version.py may not exist before build; add a fallback to package metadata to avoid import-time failures.

-from .._version import version
+try:
+    # Generated at build/install time
+    from .._version import version
+except Exception:
+    try:
+        from importlib.metadata import version as _pkg_version  # py3.8+
+    except Exception:  # pragma: no cover
+        from importlib_metadata import version as _pkg_version  # backport
+    try:
+        version = _pkg_version("cpp-linter")
+    except Exception:
+        version = "0.0.0+dev"

If your CI already generates _version.py before tests, ignore; otherwise consider adding this for robustness.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between d56a0ef and 425cbce.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (10)
  • .gitignore (1 hunks)
  • .pre-commit-config.yaml (3 hunks)
  • cpp_linter/__init__.py (1 hunks)
  • cpp_linter/cli.py (2 hunks)
  • cpp_linter/rest_api/__init__.py (1 hunks)
  • cpp_linter/rest_api/github_api.py (2 hunks)
  • docs/conf.py (4 hunks)
  • pyproject.toml (1 hunks)
  • tests/list_changes/test_get_file_changes.py (3 hunks)
  • tests/test_cli_args.py (1 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-04T02:26:17.714Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#150
File: cpp_linter/__init__.py:12-12
Timestamp: 2025-09-04T02:26:17.714Z
Learning: In the cpp-linter project, _version.py is always generated by setuptools_scm during build/install, so ImportError fallbacks for this import are not needed.

Applied to files:

  • pyproject.toml
  • .gitignore
📚 Learning: 2025-09-04T02:26:17.714Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#150
File: cpp_linter/__init__.py:12-12
Timestamp: 2025-09-04T02:26:17.714Z
Learning: In the cpp-linter project, setuptools-scm generates _version.py at install time and distributes it with both editable and binary wheels, making fallback imports unnecessary for the version module.

Applied to files:

  • pyproject.toml
  • .gitignore
📚 Learning: 2025-09-04T02:25:26.661Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#150
File: cpp_linter/rest_api/__init__.py:16-0
Timestamp: 2025-09-04T02:25:26.661Z
Learning: setuptools-scm with version_file configuration generates the _version.py file at install time, including for editable installs (pip install -e .), making direct imports from the generated version file reliable without need for fallback imports.

Applied to files:

  • pyproject.toml
📚 Learning: 2025-05-31T03:53:32.334Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#143
File: noxfile.py:10-10
Timestamp: 2025-05-31T03:53:32.334Z
Learning: In cpp-linter project, dev dependencies including `nox`, `mypy`, `ruff`, `pre-commit`, `rich`, and `types-requests` are properly configured in pyproject.toml under the `[dependency-groups]` dev section. Running `uv sync` installs all necessary dev requirements.

Applied to files:

  • pyproject.toml
🧬 Code graph analysis (2)
tests/test_cli_args.py (1)
cpp_linter/cli.py (2)
  • get_cli_parser (390-405)
  • Args (8-73)
cpp_linter/__init__.py (1)
cpp_linter/cli.py (2)
  • get_cli_parser (390-405)
  • Args (8-73)
🪛 Ruff (0.12.2)
tests/test_cli_args.py

51-51: Use of assert detected

(S101)


58-58: Use of assert detected

(S101)

docs/conf.py

292-292: Use of assert detected

(S101)


295-295: Use of assert detected

(S101)


328-328: Use of assert detected

(S101)


337-337: Use of assert detected

(S101)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (17)
  • GitHub Check: test (ubuntu-latest, 10)
  • GitHub Check: test (ubuntu-latest, 14)
  • GitHub Check: test (ubuntu-latest, 13)
  • GitHub Check: test (windows-latest, 9)
  • GitHub Check: test (windows-latest, 16)
  • GitHub Check: test (windows-latest, 10)
  • GitHub Check: test (windows-latest, 20)
  • GitHub Check: test (windows-latest, 14)
  • GitHub Check: test (windows-latest, 15)
  • GitHub Check: test (windows-latest, 12)
  • GitHub Check: test (windows-latest, 17)
  • GitHub Check: test (ubuntu-latest, 21)
  • GitHub Check: test (windows-latest, 11)
  • GitHub Check: test (windows-latest, 19)
  • GitHub Check: test (windows-latest, 18)
  • GitHub Check: test (windows-latest, 21)
  • GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (9)
.gitignore (1)

183-184: Ignore generated version file — good call

Matches setuptools-scm’s version_file config and avoids dirty working trees. No further action.

cpp_linter/cli.py (1)

72-73: Add Args.command to carry subcommands — LGTM

Keeps argparse results typed in your pseudo-namespace.

tests/test_cli_args.py (2)

51-52: Assert default subcommand state — LGTM

Validates command is unset without a subcommand.


55-59: Version subcommand parsing — LGTM

Parses and exposes args.command == "version" as intended.

pyproject.toml (1)

61-62: setuptools-scm write-out — aligned with usage

version_file = "cpp_linter/_version.py" pairs correctly with the import sites and .gitignore entry. One check: confirm your CI build step (sdist/wheel and editable installs) runs in an SCM context so fallback_version isn’t shipped unintentionally.

Would you like a quick CI check snippet that builds both artifacts and verifies _version.py is present in the wheel and that cpp-linter version prints a non-fallback value?

cpp_linter/__init__.py (1)

12-12: Direct import of generated version — LGTM

Consistent with setuptools-scm’s version_file behavior (including editable installs). No fallback import needed per project policy.

.pre-commit-config.yaml (1)

6-6: Hook bumps and ruff hook rename look fine; please confirm CI passes with new IDs/versions.

  • pre-commit-hooks v6, ruff-pre-commit v0.12.11 (ruff-check), mypy v1.17.1, and cspell v9.2.0 align with current ecosystems. Just ensure:
    • Local pre-commit run succeeds (especially ruff-check vs ruff).
    • The mypy mirror tag exists in CI runners.

Also applies to: 21-21, 24-24, 28-28, 39-39

docs/conf.py (1)

6-6: Imports added are appropriate for subcommand rendering and typing.

Also applies to: 10-10

cpp_linter/rest_api/__init__.py (1)

23-23: Consistent User-Agent definition is a good addition.

Centralizing the UA string here simplifies maintenance across clients and tests.

@codecov
Copy link

codecov bot commented Sep 4, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.26%. Comparing base (d56a0ef) to head (7dce7a3).
⚠️ Report is 13 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #155      +/-   ##
==========================================
+ Coverage   98.25%   98.26%   +0.01%     
==========================================
  Files          23       23              
  Lines        1886     1899      +13     
==========================================
+ Hits         1853     1866      +13     
  Misses         33       33              

☔ 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.

Copy link

@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: 0

🧹 Nitpick comments (2)
docs/conf.py (2)

6-10: Imports look good; optional typing modernization.

Optional: if the supported Python is >=3.9, consider dropping Dict/Optional and using builtin generics/PEP 604 unions to reduce imports.


327-338: Tighten subcommand emission: iterate items, avoid double indexing, stable ordering, optional fallback for badge.

  • Iterate choices.items() to avoid repeated lookups and simplify the loop.
  • Optionally sort for stable, alphabetic output.
  • If you want every subcommand to carry a badge, fall back to release; otherwise your current get(...) is fine and avoids implying a wrong minimum.

Apply this diff:

-        if sub_commands is not None:
-            choices = cast(Dict[str, argparse.ArgumentParser], sub_commands.choices)
+        if sub_commands is not None:
+            choices = cast(dict[str, argparse.ArgumentParser], sub_commands.choices)
             doc.write("\n\nSubcommands\n")
             doc.write("-----------\n")
-            for sub_cmd in choices:
-                doc.write(f"\n.. std:option:: {sub_cmd}\n\n    ")
-                version = SUBCOMMAND_VERSIONS.get(sub_cmd)
-                if version is not None:
-                    doc.write(f":badge-version:`{version}`\n\n    ")
-                sub_cmd_action = choices[sub_cmd]
-                assert sub_cmd_action.description is not None
-                doc.write("\n    ".join(sub_cmd_action.description.splitlines()) + "\n")
+            for sub_cmd, sub_cmd_action in sorted(choices.items()):
+                doc.write(f"\n.. std:option:: {sub_cmd}\n\n    ")
+                version = SUBCOMMAND_VERSIONS.get(sub_cmd, release)
+                doc.write(f":badge-version:`{version}`\n\n    ")
+                assert sub_cmd_action.description is not None
+                doc.write("\n    ".join(sub_cmd_action.description.splitlines()) + "\n")

Note: Good call switching to get(...) to avoid a KeyError when a new subcommand is added.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 425cbce and acaa788.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • docs/conf.py (4 hunks)
  • pyproject.toml (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pyproject.toml
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-04T23:14:04.822Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#155
File: docs/conf.py:292-292
Timestamp: 2025-09-04T23:14:04.822Z
Learning: Ruff S* (flake8-bandit) rules are not enabled by default in the cpp-linter project, so assert statements don't trigger S101 warnings. This is intentional because assert isinstance() is common practice for type checking.

Applied to files:

  • docs/conf.py
📚 Learning: 2025-09-04T23:06:44.559Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#155
File: docs/conf.py:284-299
Timestamp: 2025-09-04T23:06:44.559Z
Learning: In cpp-linter project, the maintainer 2bndy5 prefers using assert statements in documentation generation code (like docs/conf.py) rather than safe guards, as they want the doc generation to fail fast if expected conditions aren't met. This ensures complete CLI documentation is produced.

Applied to files:

  • docs/conf.py
📚 Learning: 2024-10-05T05:49:09.136Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#124
File: cpp_linter/clang_tools/clang_tidy.py:145-145
Timestamp: 2024-10-05T05:49:09.136Z
Learning: In the `get_suggestions_from_patch` method in `cpp_linter/clang_tools/clang_tidy.py`, it is acceptable to use `assert` statements for type checking when the assertion cannot evaluate as false due to `review_comments` data being passed by reference, and when it's intended exclusively for type checking tools. In such cases, writing unit tests to cover the assertion may be unnecessary.

Applied to files:

  • docs/conf.py
📚 Learning: 2025-05-31T03:53:32.334Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#143
File: noxfile.py:10-10
Timestamp: 2025-05-31T03:53:32.334Z
Learning: The cpp-linter project does not use pylint for linting because it's too slow. Instead, it uses `mypy` for type checking and `ruff` for linting, both listed as dev dependencies in pyproject.toml.

Applied to files:

  • docs/conf.py
🪛 Ruff (0.12.2)
docs/conf.py

292-292: Use of assert detected

(S101)


295-295: Use of assert detected

(S101)


337-337: Use of assert detected

(S101)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (21)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: test (windows-latest, 14)
  • GitHub Check: test (ubuntu-latest, 9)
  • GitHub Check: test (ubuntu-latest, 12)
  • GitHub Check: test (ubuntu-latest, 15)
  • GitHub Check: test (windows-latest, 9)
  • GitHub Check: test (ubuntu-latest, 19)
  • GitHub Check: test (windows-latest, 11)
  • GitHub Check: test (windows-latest, 10)
  • GitHub Check: test (ubuntu-latest, 21)
  • GitHub Check: test (ubuntu-latest, 20)
  • GitHub Check: test (ubuntu-latest, 18)
  • GitHub Check: test (windows-latest, 12)
  • GitHub Check: test (windows-latest, 13)
  • GitHub Check: test (windows-latest, 15)
  • GitHub Check: test (windows-latest, 17)
  • GitHub Check: test (windows-latest, 19)
  • GitHub Check: test (windows-latest, 16)
  • GitHub Check: test (windows-latest, 18)
  • GitHub Check: test (windows-latest, 20)
  • GitHub Check: test (windows-latest, 21)
🔇 Additional comments (2)
docs/conf.py (2)

284-299: Correctly capturing subparsers and excluding the dispatcher from positional docs.

Using sub_commands to skip the "command" dest and handle subcommands later reads cleanly. Assert usage here aligns with the project’s fail-fast preference for docs generation.


242-242: SUBCOMMAND_VERSIONS is in sync with CLI subcommands. No missing or orphaned keys detected.

because min python version (`>= 3.9`) is sufficient
Copy link

@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: 0

♻️ Duplicate comments (1)
docs/conf.py (1)

333-335: Always emit a version badge; fall back to current release.

Avoid missing badges when a subcommand lacks an explicit entry.

Apply this diff:

-                version = SUBCOMMAND_VERSIONS.get(sub_cmd)
-                if version is not None:
-                    doc.write(f":badge-version:`{version}`\n\n    ")
+                version = SUBCOMMAND_VERSIONS.get(sub_cmd, release)
+                doc.write(f":badge-version:`{version}`\n\n    ")
🧹 Nitpick comments (1)
docs/conf.py (1)

331-338: Minor: iterate items to avoid double lookup.

Slightly cleaner and avoids repeated dict indexing.

Apply this diff:

-            for sub_cmd in choices:
+            for sub_cmd, sub_cmd_action in choices.items():
                 doc.write(f"\n.. std:option:: {sub_cmd}\n\n    ")
-                version = SUBCOMMAND_VERSIONS.get(sub_cmd)
+                version = SUBCOMMAND_VERSIONS.get(sub_cmd)
                 if version is not None:
                     doc.write(f":badge-version:`{version}`\n\n    ")
-                sub_cmd_action = choices[sub_cmd]
                 assert sub_cmd_action.description is not None
                 doc.write("\n    ".join(sub_cmd_action.description.splitlines()) + "\n")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between acaa788 and 968c894.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • docs/conf.py (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-09-04T23:14:04.822Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#155
File: docs/conf.py:292-292
Timestamp: 2025-09-04T23:14:04.822Z
Learning: Ruff S* (flake8-bandit) rules are not enabled by default in the cpp-linter project, so assert statements don't trigger S101 warnings. This is intentional because assert isinstance() is common practice for type checking.

Applied to files:

  • docs/conf.py
📚 Learning: 2025-09-04T23:06:44.559Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#155
File: docs/conf.py:284-299
Timestamp: 2025-09-04T23:06:44.559Z
Learning: In cpp-linter project, the maintainer 2bndy5 prefers using assert statements in documentation generation code (like docs/conf.py) rather than safe guards, as they want the doc generation to fail fast if expected conditions aren't met. This ensures complete CLI documentation is produced.

Applied to files:

  • docs/conf.py
📚 Learning: 2024-10-05T05:49:09.136Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#124
File: cpp_linter/clang_tools/clang_tidy.py:145-145
Timestamp: 2024-10-05T05:49:09.136Z
Learning: In the `get_suggestions_from_patch` method in `cpp_linter/clang_tools/clang_tidy.py`, it is acceptable to use `assert` statements for type checking when the assertion cannot evaluate as false due to `review_comments` data being passed by reference, and when it's intended exclusively for type checking tools. In such cases, writing unit tests to cover the assertion may be unnecessary.

Applied to files:

  • docs/conf.py
📚 Learning: 2025-05-31T03:53:32.334Z
Learnt from: 2bndy5
PR: cpp-linter/cpp-linter#143
File: noxfile.py:10-10
Timestamp: 2025-05-31T03:53:32.334Z
Learning: The cpp-linter project does not use pylint for linting because it's too slow. Instead, it uses `mypy` for type checking and `ruff` for linting, both listed as dev dependencies in pyproject.toml.

Applied to files:

  • docs/conf.py
🪛 Ruff (0.12.2)
docs/conf.py

292-292: Use of assert detected

(S101)


295-295: Use of assert detected

(S101)


337-337: Use of assert detected

(S101)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: pre-commit / run-pre-commit
  • GitHub Check: codeql / Analyze (python)
  • GitHub Check: auto-label
  • GitHub Check: test (windows-latest, 17)
  • GitHub Check: test (windows-latest, 21)
  • GitHub Check: test (windows-latest, 20)
  • GitHub Check: test (windows-latest, 19)
🔇 Additional comments (4)
docs/conf.py (4)

6-10: LGTM: argparse import and typing.cast usage are appropriate.

No concerns with introducing argparse and cast here.


141-143: LGTM: None-aware type hints for badge fields.

Consistent with Python 3.10+ union syntax, fits SphinxRole usage.


242-242: LGTM: SUBCOMMAND_VERSIONS entry for "version".

Mapping looks correct for the new subcommand.


284-298: LGTM: Capturing subparser action and skipping "command" positional.

Asserts are fine here per your stated fail-fast preference for docs/conf.py.

@2bndy5 2bndy5 merged commit 684fb08 into main Sep 5, 2025
40 checks passed
@2bndy5 2bndy5 deleted the generated-version-info branch September 5, 2025 01:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request minor A minor version bump

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants