Skip to content

Conversation

@shenxianpeng
Copy link
Contributor

@shenxianpeng shenxianpeng commented Sep 7, 2025

closes #275

Summary by CodeRabbit

  • Refactor
    • Standardized and clearer colored error reporting and validation across author, branch, merge-base, commit message, sign-off, and imperative checks.
    • Empty patterns or no-commit situations now gracefully pass with a warning.
    • Improved resilience when the commit message file is missing (fallback to available commit data).
  • Tests
    • Pytest configured with a "benchmark" marker to silence warnings; tests adjusted to match the centralized reporting.
  • Chores
    • No public API changes.

@shenxianpeng shenxianpeng requested a review from a team as a code owner September 7, 2025 19:20
@shenxianpeng shenxianpeng added the enhancement New feature or request label Sep 7, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 7, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Warning

Rate limit exceeded

@shenxianpeng has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 14 minutes and 28 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between bccd3dc and 8a4161e.

📒 Files selected for processing (1)
  • commit_check/util.py (2 hunks)

Walkthrough

Centralizes check lookup and failure reporting by adding commit_check.util._find_check and _print_failure; refactors author/branch/commit checks to use these helpers, adds commit-message helpers/fallbacks, updates tests to patch commit_check.util, and adds a pytest marker in pyproject.toml. Early-return behavior standardized.

Changes

Cohort / File(s) Summary
Utilities
commit_check/util.py
Added _find_check(checks, check_type) and _print_failure(check, regex, actual) to centralize check lookup and failure printing.
Author checks
commit_check/author.py
Added _AUTHOR_FORMAT_MAP and _get_author_value; switched to _find_check; use _print_failure for failures; streamlined control flow and early returns.
Branch & merge-base checks
commit_check/branch.py
Use _find_check for targeted lookups; centralized failure printing via _print_failure; preserved origin-prefix and merge-base logic; empty-regex and no-check cases now early-return PASS.
Commit message checks
commit_check/commit.py
Added _ensure_msg_file; read_commit_msg falls back to get_commit_info; check_commit_msg, check_commit_signoff, and check_imperative now use _find_check and _print_failure; preserved imperative detection and Merge commit skipping.
Tests updated
tests/author_test.py, tests/branch_test.py, tests/commit_test.py
Updated patch targets: mocks for print_error_message/print_suggestion now reference commit_check.util instead of module-local names; LOCATION constants adjusted accordingly.
Test config
pyproject.toml
Added [tool.pytest.ini_options] with a markers entry (adds benchmark marker) to silence PytestUnknownMarkWarning.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor Runner
  participant Checker as check_* functions
  participant Util as commit_check.util._find_check
  participant VCS as VCS/FS (git/get_commit_info)
  participant Printer as commit_check.util._print_failure

  Runner->>Checker: run(checks)
  Checker->>Util: _find_check(checks, type)
  alt not found or regex empty
    Checker-->>Runner: PASS
  else found
    Checker->>VCS: obtain actual value (branch/author/msg)
    alt matches regex
      Checker-->>Runner: PASS
    else mismatch
      Checker->>Printer: _print_failure(check, regex, actual)
      Checker-->>Runner: FAIL
    end
  end
Loading
sequenceDiagram
  autonumber
  actor Runner
  participant Commit as check_commit_msg
  participant FS as _ensure_msg_file / read_commit_msg
  participant Printer as commit_check.util._print_failure

  Runner->>Commit: run(checks)
  Commit->>FS: resolve and read commit message
  Commit->>Commit: re.match(regex, message)
  alt match
    Commit-->>Runner: PASS
  else no match
    Commit->>Printer: _print_failure(...)
    Commit-->>Runner: FAIL
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested labels

minor

Pre-merge checks (2 passed, 3 warnings)

❌ Failed Checks (3 warnings)
Check Name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The addition of a pytest marker in pyproject.toml for “benchmark” tests is unrelated to reducing cognitive complexity and doesn’t align with issue #275’s objectives, making it an out-of-scope configuration change. Please remove the pytest marker addition from this PR or move it to a separate configuration-focused PR so that this refactor remains focused solely on reducing code complexity per issue #275.
Description Check ⚠️ Warning The current description only includes “closes #275” and provides no context, rationale, or summary of the changes, making it difficult for reviewers to understand what was modified and why. Please add a detailed description of the refactoring work, including the key changes made to reduce cognitive complexity, and consider adding a pull request template to ensure future descriptions include all necessary information.
Docstring Coverage ⚠️ Warning Docstring coverage is 68.42% 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
Title Check ✅ Passed The title succinctly captures the main change—refactoring to reduce cognitive complexity—and aligns with the linked issue, making it clear and specific enough for reviewers.
Linked Issues Check ✅ Passed The refactoring introduces _find_check and _print_failure helpers to centralize repetitive logic and reduces branching in check functions across author, branch, and commit modules, directly addressing SonarCloud’s cognitive complexity concerns in issue #275.

Poem

I thump my paws at tidy loops,
Helpers hop in neat little groups.
One place to call a failure's name,
Branch, author, commit — all the same.
Tests rebounded, markers set — hooray! 🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/refactor-function

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.

@github-actions github-actions bot added the bug Something isn't working label Sep 7, 2025
@codecov
Copy link

codecov bot commented Sep 7, 2025

Codecov Report

❌ Patch coverage is 98.03922% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 99.13%. Comparing base (313d2ea) to head (8a4161e).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
commit_check/branch.py 96.29% 1 Missing ⚠️
commit_check/commit.py 97.82% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #276      +/-   ##
==========================================
- Coverage   99.14%   99.13%   -0.01%     
==========================================
  Files           8        8              
  Lines         350      346       -4     
==========================================
- Hits          347      343       -4     
  Misses          3        3              

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

@shenxianpeng shenxianpeng changed the title refactor: reduce #275 its cognitive complexity refactor: reduce #275 cognitive complexity Sep 7, 2025
@codspeed-hq
Copy link

codspeed-hq bot commented Sep 7, 2025

CodSpeed Performance Report

Merging #276 will improve performances by 58.16%

Comparing bugfix/refactor-function (8a4161e) with main (313d2ea)

Summary

⚡ 5 improvements
✅ 102 untouched benchmarks

Benchmarks breakdown

Benchmark BASE HEAD Change
test_check_commit_signoff_with_empty_checks 1,286.7 µs 957 µs +34.46%
test_check_commit_signoff_with_empty_regex 1,290.2 µs 969.5 µs +33.09%
test_check_commit_with_different_check 1,514.3 µs 959.6 µs +57.8%
test_check_commit_with_empty_checks 1,517.6 µs 959.5 µs +58.16%
test_check_commit_with_len0_regex 1.6 ms 1 ms +54.75%

Copy link
Contributor

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

🧹 Nitpick comments (5)
pyproject.toml (1)

71-75: Optional: pin pytest minimum version for pyproject-based config.

Small guard to avoid older pytest parsing differences.

Apply this diff:

 [tool.pytest.ini_options]
+# Ensure a modern pytest that fully supports pyproject config
+minversion = "7.0"
 # Silence PytestUnknownMarkWarning for custom marks used in tests
 markers = [
     "benchmark: performance-related tests (no-op marker in this project)",
 ]
commit_check/author.py (1)

28-31: Optional: assert known author check types.

Preempt accidental misuse and avoid calling git with an empty format string.

Apply this diff:

 def _get_author_value(check_type: str) -> str:
     """Fetch the author value from git for the given check type."""
-    format_str = _AUTHOR_FORMAT_MAP.get(check_type, "")
+    format_str = _AUTHOR_FORMAT_MAP.get(check_type, "")
+    if not format_str:  # defensive
+        return ""
     return str(get_commit_info(format_str))
commit_check/branch.py (2)

33-39: Optional: tolerate missing 'error'/'suggest' keys.

Use .get with defaults to avoid KeyError on partial configs.

Apply this diff:

-    print_error_message(
-        check['check'], regex,
-        check['error'], branch_name,
-    )
-    if check.get('suggest'):
-        print_suggestion(check['suggest'])
+    print_error_message(
+        check.get('check', 'branch'), regex,
+        check.get('error', ''), branch_name,
+    )
+    if check.get('suggest'):
+        print_suggestion(check['suggest'])

71-77: Optional: same defensive .get usage for merge_base.

Apply this diff:

-    print_error_message(
-        merge_check['check'], regex,
-        merge_check['error'], current_branch,
-    )
-    if merge_check.get('suggest'):
-        print_suggestion(merge_check['suggest'])
+    print_error_message(
+        merge_check.get('check', 'merge_base'), regex,
+        merge_check.get('error', ''), current_branch,
+    )
+    if merge_check.get('suggest'):
+        print_suggestion(merge_check['suggest'])
commit_check/commit.py (1)

30-36: Centralize _find_check to avoid duplication across modules

This duplicates commit_check/author.py::_find_check. Consider moving a single find_check helper to commit_check.util and importing it here and in other modules.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc169ba and 4e14363.

📒 Files selected for processing (4)
  • commit_check/author.py (1 hunks)
  • commit_check/branch.py (2 hunks)
  • commit_check/commit.py (1 hunks)
  • pyproject.toml (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-07T15:16:47.288Z
Learnt from: CR
PR: commit-check/commit-check#0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-09-07T15:16:47.288Z
Learning: Ensure author information in commits is valid: proper author name and email formats

Applied to files:

  • commit_check/author.py
🧬 Code graph analysis (3)
commit_check/author.py (2)
commit_check/util.py (5)
  • get_commit_info (48-67)
  • has_commits (33-46)
  • print_error_header (128-144)
  • print_error_message (147-159)
  • print_suggestion (162-173)
commit_check/main.py (1)
  • main (106-136)
commit_check/branch.py (2)
commit_check/util.py (5)
  • get_branch_name (15-30)
  • print_error_header (128-144)
  • print_error_message (147-159)
  • print_suggestion (162-173)
  • git_merge_base (70-84)
tests/branch_test.py (5)
  • TestCheckBranch (10-120)
  • test_check_merge_base_with_different_check (144-152)
  • TestCheckMergeBase (123-169)
  • test_check_branch_with_different_check (49-66)
  • test_check_merge_base_with_empty_regex (133-141)
commit_check/commit.py (4)
commit_check/author.py (1)
  • _find_check (20-25)
commit_check/util.py (5)
  • print_error_header (128-144)
  • print_error_message (147-159)
  • print_suggestion (162-173)
  • has_commits (33-46)
  • get_commit_info (48-67)
commit_check/main.py (1)
  • main (106-136)
tests/commit_test.py (2)
  • test_check_imperative_different_check_type (348-362)
  • test_check_imperative_empty_checks (382-393)
⏰ 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). (4)
  • GitHub Check: install (3.13, windows-latest)
  • GitHub Check: install (3.11, windows-latest)
  • GitHub Check: install (3.10, windows-latest)
  • GitHub Check: Run benchmarks
🔇 Additional comments (8)
pyproject.toml (1)

71-75: Registering the custom pytest marker looks good.

This silences PytestUnknownMarkWarning and documents the benchmark mark.

commit_check/commit.py (7)

38-43: LGTM: robust fallback for commit message file

Helper cleanly normalizes empty input to default path.


58-66: LGTM: early exit and clear skip messaging when regex missing

Flow is simple and avoids unnecessary I/O.


67-75: LGTM: standardized failure path via _print_failure

Good reuse and early return structure.


81-89: LGTM: signoff check retrieval and empty-regex skip

Matches the new pattern used across modules.


90-99: LGTM: merge commits correctly skipped by subject prefix

Simple and correct.


115-116: LGTM: no-commit fast path

Avoids running checks in fresh repos.


117-137: LGTM: imperative check flow and conventional-commit description extraction

The colon-split heuristic is clear and keeps the function concise.

@shenxianpeng shenxianpeng changed the title refactor: reduce #275 cognitive complexity refactor: reduce functions cognitive complexity Sep 8, 2025
@shenxianpeng shenxianpeng added this to the v1.0.0 release milestone Sep 8, 2025
Copy link
Contributor

@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

Caution

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

⚠️ Outside diff range comments (1)
pyproject.toml (1)

14-14: Bump Python requirement to ≥3.10

Code uses PEP 604 unions (| None) in commit_check/util.py (lines 15–16) and commit_check/commit.py (lines 13–14), which require Python 3.10+. Update pyproject.toml accordingly:

pyproject.toml
14c14
- requires-python = ">=3.9"
+ requires-python = ">=3.10"
@@
-    "Programming Language :: Python :: 3.9",
+    # remove or update this classifier to 3.10+ 
♻️ Duplicate comments (1)
commit_check/branch.py (1)

4-4: Python 3.9 compat: imported _find_check likely uses PEP 604 unions

util._find_check is annotated as dict | None in the repo snippets; this breaks on Python 3.9. Please switch to Optional[dict] in commit_check/util.py (and elsewhere) or drop the annotation.

Run to locate offenders:

#!/bin/bash
# Find PEP 604 unions that will fail on Python 3.9
rg -nP --type=py '\b[A-Za-z_][A-Za-z0-9_\[\],\s]*\|\s*None\b|->\s*[A-Za-z_][A-Za-z0-9_\[\],\s]*\|\s*None\b' -C2
🧹 Nitpick comments (7)
commit_check/author.py (1)

26-31: Harden failure printing: guard header flag and avoid KeyError

Use getattr to avoid AttributeError and .get() to avoid KeyError if config misses fields.

 def _print_failure(check: dict, regex: str, actual: str) -> None:
-    if not print_error_header.has_been_called:
-        print_error_header()
-    print_error_message(check['check'], regex, check['error'], actual)
+    if not getattr(print_error_header, "has_been_called", False):
+        print_error_header()
+    print_error_message(str(check.get('check', "")), regex, str(check.get('error', "")), actual)
     if check.get('suggest'):
         print_suggestion(check['suggest'])
commit_check/commit.py (2)

20-26: Unify failure handling: guard header flag and avoid KeyError

Mirror the robustness used elsewhere.

 def _print_failure(check: dict, regex: str, actual: str) -> None:
-    if not print_error_header.has_been_called:
-        print_error_header()  # pragma: no cover
-    print_error_message(check['check'], regex, check['error'], actual)
+    if not getattr(print_error_header, "has_been_called", False):
+        print_error_header()  # pragma: no cover
+    print_error_message(str(check.get('check', "")), regex, str(check.get('error', "")), actual)
     if check.get('suggest'):
         print_suggestion(check['suggest'])

90-95: Micro-opt: defer git hash lookup until failure

Avoids a subprocess on success path.

-    commit_hash = get_commit_info("H")
     if re.search(regex, commit_msg):
         return PASS
 
-    _print_failure(check, regex, commit_hash)
+    commit_hash = get_commit_info("H")
+    _print_failure(check, regex, commit_hash)
     return FAIL
commit_check/branch.py (4)

22-24: Polish the skip message

More concise and consistent wording.

-        print(
-            f"{YELLOW}Not found regex for branch naming. skip checking.{RESET_COLOR}",
-        )
+        print(f"{YELLOW}No branch regex configured; skipping check.{RESET_COLOR}")

28-29: Consider fullmatch for exact branch rule compliance

re.match allows partial matches unless the pattern is anchored; fullmatch enforces whole-string validation.

-    if re.match(regex, branch_name):
+    if re.fullmatch(regex, branch_name):
         return PASS

49-55: Polish the merge_base skip message

Clearer wording.

-    if regex == "":
-        print(
-            f"{YELLOW}Not found target branch for checking merge base. skip checking.{RESET_COLOR}",
-        )
+    if regex == "":
+        print(f"{YELLOW}No target branch configured for merge_base; skipping check.{RESET_COLOR}")
         return PASS

56-56: Avoid false positives when detecting existing origin/ prefix

Use startswith to prevent accidental matches where "origin/" appears elsewhere in the string.

-    target_branch = regex if "origin/" in regex else f"origin/{regex}"
+    target_branch = regex if regex.startswith("origin/") else f"origin/{regex}"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 313d2ea and 6d4fff6.

📒 Files selected for processing (5)
  • commit_check/author.py (1 hunks)
  • commit_check/branch.py (2 hunks)
  • commit_check/commit.py (2 hunks)
  • commit_check/util.py (1 hunks)
  • pyproject.toml (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (4)
commit_check/util.py (2)
tests/branch_test.py (4)
  • TestCheckBranch (10-120)
  • test_check_branch_with_empty_checks (32-46)
  • test_check_branch_with_different_check (49-66)
  • test_check_branch_with_result_none (93-120)
tests/commit_test.py (1)
  • test_check_commit_with_different_check (70-81)
commit_check/author.py (2)
commit_check/util.py (6)
  • get_commit_info (56-75)
  • has_commits (41-54)
  • print_error_header (136-152)
  • print_error_message (155-167)
  • print_suggestion (170-181)
  • _find_check (15-20)
tests/author_test.py (4)
  • test_check_author_with_different_check (73-90)
  • test_check_author_with_different_check (188-205)
  • test_check_author (16-33)
  • test_check_author (151-168)
commit_check/branch.py (4)
commit_check/util.py (7)
  • _find_check (15-20)
  • get_branch_name (23-38)
  • git_merge_base (78-92)
  • print_error_header (136-152)
  • print_error_message (155-167)
  • print_suggestion (170-181)
  • has_commits (41-54)
commit_check/author.py (1)
  • _print_failure (26-31)
commit_check/commit.py (1)
  • _print_failure (20-25)
tests/branch_test.py (4)
  • TestCheckBranch (10-120)
  • test_check_merge_base_with_different_check (144-152)
  • TestCheckMergeBase (123-169)
  • test_check_merge_base_with_empty_regex (133-141)
commit_check/commit.py (6)
noxfile.py (1)
  • commit_check (45-47)
commit_check/util.py (7)
  • _find_check (15-20)
  • cmd_output (95-109)
  • get_commit_info (56-75)
  • print_error_header (136-152)
  • print_error_message (155-167)
  • print_suggestion (170-181)
  • has_commits (41-54)
commit_check/author.py (1)
  • _print_failure (26-31)
commit_check/branch.py (1)
  • _print_failure (7-12)
commit_check/main.py (1)
  • main (106-136)
tests/commit_test.py (5)
  • test_check_commit_with_different_check (70-81)
  • test_check_imperative_skip_merge_commit (329-344)
  • test_check_imperative_empty_checks (382-393)
  • test_check_imperative_different_check_type (348-362)
  • test_check_commit_signoff_still_fails_non_merge_without_signoff (226-250)
⏰ 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). (9)
  • GitHub Check: install (3.13, windows-latest)
  • GitHub Check: install (3.12, windows-latest)
  • GitHub Check: install (3.13, ubuntu-24.04)
  • GitHub Check: install (3.10, windows-latest)
  • GitHub Check: install (3.11, windows-latest)
  • GitHub Check: install (3.10, macos-latest)
  • GitHub Check: install (3.9, windows-latest)
  • GitHub Check: install (3.9, macos-latest)
  • GitHub Check: Run benchmarks
🔇 Additional comments (5)
pyproject.toml (1)

71-75: Pytest marker config: LGTM

Silences PytestUnknownMarkWarning cleanly without side effects.

commit_check/author.py (1)

39-47: Early-return flow for empty regex: LGTM

Skips I/O and regex work when config lacks a pattern; message is clear.

commit_check/commit.py (2)

60-65: Message regex check path: LGTM

Single lookup + early return reduces branching and work done.


110-126: Imperative mood check refactor: LGTM

Clear skip conditions and focused subject parsing keep complexity low.

commit_check/branch.py (1)

16-19: Nice complexity reduction via early return

Using _find_check and bailing out early simplifies control flow.

@shenxianpeng shenxianpeng removed the bug Something isn't working label Sep 8, 2025
@github-actions github-actions bot added the bug Something isn't working label Sep 8, 2025
@shenxianpeng shenxianpeng removed the bug Something isn't working label Sep 8, 2025
@shenxianpeng shenxianpeng requested a review from Copilot September 8, 2025 23:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR refactors the commit-check codebase to reduce cognitive complexity by centralizing validation logic and improving code organization. The refactoring introduces helper functions to eliminate repetitive code patterns and standardizes error handling across different validation modules.

Key changes:

  • Centralized check finding and error printing logic in utility functions
  • Simplified validation functions by removing repetitive loops and conditional structures
  • Updated test imports to reflect relocated functions and standardized test markers

Reviewed Changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
commit_check/util.py Added centralized helper functions _find_check and _print_failure for standardized validation flow
commit_check/commit.py Refactored commit message, signoff, and imperative checks to use centralized helpers and reduce complexity
commit_check/branch.py Simplified branch and merge base validation by removing loops and using helper functions
commit_check/author.py Streamlined author validation with centralized logic and improved organization
tests/*.py Updated test mocks to reference relocated utility functions and added pytest marker configuration
pyproject.toml Added pytest configuration to silence warnings for custom test markers

@shenxianpeng
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

✅ Actions performed

Full review triggered.

Copy link
Contributor

@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 (3)
tests/commit_test.py (2)

31-35: Fix expectation: read_commit_msg should call util.get_commit_info when file is missing

read_commit_msg falls back to subject+body via util.get_commit_info; expecting zero calls is incorrect. Assert two calls and the composed result.

Apply this diff:

-    m_commits_info = mocker.patch('commit_check.util.get_commit_info', return_value='mocked_commits_info')
-    read_commit_msg("non_existent_file.txt")
-    assert m_commits_info.call_count == 0
+    m_commits_info = mocker.patch('commit_check.util.get_commit_info', return_value='mocked')
+    result = read_commit_msg("non_existent_file.txt")
+    assert m_commits_info.call_count == 2
+    # expect subject ("s") then body ("b")
+    from unittest import mock
+    m_commits_info.assert_has_calls([mock.call("s"), mock.call("b")])
+    assert result == "mocked\n\nmocked"

38-55: Stabilize tests: patch has_commits(True) where downstream behavior is asserted

These tests can short-circuit early if the repo has no commits, making them flaky. Explicitly patch has_commits to True within each test.

Apply patterns like this at the top of each affected test:

 def test_check_commit_msg_no_commit_msg_file(mocker):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)
 def test_check_commit_with_len0_regex(mocker, capfd):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)
 def test_check_commit_with_result_none(mocker):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)
 def test_check_commit_signoff(mocker):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)
 def test_check_commit_signoff_still_fails_non_merge_without_signoff(mocker):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)
 def test_check_imperative_fail_past_tense(mocker):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)
 def test_check_imperative_fail_present_continuous(mocker):
+    mocker.patch("commit_check.commit.has_commits", return_value=True)

Also applies to: 85-101, 103-126, 129-156, 226-251, 273-298, 301-326

tests/branch_test.py (1)

155-170: Fix ineffective patching: local import bypasses your mock and test may hit real git.

Because the test does from commit_check.branch import check_merge_base, patching f"{LOCATION}.check_merge_base" (Line 162) doesn’t affect the locally imported check_merge_base. Also, the function calls has_commits() and git_merge_base(), which will touch the environment unless mocked. Patch util dependencies instead.

Apply this diff within this test to mock the right call sites:

@@
-        mocker.patch(f"{LOCATION}.check_merge_base", return_value=1)
-        m_print_error = mocker.patch("commit_check.util.print_error_message")
-        m_print_suggest = mocker.patch("commit_check.util.print_suggestion")
+        mocker.patch("commit_check.util.has_commits", return_value=True)
+        mocker.patch("commit_check.util.get_branch_name", return_value="feature/x")
+        mocker.patch("commit_check.util.git_merge_base", return_value=1)
+        m_print_error = mocker.patch("commit_check.util.print_error_message")
+        m_print_suggest = mocker.patch("commit_check.util.print_suggestion")

Also fix the three earlier merge-base tests to assert no git calls instead of patching the function under test:

@@ def test_check_merge_base_with_empty_checks(self, mocker):
-        m_check_merge = mocker.patch(f"{LOCATION}.check_merge_base")
+        mocker.patch("commit_check.util.has_commits", return_value=True)
+        m_git_merge_base = mocker.patch("commit_check.util.git_merge_base")
@@
-        assert m_check_merge.call_count == 0
+        assert m_git_merge_base.call_count == 0

@@ def test_check_merge_base_with_empty_regex(self, mocker):
-        m_check_merge = mocker.patch(f"{LOCATION}.check_merge_base")
+        mocker.patch("commit_check.util.has_commits", return_value=True)
+        m_git_merge_base = mocker.patch("commit_check.util.git_merge_base")
@@
-        assert m_check_merge.call_count == 0
+        assert m_git_merge_base.call_count == 0

@@ def test_check_merge_base_with_different_check(self, mocker):
-        m_check_merge = mocker.patch(f"{LOCATION}.check_merge_base")
+        mocker.patch("commit_check.util.has_commits", return_value=True)
+        m_git_merge_base = mocker.patch("commit_check.util.git_merge_base")
@@
-        assert m_check_merge.call_count == 0
+        assert m_git_merge_base.call_count == 0

Optional: add capfd assertion for the empty-regex warning to mirror branch tests.

♻️ Duplicate comments (2)
commit_check/commit.py (2)

13-18: Replace PEP 604 str | None with Optional[str] or bump supported Python

Project metadata previously targeted Python >=3.9; X | None requires 3.10+. Use Optional[str] to retain 3.9 support, or update requires-python to >=3.10.

+from typing import Optional
@@
-def _ensure_msg_file(commit_msg_file: str | None) -> str:
+def _ensure_msg_file(commit_msg_file: Optional[str]) -> str:
#!/bin/bash
# Confirm declared Python version to decide between Optional vs. 3.10+
rg -n "requires-python|Programming Language :: Python ::" -S pyproject.toml || true

55-57: Guard header gating inside util._print_failure to avoid AttributeError

current implementation in util reads print_error_header.has_been_called directly, which can crash on first call. Use getattr/setattr there.

Apply in commit_check/util.py (for reference):

-    if not print_error_header.has_been_called:
-        print_error_header()
+    if not getattr(print_error_header, "has_been_called", False):
+        print_error_header()  # pragma: no cover
+        setattr(print_error_header, "has_been_called", True)

Also applies to: 86-87, 116-118

🧹 Nitpick comments (8)
tests/commit_test.py (1)

52-55: Use PASS constant instead of magic number

Avoid asserting literal 0; assert against PASS for clarity and resilience.

-    assert result == 0
+    assert result == PASS
tests/author_test.py (1)

260-261: Remove duplicate assertion

The second identical assert is redundant.

-            assert m_print_suggestion.call_count == 1
commit_check/commit.py (3)

26-34: Tighten typing and drop unnecessary str()

Add a type for commit_msg_file; no need to wrap concatenated string with str().

-def read_commit_msg(commit_msg_file) -> str:
+def read_commit_msg(commit_msg_file: str) -> str:
@@
-    except FileNotFoundError:
-        # Commit message is composed by subject and body
-        return str(get_commit_info("s") + "\n\n" + get_commit_info("b"))
+    except FileNotFoundError:
+        # Fallback to subject + body when file is absent
+        return get_commit_info("s") + "\n\n" + get_commit_info("b")

35-57: Reduce noise: show subject in failure instead of full message

Printing the entire message can be verbose. Passing the subject keeps output focused.

-    if re.match(regex, commit_msg):
+    if re.match(regex, commit_msg):
         return PASS
-
-    _print_failure(check, regex, commit_msg)
+
+    subject = commit_msg.split('\n')[0].strip()
+    _print_failure(check, regex, subject)
     return FAIL

82-87: Avoid unnecessary git calls on success path

Only fetch commit hash if the regex fails.

-    commit_hash = get_commit_info("H")
-    if re.search(regex, commit_msg):
-        return PASS
-
-    _print_failure(check, regex, commit_hash)
-    return FAIL
+    if re.search(regex, commit_msg):
+        return PASS
+
+    commit_hash = get_commit_info("H")
+    _print_failure(check, regex, commit_hash)
+    return FAIL
commit_check/branch.py (3)

20-21: Prefer fullmatch for branch naming rules.

re.match allows partial prefixes; most naming policies expect the whole branch to conform.

-    if re.match(regex, branch_name):
+    if re.fullmatch(regex, branch_name):
         return PASS

42-47: Consistent warning mechanism.

Consider routing warnings through a shared helper (e.g., util.print_warning(...)) to keep messaging uniform across modules and simplify testing.


48-49: Nit: use startswith for remote prefix.

"origin/" in regex can misfire on exotic patterns; regex.startswith("origin/") is stricter.

-    target_branch = regex if "origin/" in regex else f"origin/{regex}"
+    target_branch = regex if regex.startswith("origin/") else f"origin/{regex}"
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6d4fff6 and bfb2edb.

📒 Files selected for processing (7)
  • commit_check/author.py (1 hunks)
  • commit_check/branch.py (2 hunks)
  • commit_check/commit.py (2 hunks)
  • commit_check/util.py (1 hunks)
  • tests/author_test.py (2 hunks)
  • tests/branch_test.py (2 hunks)
  • tests/commit_test.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • commit_check/util.py
  • commit_check/author.py
🧰 Additional context used
🧬 Code graph analysis (3)
tests/commit_test.py (1)
tests/util_test.py (2)
  • TestUtil (16-323)
  • TestGetCommitInfo (116-180)
commit_check/branch.py (2)
noxfile.py (1)
  • commit_check (45-47)
commit_check/util.py (5)
  • _find_check (15-20)
  • _print_failure (23-33)
  • get_branch_name (36-51)
  • git_merge_base (91-105)
  • has_commits (54-67)
commit_check/commit.py (3)
noxfile.py (1)
  • commit_check (45-47)
commit_check/util.py (5)
  • _find_check (15-20)
  • _print_failure (23-33)
  • cmd_output (108-122)
  • get_commit_info (69-88)
  • has_commits (54-67)
commit_check/main.py (1)
  • main (106-136)
⏰ 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). (7)
  • GitHub Check: install (3.12, windows-latest)
  • GitHub Check: install (3.13, windows-latest)
  • GitHub Check: install (3.11, windows-latest)
  • GitHub Check: install (3.10, macos-latest)
  • GitHub Check: install (3.10, windows-latest)
  • GitHub Check: install (3.9, macos-latest)
  • GitHub Check: Run benchmarks
🔇 Additional comments (5)
tests/commit_test.py (1)

8-8: LGTM: patching util-level printers

Switching LOCATION to "commit_check.util" aligns with the refactor.

tests/author_test.py (2)

134-138: LGTM: patch print_error_message/print_suggestion at util-level

Targets match the refactor centralizing output.


249-253: LGTM: consistent util-level patches here as well

Matches the new design.

tests/branch_test.py (1)

110-114: Mocks redirected to util — correct alignment.

Patching commit_check.util.print_error_message and print_suggestion matches the centralization. LGTM.

commit_check/branch.py (1)

8-24: Early-return refactor reads well and reduces complexity.

Centralized lookup/printing and short-circuiting paths look good.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@github-actions github-actions bot added the bug Something isn't working label Sep 8, 2025
@sonarqubecloud
Copy link

sonarqubecloud bot commented Sep 8, 2025

@shenxianpeng shenxianpeng added minor A minor version bump and removed bug Something isn't working labels Sep 9, 2025
@shenxianpeng shenxianpeng merged commit 8991ac1 into main Sep 9, 2025
27 checks passed
@shenxianpeng shenxianpeng deleted the bugfix/refactor-function branch September 9, 2025 00:01
@coderabbitai coderabbitai bot mentioned this pull request Oct 6, 2025
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.

Refactor functions to reduce its Cognitive Complexity

2 participants