Skip to content

Feature/147 split mining logic #148

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Jun 12, 2025
Merged

Conversation

OlivieFranklova
Copy link
Collaborator

@OlivieFranklova OlivieFranklova commented Jun 10, 2025

Release Notes:

  • new dataclass MinedData
  • move mining logic from generator

Closes #147

Summary by CodeRabbit

  • New Features

    • Introduced a new data mining module to efficiently gather GitHub repository data for release notes generation.
    • Added a structured data model to store mined GitHub data, including issues, pull requests, commits, and releases.
  • Refactor

    • Streamlined the release notes generator by delegating all GitHub data retrieval and filtering to the new data mining module.
  • Tests

    • Added comprehensive unit tests for the new data mining functionality, covering release retrieval and error handling.
    • Updated existing tests to align with the refactored architecture and new data mining approach.

@OlivieFranklova OlivieFranklova linked an issue Jun 10, 2025 that may be closed by this pull request
@OlivieFranklova OlivieFranklova self-assigned this Jun 10, 2025
Copy link

coderabbitai bot commented Jun 10, 2025

Caution

Review failed

The pull request is closed.

"""

Walkthrough

The mining logic previously embedded in the release notes generator was extracted into a new DataMiner class, now responsible for all GitHub data retrieval. The generator delegates data mining to this class and then filters results. Supporting data structures and tests were added or updated to reflect this separation, with obsolete methods and related tests removed.

Changes

File(s) Change Summary
release_notes_generator/generator.py Refactored to remove direct GitHub API calls and manual filtering; delegates mining to DataMiner.
release_notes_generator/miner.py New module introducing DataMiner class for mining GitHub data, with robust error handling.
release_notes_generator/model/MinedData.py New module defining MinedData data class for holding mined GitHub repository data.
tests/test_release_notes_generator.py Removed tests for generator’s mining logic; updated mocks to target DataMiner methods.
tests/test_miner.py New test module with comprehensive unit tests for DataMiner mining and release retrieval logic.

Sequence Diagram(s)

sequenceDiagram
    participant Generator as ReleaseNotesGenerator
    participant Miner as DataMiner
    participant GitHub as GitHub API

    Generator->>Miner: mine_data()
    Miner->>GitHub: Fetch repository, releases, issues, PRs, commits
    Miner-->>Generator: MinedData
    Generator->>Generator: _filter_by_release(MinedData)
    Generator->>Generator: Generate release notes from filtered data
Loading

Assessment against linked issues

Objective (Issue #) Addressed Explanation
Extract mining logic into a separate class (#147)

Assessment against linked issues: Out-of-scope changes

No out-of-scope changes found.

Possibly related PRs

Suggested reviewers

  • Zejnilovic
  • benedeki

Poem

In tunnels deep, I mined with glee,
Extracting logic, now code runs free!
The DataMiner hops in place,
Fetching facts with nimble grace.
Release notes bloom, so neat and bright—
A rabbit’s work, just done right! 🐇✨
"""


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 103f98e and cc13a58.

📒 Files selected for processing (3)
  • release_notes_generator/generator.py (3 hunks)
  • release_notes_generator/miner.py (1 hunks)
  • tests/test_miner.py (1 hunks)
✨ Finishing Touches
  • 📝 Generate Docstrings

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.
    • Explain this complex logic.
    • 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. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

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

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary or {PR 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

Documentation and Community

  • 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

🔭 Outside diff range comments (1)
tests/tests_miner.py (1)

36-193: 🛠️ Refactor suggestion

Tests are directly accessing private methods.

All tests in this file are testing the private method _get_latest_release directly, which breaks encapsulation. Consider either:

  1. Making the method public if it's intended to be part of the public API
  2. Testing it indirectly through the public mine_data method

Would you like me to help refactor these tests to use the public interface instead?

🧰 Tools
🪛 Ruff (0.11.9)

60-60: Yoda condition detected

(SIM300)


61-61: Yoda condition detected

(SIM300)


62-62: Yoda condition detected

(SIM300)


63-63: Yoda condition detected

(SIM300)


89-89: Yoda condition detected

(SIM300)


90-90: Yoda condition detected

(SIM300)


91-91: Yoda condition detected

(SIM300)


92-92: Yoda condition detected

(SIM300)


114-114: Yoda condition detected

(SIM300)


136-136: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


137-137: Yoda condition detected

(SIM300)


138-138: Yoda condition detected

(SIM300)


164-164: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 1

(SIM300)


165-165: Yoda condition detected

(SIM300)


190-190: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


191-191: Yoda condition detected

(SIM300)


192-192: Yoda condition detected

(SIM300)

🧹 Nitpick comments (4)
release_notes_generator/model/MinedData.py (2)

54-57: Simplify the return statement.

The method can be simplified by directly returning the condition.

 def is_empty(self):
-    if self.repository is None:
-        return True
-    return False
+    return self.repository is None
🧰 Tools
🪛 Ruff (0.11.9)

55-57: Return the condition self.repository is None directly

Replace with return self.repository is None

(SIM103)


62-62: Use idiomatic None comparison.

Use is not for None comparison as per Python conventions.

-        self.release = rls_mock if rls_mock != None else mocker.Mock(spec=GitRelease)
+        self.release = rls_mock if rls_mock is not None else mocker.Mock(spec=GitRelease)
🧰 Tools
🪛 Ruff (0.11.9)

62-62: Comparison to None should be cond is not None

Replace with cond is not None

(E711)

tests/tests_miner.py (2)

17-28: Remove unused imports.

Static analysis identified several unused imports that should be removed.

-import time
-from datetime import datetime, timedelta
-from unittest.mock import MagicMock
-
 from github import Github
 from github.GitRelease import GitRelease

 from release_notes_generator.miner import DataMiner
 from release_notes_generator.model.MinedData import MinedData
-from release_notes_generator.generator import ReleaseNotesGenerator
-from release_notes_generator.model.custom_chapters import CustomChapters
-from release_notes_generator.utils.constants import ROW_FORMAT_ISSUE
🧰 Tools
🪛 Ruff (0.11.9)

17-17: time imported but unused

Remove unused import: time

(F401)


18-18: datetime.datetime imported but unused

Remove unused import

(F401)


18-18: datetime.timedelta imported but unused

Remove unused import

(F401)


19-19: unittest.mock.MagicMock imported but unused

Remove unused import: unittest.mock.MagicMock

(F401)


26-26: release_notes_generator.generator.ReleaseNotesGenerator imported but unused

Remove unused import: release_notes_generator.generator.ReleaseNotesGenerator

(F401)


27-27: release_notes_generator.model.custom_chapters.CustomChapters imported but unused

Remove unused import: release_notes_generator.model.custom_chapters.CustomChapters

(F401)


28-28: release_notes_generator.utils.constants.ROW_FORMAT_ISSUE imported but unused

Remove unused import: release_notes_generator.utils.constants.ROW_FORMAT_ISSUE

(F401)


60-63: Consider using standard assertion order for better readability.

The Yoda-style assertions (expected == actual) are less readable than the standard order (actual == expected).

For example:

-    assert 2 == len(mock_log_info.call_args_list)
+    assert len(mock_log_info.call_args_list) == 2

This makes the assertions more natural to read: "assert that the length of call args list equals 2" rather than "assert that 2 equals the length of call args list".

Also applies to: 89-92, 114-114, 136-138, 164-165, 190-192

🧰 Tools
🪛 Ruff (0.11.9)

60-60: Yoda condition detected

(SIM300)


61-61: Yoda condition detected

(SIM300)


62-62: Yoda condition detected

(SIM300)


63-63: Yoda condition detected

(SIM300)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb35bf1 and 63f130b.

📒 Files selected for processing (5)
  • release_notes_generator/generator.py (3 hunks)
  • release_notes_generator/miner.py (1 hunks)
  • release_notes_generator/model/MinedData.py (1 hunks)
  • tests/test_release_notes_generator.py (3 hunks)
  • tests/tests_miner.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
tests/test_release_notes_generator.py (1)
tests/conftest.py (1)
  • mock_git_release (86-89)
release_notes_generator/miner.py (4)
release_notes_generator/action_inputs.py (5)
  • ActionInputs (60-411)
  • get_github_repository (70-74)
  • is_from_tag_name_defined (98-103)
  • get_from_tag_name (91-95)
  • get_published_at (148-153)
release_notes_generator/model/MinedData.py (1)
  • MinedData (37-76)
release_notes_generator/utils/decorators.py (1)
  • safe_call_decorator (51-82)
release_notes_generator/utils/github_rate_limiter.py (1)
  • GithubRateLimiter (31-75)
release_notes_generator/model/MinedData.py (1)
tests/conftest.py (1)
  • mock_repo (78-81)
🪛 Pylint (3.3.7)
release_notes_generator/miner.py

[refactor] 37-37: Too few public methods (1/2)

(R0903)

🪛 Ruff (0.11.9)
release_notes_generator/model/MinedData.py

55-57: Return the condition self.repository is None directly

Replace with return self.repository is None

(SIM103)


62-62: Comparison to None should be cond is not None

Replace with cond is not None

(E711)

tests/tests_miner.py

17-17: time imported but unused

Remove unused import: time

(F401)


18-18: datetime.datetime imported but unused

Remove unused import

(F401)


18-18: datetime.timedelta imported but unused

Remove unused import

(F401)


19-19: unittest.mock.MagicMock imported but unused

Remove unused import: unittest.mock.MagicMock

(F401)


26-26: release_notes_generator.generator.ReleaseNotesGenerator imported but unused

Remove unused import: release_notes_generator.generator.ReleaseNotesGenerator

(F401)


27-27: release_notes_generator.model.custom_chapters.CustomChapters imported but unused

Remove unused import: release_notes_generator.model.custom_chapters.CustomChapters

(F401)


28-28: release_notes_generator.utils.constants.ROW_FORMAT_ISSUE imported but unused

Remove unused import: release_notes_generator.utils.constants.ROW_FORMAT_ISSUE

(F401)


60-60: Yoda condition detected

(SIM300)


61-61: Yoda condition detected

(SIM300)


62-62: Yoda condition detected

(SIM300)


63-63: Yoda condition detected

(SIM300)


89-89: Yoda condition detected

(SIM300)


90-90: Yoda condition detected

(SIM300)


91-91: Yoda condition detected

(SIM300)


92-92: Yoda condition detected

(SIM300)


114-114: Yoda condition detected

(SIM300)


136-136: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


137-137: Yoda condition detected

(SIM300)


138-138: Yoda condition detected

(SIM300)


164-164: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 1

(SIM300)


165-165: Yoda condition detected

(SIM300)


190-190: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


191-191: Yoda condition detected

(SIM300)


192-192: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Mypy Type Check
  • GitHub Check: Black Format Check
🔇 Additional comments (2)
tests/test_release_notes_generator.py (1)

68-68: LGTM! Test mocks correctly updated.

The mock patches have been properly updated to reflect the architectural change where release retrieval logic moved from ReleaseNotesGenerator to DataMiner.

Also applies to: 111-111, 161-161

release_notes_generator/generator.py (1)

28-28: Excellent refactoring - separation of concerns well implemented.

The generator now properly delegates data mining responsibilities to DataMiner and focuses solely on release notes generation. The use of the MinedData object provides a clean interface between components.

Also applies to: 49-49, 74-103

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)
release_notes_generator/miner.py (1)

125-125: Add None check for safer logging.

This matches a previous review comment about adding a None check before accessing length to prevent potential AttributeError.

🧹 Nitpick comments (4)
release_notes_generator/miner.py (1)

67-73: Update docstring parameter reference.

The docstring references @param repo but the actual parameter is data: MinedData.

-        @param repo: The repository to get the latest release from.
+        @param data: The MinedData object containing the repository to get the latest release from.
tests/tests_miner.py (3)

44-44: Fix None comparison to use identity check.

Use is not None instead of != None for None comparisons.

-        self.release = rls_mock if rls_mock != None else mocker.Mock(spec=GitRelease)
+        self.release = rls_mock if rls_mock is not None else mocker.Mock(spec=GitRelease)
🧰 Tools
🪛 Ruff (0.11.9)

44-44: Comparison to None should be cond is not None

Replace with cond is not None

(E711)


86-89: Fix Yoda conditions for better readability.

The assertions use Yoda conditions (literal == variable) which are less readable than normal conditions.

-    assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for %s. 1st release for repository!', 'org/repo') == mock_log_info.call_args_list[1][0]
-    assert ('Skipping invalid type of version tag: %s', 'v1.0.0') == mock_log_debug.call_args_list[0][0]
-    assert ('Skipping invalid type of version tag: %s', 'v2.0.0') == mock_log_debug.call_args_list[1][0]
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for %s. 1st release for repository!', 'org/repo')
+    assert mock_log_debug.call_args_list[0][0] == ('Skipping invalid type of version tag: %s', 'v1.0.0')
+    assert mock_log_debug.call_args_list[1][0] == ('Skipping invalid type of version tag: %s', 'v2.0.0')

Also applies to: 115-118

🧰 Tools
🪛 Ruff (0.11.9)

86-86: Yoda condition detected

(SIM300)


87-87: Yoda condition detected

(SIM300)


88-88: Yoda condition detected

(SIM300)


89-89: Yoda condition detected

(SIM300)


162-164: Fix remaining Yoda conditions for consistency.

Apply the same Yoda condition fixes to the remaining assertions for consistency.

-    assert 2 == len(mock_log_info.call_args_list)
-    assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for %s. 1st release for repository!', 'org/repo') == mock_log_info.call_args_list[1][0]
+    assert len(mock_log_info.call_args_list) == 2
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for %s. 1st release for repository!', 'org/repo')
-    assert 1 == len(mock_log_info.call_args_list)
-    assert ('Getting latest release by from-tag name %s', "") == mock_log_info.call_args_list[0][0]
+    assert len(mock_log_info.call_args_list) == 1
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by from-tag name %s', "")
-    assert 2 == len(mock_log_info.call_args_list)
-    assert ('Getting latest release by from-tag name %s', "") == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for received from-tag %s. Ending!', '') == mock_log_info.call_args_list[1][0]
+    assert len(mock_log_info.call_args_list) == 2
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by from-tag name %s', "")
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for received from-tag %s. Ending!', '')

Also applies to: 190-191, 216-218

🧰 Tools
🪛 Ruff (0.11.9)

162-162: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


163-163: Yoda condition detected

(SIM300)


164-164: Yoda condition detected

(SIM300)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 63f130b and 856333f.

📒 Files selected for processing (5)
  • release_notes_generator/generator.py (3 hunks)
  • release_notes_generator/miner.py (1 hunks)
  • release_notes_generator/model/MinedData.py (1 hunks)
  • tests/test_release_notes_generator.py (3 hunks)
  • tests/tests_miner.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • release_notes_generator/model/MinedData.py
🧰 Additional context used
🧬 Code Graph Analysis (2)
tests/test_release_notes_generator.py (1)
tests/conftest.py (1)
  • mock_git_release (86-89)
release_notes_generator/generator.py (5)
release_notes_generator/miner.py (2)
  • DataMiner (38-148)
  • mine_data (47-65)
release_notes_generator/utils/github_rate_limiter.py (1)
  • GithubRateLimiter (31-75)
release_notes_generator/model/MinedData.py (1)
  • is_empty (49-50)
release_notes_generator/utils/utils.py (1)
  • get_change_url (31-56)
release_notes_generator/record/record_factory.py (2)
  • RecordFactory (40-142)
  • generate (46-142)
🪛 Ruff (0.11.9)
tests/tests_miner.py

44-44: Comparison to None should be cond is not None

Replace with cond is not None

(E711)


86-86: Yoda condition detected

(SIM300)


87-87: Yoda condition detected

(SIM300)


88-88: Yoda condition detected

(SIM300)


89-89: Yoda condition detected

(SIM300)


115-115: Yoda condition detected

(SIM300)


116-116: Yoda condition detected

(SIM300)


117-117: Yoda condition detected

(SIM300)


118-118: Yoda condition detected

(SIM300)


140-140: Yoda condition detected

(SIM300)


162-162: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


163-163: Yoda condition detected

(SIM300)


164-164: Yoda condition detected

(SIM300)


190-190: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 1

(SIM300)


191-191: Yoda condition detected

(SIM300)


216-216: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


217-217: Yoda condition detected

(SIM300)


218-218: Yoda condition detected

(SIM300)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: Black Format Check
  • GitHub Check: Mypy Type Check
🔇 Additional comments (9)
tests/test_release_notes_generator.py (1)

68-68: LGTM! Mock targets correctly updated for DataMiner refactoring.

The patch targets have been properly updated to reflect the migration of get_latest_release method from ReleaseNotesGenerator to DataMiner. This aligns perfectly with the architectural changes.

Also applies to: 111-111, 161-161

release_notes_generator/generator.py (5)

28-28: LGTM! Clean import of the new DataMiner component.


49-49: LGTM! Rate limiter properly instantiated in constructor.

Good practice to create the rate limiter once in the constructor rather than on each method call.


73-76: LGTM! Clean delegation to DataMiner with proper error handling.

The data mining is now properly delegated to DataMiner, and the empty data check ensures graceful handling when repository is not found.


77-97: LGTM! Well-preserved filtering logic with improved data access.

The filtering logic has been correctly adapted to work with the pre-mined data structure. The logic for filtering issues, PRs, and commits based on release dates remains intact while benefiting from the cleaner data structure.


98-110: LGTM! Clean integration with the new data structure.

The changelog URL generation and record factory calls have been properly updated to use the mined data, maintaining the same functionality with improved organization.

release_notes_generator/miner.py (2)

82-87: LGTM! Appropriate error handling for missing required tag.

Using sys.exit(1) is appropriate for a CLI tool when a required tag is not found, as this represents a user configuration error that should terminate the process.


127-148: LGTM! Robust semantic version parsing with proper error handling.

The semantic version parsing correctly:

  • Filters out drafts and pre-releases
  • Handles ValueError and TypeError exceptions gracefully
  • Strips 'v' prefix from tag names
  • Uses proper version comparison
  • Includes helpful debug logging for skipped tags
tests/tests_miner.py (1)

62-219: LGTM! Comprehensive test coverage for DataMiner functionality.

The test suite provides excellent coverage of the get_latest_release method, including:

  • Tag-defined release scenarios (found/not found)
  • Semantic version sorting scenarios
  • Error handling for invalid version tags (TypeError/ValueError)
  • Proper logging verification
  • Appropriate mocking of dependencies

The tests will help ensure the reliability of the extracted mining logic.

🧰 Tools
🪛 Ruff (0.11.9)

86-86: Yoda condition detected

(SIM300)


87-87: Yoda condition detected

(SIM300)


88-88: Yoda condition detected

(SIM300)


89-89: Yoda condition detected

(SIM300)


115-115: Yoda condition detected

(SIM300)


116-116: Yoda condition detected

(SIM300)


117-117: Yoda condition detected

(SIM300)


118-118: Yoda condition detected

(SIM300)


140-140: Yoda condition detected

(SIM300)


162-162: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


163-163: Yoda condition detected

(SIM300)


164-164: Yoda condition detected

(SIM300)


190-190: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 1

(SIM300)


191-191: Yoda condition detected

(SIM300)


216-216: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


217-217: Yoda condition detected

(SIM300)


218-218: Yoda condition detected

(SIM300)

@OlivieFranklova OlivieFranklova requested a review from benedeki June 10, 2025 10:16
Copy link
Collaborator

@miroslavpojer miroslavpojer left a comment

Choose a reason for hiding this comment

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

  • pulled
  • code reviewed

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

♻️ Duplicate comments (1)
release_notes_generator/miner.py (1)

127-127: ⚠️ Potential issue

Add None check before accessing length.

If self._safe_call returns None due to an error, this line will raise an AttributeError.

-logger.info("Fetched %d issues", len(data.issues))
+logger.info("Fetched %d issues", len(data.issues) if data.issues else 0)
🧹 Nitpick comments (2)
release_notes_generator/miner.py (1)

49-67: Consider consistent data handling for all collections.

Commits are converted to a list immediately (line 64) while issues and pull_requests remain as PaginatedList objects. This inconsistency could lead to unexpected behavior.

Consider either converting all to lists immediately or keeping all as PaginatedList objects until needed:

 # pulls and commits, and then reduce them by the latest release since time
 data.pull_requests = self._safe_call(data.repository.get_pulls)(state=PR_STATE_CLOSED)
-data.commits = list(self._safe_call(data.repository.get_commits)())
+data.commits = self._safe_call(data.repository.get_commits)()
tests/test_miner.py (1)

81-84: Fix Yoda conditions for better readability.

Replace Yoda conditions with standard Python comparison style for improved readability.

-assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
-assert ('Latest release not found for %s. 1st release for repository!', 'org/repo') == mock_log_info.call_args_list[1][0]
-assert ('Skipping invalid type of version tag: %s | Error: %s', 'v1.0.0', '') == mock_log_error.call_args_list[0][0]
-assert ('Skipping invalid type of version tag: %s | Error: %s', 'v2.0.0', '') == mock_log_error.call_args_list[2][0]
+assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)
+assert mock_log_info.call_args_list[1][0] == ('Latest release not found for %s. 1st release for repository!', 'org/repo')
+assert mock_log_error.call_args_list[0][0] == ('Skipping invalid type of version tag: %s | Error: %s', 'v1.0.0', '')
+assert mock_log_error.call_args_list[2][0] == ('Skipping invalid type of version tag: %s | Error: %s', 'v2.0.0', '')

Apply the same pattern to all other Yoda conditions in the file:

  • Line 157: assert len(mock_log_info.call_args_list) == 2
  • Line 184: assert len(mock_log_info.call_args_list) == 1
  • Line 210: assert len(mock_log_info.call_args_list) == 2

Also applies to: 110-113, 135-135, 157-159, 184-185, 210-212

🧰 Tools
🪛 Ruff (0.11.9)

81-81: Yoda condition detected

(SIM300)


82-82: Yoda condition detected

(SIM300)


83-83: Yoda condition detected

(SIM300)


84-84: Yoda condition detected

(SIM300)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 856333f and 9107937.

📒 Files selected for processing (3)
  • release_notes_generator/generator.py (3 hunks)
  • release_notes_generator/miner.py (1 hunks)
  • tests/test_miner.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
release_notes_generator/generator.py (5)
release_notes_generator/miner.py (2)
  • DataMiner (40-151)
  • mine_data (49-67)
release_notes_generator/utils/github_rate_limiter.py (1)
  • GithubRateLimiter (31-75)
release_notes_generator/model/MinedData.py (1)
  • is_empty (49-50)
release_notes_generator/utils/utils.py (1)
  • get_change_url (31-56)
release_notes_generator/record/record_factory.py (2)
  • RecordFactory (40-142)
  • generate (46-142)
🪛 Ruff (0.11.9)
tests/test_miner.py

81-81: Yoda condition detected

(SIM300)


82-82: Yoda condition detected

(SIM300)


83-83: Yoda condition detected

(SIM300)


84-84: Yoda condition detected

(SIM300)


110-110: Yoda condition detected

(SIM300)


111-111: Yoda condition detected

(SIM300)


112-112: Yoda condition detected

(SIM300)


113-113: Yoda condition detected

(SIM300)


135-135: Yoda condition detected

(SIM300)


157-157: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


158-158: Yoda condition detected

(SIM300)


159-159: Yoda condition detected

(SIM300)


184-184: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 1

(SIM300)


185-185: Yoda condition detected

(SIM300)


210-210: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


211-211: Yoda condition detected

(SIM300)


212-212: Yoda condition detected

(SIM300)

🔇 Additional comments (7)
release_notes_generator/generator.py (4)

28-28: Import statement looks good.

The import follows the project's structure and naming conventions.


49-49: Good refactoring to instantiate rate limiter once.

Creating the rate limiter in the constructor and reusing it is more efficient than creating it multiple times.


73-77: DataMiner integration is well implemented.

The error handling using data.is_empty() properly checks if the repository was found before proceeding.


105-111: RecordFactory call is properly implemented.

The explicit conversion to lists ensures RecordFactory receives proper list objects instead of PaginatedList objects.

release_notes_generator/miner.py (2)

40-47: Well-structured class initialization.

The DataMiner class is properly initialized with dependency injection for both GitHub instance and rate limiter.


129-151: Robust semantic version handling.

The method properly filters releases and handles version parsing errors with appropriate logging.

tests/test_miner.py (1)

30-57: Well-designed test helpers.

The decorator mock and MinedDataMock provide good test isolation and make tests more maintainable.

Copy link
Collaborator

@miroslavpojer miroslavpojer left a comment

Choose a reason for hiding this comment

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

  • pulled
  • code review

See new comments and AI ones, pls.

@OlivieFranklova OlivieFranklova linked an issue Jun 11, 2025 that may be closed by this pull request
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)
release_notes_generator/miner.py (1)

126-126: Add None check before accessing length.

This issue was previously raised and is still valid. If self._safe_call returns None due to an error, this line will raise an AttributeError.

Apply this fix:

-        logger.info("Fetched %d issues", len(data.issues))
+        logger.info("Fetched %d issues", len(data.issues) if data.issues else 0)
🧹 Nitpick comments (2)
tests/test_miner.py (2)

81-84: Fix Yoda conditions for better readability.

Replace the Yoda conditions with standard comparisons for better readability.

For lines 81-84:

-    assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for %s. 1st release for repository!', 'org/repo') == mock_log_info.call_args_list[1][0]
-    assert ('Skipping invalid type of version tag: %s | Error: %s', 'v1.0.0', '') == mock_log_error.call_args_list[0][0]
-    assert ('Skipping invalid type of version tag: %s | Error: %s', 'v2.0.0', '') == mock_log_error.call_args_list[2][0]
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for %s. 1st release for repository!', 'org/repo')
+    assert mock_log_error.call_args_list[0][0] == ('Skipping invalid type of version tag: %s | Error: %s', 'v1.0.0', '')
+    assert mock_log_error.call_args_list[2][0] == ('Skipping invalid type of version tag: %s | Error: %s', 'v2.0.0', '')

For lines 110-113:

-    assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for %s. 1st release for repository!', 'org/repo') == mock_log_info.call_args_list[1][0]
-    assert ('Skipping invalid value of version tag: %s', 'v1.0.0') == mock_log_error.call_args_list[0][0]
-    assert ('Skipping invalid value of version tag: %s', 'v2.0.0') == mock_log_error.call_args_list[1][0]
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for %s. 1st release for repository!', 'org/repo')
+    assert mock_log_error.call_args_list[0][0] == ('Skipping invalid value of version tag: %s', 'v1.0.0')
+    assert mock_log_error.call_args_list[1][0] == ('Skipping invalid value of version tag: %s', 'v2.0.0')

Also applies to: 110-113

🧰 Tools
🪛 Ruff (0.11.9)

81-81: Yoda condition detected

(SIM300)


82-82: Yoda condition detected

(SIM300)


83-83: Yoda condition detected

(SIM300)


84-84: Yoda condition detected

(SIM300)


135-135: Fix remaining Yoda conditions throughout the test file.

For consistency and readability, fix all remaining Yoda conditions.

Line 135:

-    assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)

Lines 157-159:

-    assert 2 == len(mock_log_info.call_args_list)
-    assert ('Getting latest release by semantic ordering (could not be the last one by time).',) == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for %s. 1st release for repository!', 'org/repo') == mock_log_info.call_args_list[1][0]
+    assert len(mock_log_info.call_args_list) == 2
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by semantic ordering (could not be the last one by time).',)
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for %s. 1st release for repository!', 'org/repo')

Lines 184-185:

-    assert 1 == len(mock_log_info.call_args_list)
-    assert ('Getting latest release by from-tag name %s', "") == mock_log_info.call_args_list[0][0]
+    assert len(mock_log_info.call_args_list) == 1
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by from-tag name %s', "")

Lines 210-212:

-    assert 2 == len(mock_log_info.call_args_list)
-    assert ('Getting latest release by from-tag name %s', "") == mock_log_info.call_args_list[0][0]
-    assert ('Latest release not found for received from-tag %s. Ending!', '') == mock_log_info.call_args_list[1][0]
+    assert len(mock_log_info.call_args_list) == 2
+    assert mock_log_info.call_args_list[0][0] == ('Getting latest release by from-tag name %s', "")
+    assert mock_log_info.call_args_list[1][0] == ('Latest release not found for received from-tag %s. Ending!', '')

Also applies to: 157-159, 184-185, 210-212

🧰 Tools
🪛 Ruff (0.11.9)

135-135: Yoda condition detected

(SIM300)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9107937 and abbc145.

📒 Files selected for processing (3)
  • release_notes_generator/generator.py (3 hunks)
  • release_notes_generator/miner.py (1 hunks)
  • tests/test_miner.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • release_notes_generator/generator.py
🧰 Additional context used
🧬 Code Graph Analysis (1)
tests/test_miner.py (3)
release_notes_generator/miner.py (2)
  • DataMiner (40-150)
  • get_latest_release (69-107)
release_notes_generator/model/MinedData.py (1)
  • MinedData (38-50)
tests/conftest.py (2)
  • mock_repo (78-81)
  • mock_git_releases (93-102)
🪛 Ruff (0.11.9)
tests/test_miner.py

81-81: Yoda condition detected

(SIM300)


82-82: Yoda condition detected

(SIM300)


83-83: Yoda condition detected

(SIM300)


84-84: Yoda condition detected

(SIM300)


110-110: Yoda condition detected

(SIM300)


111-111: Yoda condition detected

(SIM300)


112-112: Yoda condition detected

(SIM300)


113-113: Yoda condition detected

(SIM300)


135-135: Yoda condition detected

(SIM300)


157-157: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


158-158: Yoda condition detected

(SIM300)


159-159: Yoda condition detected

(SIM300)


184-184: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 1

(SIM300)


185-185: Yoda condition detected

(SIM300)


210-210: Yoda condition detected

Rewrite as len(mock_log_info.call_args_list) == 2

(SIM300)


211-211: Yoda condition detected

(SIM300)


212-212: Yoda condition detected

(SIM300)

🔇 Additional comments (2)
release_notes_generator/miner.py (2)

69-107: Well-structured release retrieval logic.

The method properly handles both tag-based and semantic version-based release retrieval with appropriate error handling and logging.


128-150: Robust semantic version handling.

The implementation correctly filters releases and handles version parsing errors with detailed logging including tracebacks.

Copy link
Collaborator

@miroslavpojer miroslavpojer left a comment

Choose a reason for hiding this comment

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

  • pulled
  • code review
  • local run tested

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 (1)
release_notes_generator/generator.py (1)

76-78: Consider consistent handling of data conversions.

The code converts pull_requests and commits to lists but not issues. While this works because issues is already converted to a list in the miner, it creates inconsistency that could lead to confusion.

Either convert all to lists for consistency:

-        issues_list = data.issues
+        issues_list = list(data.issues)

Or add a comment explaining why issues doesn't need conversion:

+        # Note: data.issues is already a list from DataMiner
         issues_list = data.issues
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between abbc145 and 103f98e.

📒 Files selected for processing (3)
  • release_notes_generator/action_inputs.py (1 hunks)
  • release_notes_generator/generator.py (3 hunks)
  • release_notes_generator/miner.py (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
release_notes_generator/action_inputs.py (1)
release_notes_generator/utils/gh_action.py (1)
  • get_action_input (26-38)

Comment on lines 84 to 88
if rls is None:
logger.info(
"Latest release not found for received from-tag %s. Ending!", ActionInputs.get_from_tag_name()
)
sys.exit(1)
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Replace sys.exit with exception for better error handling.

Using sys.exit(1) in a class method makes the code difficult to test and prevents proper error handling by callers. Consider raising an exception instead.

             if rls is None:
                 logger.info(
                     "Latest release not found for received from-tag %s. Ending!", ActionInputs.get_from_tag_name()
                 )
-                sys.exit(1)
+                raise ValueError(f"Release with tag '{ActionInputs.get_from_tag_name()}' not found")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if rls is None:
logger.info(
"Latest release not found for received from-tag %s. Ending!", ActionInputs.get_from_tag_name()
)
sys.exit(1)
if rls is None:
logger.info(
"Latest release not found for received from-tag %s. Ending!",
ActionInputs.get_from_tag_name()
)
raise ValueError(
f"Release with tag '{ActionInputs.get_from_tag_name()}' not found"
)
🤖 Prompt for AI Agents
In release_notes_generator/miner.py around lines 84 to 88, replace the
sys.exit(1) call with raising a specific exception to indicate the error
condition. This change will improve error handling by allowing callers to catch
and manage the exception instead of abruptly terminating the program, making the
code more testable and maintainable.

Copy link
Collaborator

@miroslavpojer miroslavpojer left a comment

Choose a reason for hiding this comment

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

  • pulled
  • code review
  • local run done

@OlivieFranklova OlivieFranklova merged commit 045bb20 into master Jun 12, 2025
5 checks passed
@OlivieFranklova OlivieFranklova deleted the feature/147-split-mining-logic branch June 12, 2025 09:10
@OlivieFranklova OlivieFranklova added the enhancement New feature or request label Jun 17, 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
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Split mining logic
2 participants