-
Notifications
You must be signed in to change notification settings - Fork 0
Feature/150 refactor generation of rn #151
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
base: master
Are you sure you want to change the base?
Conversation
WalkthroughThis update introduces a modular filtering system for release notes generation by adding a Changes
Sequence Diagram(s)sequenceDiagram
participant Main
participant FilterByRelease
participant ReleaseNotesGenerator
participant RecordFactory
Main->>FilterByRelease: Instantiate filterer
Main->>ReleaseNotesGenerator: generate(filterer)
ReleaseNotesGenerator->>FilterByRelease: filter(data)
FilterByRelease-->>ReleaseNotesGenerator: filtered data
ReleaseNotesGenerator->>RecordFactory: generate(data)
RecordFactory-->>ReleaseNotesGenerator: records
ReleaseNotesGenerator-->>Main: release notes output
Possibly related PRs
Suggested labels
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 10
🔭 Outside diff range comments (2)
release_notes_generator/model/record.py (1)
1-1
: Apply Black formatting.The pipeline indicates that Black formatting is required for this file.
black release_notes_generator/model/record.pyrelease_notes_generator/record/record_factory.py (1)
1-1
: Apply Black formatting.The pipeline indicates that Black formatting is required for this file.
black release_notes_generator/record/record_factory.py
🧹 Nitpick comments (8)
.gitignore (1)
161-161
: Qualify the path or use a pattern fordefault_output.txt
.If the file is always produced in the repository root, prefix it with “/” to avoid accidentally ignoring identically-named files in nested directories.
Alternatively, if multiple output files can be generated, consider a pattern such as*_output.txt
.tests/test_filter.py (2)
18-18
: Remove unused import.The
pytest
import is not used in this file.-import pytest from unittest.mock import MagicMock
46-47
: Fix Yoda conditions for better readability.The comparison order should follow natural language conventions.
-assert 0 == len(mock_log_info.call_args_list) -assert 0 == len(mock_log_debug.call_args_list) +assert len(mock_log_info.call_args_list) == 0 +assert len(mock_log_debug.call_args_list) == 0 -assert ('Starting issue, prs and commit reduction by the latest release since time.',) == mock_log_info.call_args_list[0][0] -assert ('Count of issues reduced from %d to %d', 2, 1) == mock_log_debug.call_args_list[0][0] -assert ('Count of pulls reduced from %d to %d', 2, 1) == mock_log_debug.call_args_list[1][0] -assert ('Count of commits reduced from %d to %d', 2, 1) == mock_log_debug.call_args_list[2][0] +assert mock_log_info.call_args_list[0][0] == ('Starting issue, prs and commit reduction by the latest release since time.',) +assert mock_log_debug.call_args_list[0][0] == ('Count of issues reduced from %d to %d', 2, 1) +assert mock_log_debug.call_args_list[1][0] == ('Count of pulls reduced from %d to %d', 2, 1) +assert mock_log_debug.call_args_list[2][0] == ('Count of commits reduced from %d to %d', 2, 1)Also applies to: 84-87
tests/release_notes/test_record_factory.py (3)
26-26
: Fix import path consistency.The import should use the full path like other imports for consistency.
-from model.MinedData import MinedData +from release_notes_generator.model.MinedData import MinedData
187-187
: Remove debug print statement.Debug print statements should not be left in production code.
-print(records[1].__class__ ) assert records[1].__class__ is IssueRecord
281-348
: Complete the test migration.Two test functions are commented out with TODO notes, indicating incomplete migration. These tests should either be fixed or removed to avoid confusion.
The commented tests suggest there are issues with the new MinedData interface. Would you like me to help fix these tests or should they be removed if they're no longer needed?
release_notes_generator/model/record.py (2)
253-254
: Address the TODO comment and verify function usage.The TODO comment suggests that both functions should be called and merged. However, based on the AI summary, the usage should be migrated to
get_issues_for_pr
. Please clarify the intended approach.Do you want me to help implement the proper integration of both functions or migrate to the new
get_issues_for_pr
function?
443-475
: LGTM! Well-structured inheritance with one naming concern.The new record subclasses properly inherit from the base
Record
class and specialize for different GitHub entity types. The initialization logic correctly calls the parent constructor and sets the release notes detection flag.Consider using the conventional double underscore for the
___issues
attribute inPullRequestRecord
:- self.___issues: Dict[int, List[Issue]] = {} + self.__issues: Dict[int, List[Issue]] = {}Triple underscores are unusual in Python and may cause confusion. Double underscores provide proper name mangling.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
.gitignore
(1 hunks)main.py
(2 hunks)release_notes_generator/action_inputs.py
(3 hunks)release_notes_generator/filter.py
(1 hunks)release_notes_generator/generator.py
(4 hunks)release_notes_generator/model/record.py
(4 hunks)release_notes_generator/record/record_factory.py
(4 hunks)release_notes_generator/utils/constants.py
(1 hunks)release_notes_generator/utils/pull_request_utils.py
(2 hunks)tests/release_notes/test_record_factory.py
(7 hunks)tests/test_filter.py
(1 hunks)tests/test_release_notes_generator.py
(7 hunks)tests/utils/test_pull_reuqest_utils.py
(1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (7)
tests/utils/test_pull_reuqest_utils.py (1)
release_notes_generator/utils/pull_request_utils.py (2)
extract_issue_numbers_from_body
(31-47)get_issues_for_pr
(50-64)
main.py (2)
release_notes_generator/filter.py (1)
FilterByRelease
(40-77)release_notes_generator/generator.py (1)
generate
(68-98)
release_notes_generator/filter.py (2)
release_notes_generator/model/MinedData.py (1)
MinedData
(38-50)release_notes_generator/model/record.py (2)
commits
(75-77)issue
(65-67)
release_notes_generator/model/record.py (2)
release_notes_generator/utils/pull_request_utils.py (1)
extract_issue_numbers_from_body
(31-47)release_notes_generator/record/record_factory.py (1)
register_pull_request
(74-97)
release_notes_generator/action_inputs.py (1)
release_notes_generator/utils/gh_action.py (1)
get_action_input
(26-38)
tests/release_notes/test_record_factory.py (5)
release_notes_generator/model/MinedData.py (1)
MinedData
(38-50)release_notes_generator/model/record.py (4)
Record
(43-441)IssueRecord
(456-464)commits
(75-77)labels
(128-133)tests/conftest.py (1)
mock_repo
(78-81)release_notes_generator/record/record_factory.py (2)
RecordFactory
(41-138)generate
(47-138)release_notes_generator/generator.py (1)
generate
(68-98)
release_notes_generator/generator.py (2)
release_notes_generator/filter.py (3)
filter
(30-37)filter
(48-77)Filter
(25-37)release_notes_generator/record/record_factory.py (1)
generate
(47-138)
🪛 Ruff (0.11.9)
tests/utils/test_pull_reuqest_utils.py
19-19: release_notes_generator.utils.pull_request_utils.get_issues_for_pr
imported but unused
Remove unused import: release_notes_generator.utils.pull_request_utils.get_issues_for_pr
(F401)
release_notes_generator/model/record.py
24-24: typing.AnyStr
imported but unused
Remove unused import: typing.AnyStr
(F401)
tests/test_filter.py
18-18: pytest
imported but unused
Remove unused import: pytest
(F401)
46-46: Yoda condition detected
Rewrite as len(mock_log_info.call_args_list) == 0
(SIM300)
47-47: Yoda condition detected
Rewrite as len(mock_log_debug.call_args_list) == 0
(SIM300)
84-84: Yoda condition detected
(SIM300)
85-85: Yoda condition detected
(SIM300)
86-86: Yoda condition detected
(SIM300)
87-87: Yoda condition detected
(SIM300)
🪛 GitHub Actions: Build and Test
release_notes_generator/utils/pull_request_utils.py
[error] 1-1: Black formatting check failed. File would be reformatted. Run 'black' to fix code style issues.
release_notes_generator/filter.py
[error] 1-1: Black formatting check failed. File would be reformatted. Run 'black' to fix code style issues.
release_notes_generator/model/record.py
[error] 1-1: Black formatting check failed. File would be reformatted. Run 'black' to fix code style issues.
release_notes_generator/action_inputs.py
[error] 1-1: Black formatting check failed. File would be reformatted. Run 'black' to fix code style issues.
release_notes_generator/record/record_factory.py
[error] 1-1: Black formatting check failed. File would be reformatted. Run 'black' to fix code style issues.
release_notes_generator/generator.py
[error] 1-1: Black formatting check failed. File would be reformatted. Run 'black' to fix code style issues.
🔇 Additional comments (12)
release_notes_generator/utils/constants.py (1)
71-84
: LGTM! Well-structured constants for GitHub API integration.The new constants are properly designed:
LINKED_ISSUES_MAX
provides a reasonable limit for API callsISSUES_FOR_PRS
GraphQL query correctly usesclosingIssuesReferences
to fetch linked issues- The placeholders align with the usage in
pull_request_utils.py
tests/utils/test_pull_reuqest_utils.py (1)
19-19
: LGTM! Import statement corrected and expanded.The import statement properly fixes the typo from
pull_reuqest_utils
topull_request_utils
and adds the newget_issues_for_pr
function. The unused import warning can be ignored as this is common in test files where imports may be prepared for future tests.main.py (2)
32-32
: LGTM! Import added for new filtering mechanism.The import correctly adds
FilterByRelease
to support the new external filtering approach.
55-56
: LGTM! Generator updated to use new filtering interface.The changes correctly implement the new filtering mechanism:
- Creates a
FilterByRelease
instance- Passes it to the
generate()
method as required by the new signatureThis aligns with the refactoring to externalize filtering logic from the generator.
tests/test_release_notes_generator.py (3)
26-26
: LGTM! Import added for test integration.The import correctly adds
FilterByRelease
to support testing the new filtering mechanism.
42-42
: LGTM! Tests updated for new filtering interface.All test cases have been consistently updated to pass
FilterByRelease()
instances to thegenerate()
method, ensuring compatibility with the new external filtering approach.Also applies to: 77-77, 127-127, 173-173
70-70
: LGTM! Proper mocking for external dependencies.The addition of mocking for
get_issues_for_pr
ensures test isolation and prevents external API calls during testing. This is a best practice for unit testing.Also applies to: 113-113, 164-164
release_notes_generator/generator.py (1)
68-98
: Excellent architectural improvement.The refactoring to use external filtering via the
Filter
abstraction is a great improvement in separation of concerns and modularity. This makes the code more testable and allows for different filtering strategies.release_notes_generator/model/record.py (1)
37-37
: No import inconsistency detected in record.pyThe file consistently imports and uses
extract_issue_numbers_from_body
—there is no reference toget_issues_for_pr
, so no change is needed.• File: release_notes_generator/model/record.py
– Line 37: importsextract_issue_numbers_from_body
– Line 254: callsextract_issue_numbers_from_body(self.__pulls[0])
Likely an incorrect or invalid review comment.
release_notes_generator/record/record_factory.py (3)
47-56
: LGTM! Excellent refactoring to use MinedData.The method signature change to accept a single
MinedData
object instead of separate parameters is a clean improvement that enhances data encapsulation and consistency across the codebase.
70-70
: LGTM! Proper usage of specialized record classes.The migration from generic
Record
to specializedIssueRecord
andPullRequestRecord
classes improves type safety and code clarity. The instantiation logic correctly maintains the existing functionality while leveraging the new class hierarchy.Also applies to: 91-91, 124-124
75-75
: LGTM! Consistent migration to new utility function.The change from
extract_issue_numbers_from_body
toget_issues_for_pr
is consistent with the refactoring goals and provides better integration with GitHub's GraphQL API for retrieving linked issues.Also applies to: 123-123
@@ -157,3 +157,6 @@ cython_debug/ | |||
# and can be added to the global gitignore or merged into this file. For a more nuclear | |||
# option (not recommended) you can uncomment the following to ignore the entire idea folder. | |||
.idea/ | |||
|
|||
default_output.txt | |||
run_localy.sh |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo in ignored script name – likely ineffective rule.
run_localy.sh
is probably intended to be run_locally.sh
.
Because of the misspelling, the actual helper script will still be tracked (and the stray typo will mask future mistakes).
Fix the entry and keep the incorrect form for backward compatibility if it was already committed before:
-run_localy.sh
+# Correct name
+run_locally.sh
+# Legacy misspelling kept to cover any existing accidental commits
+run_localy.sh
📝 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.
run_localy.sh | |
# Correct name | |
run_locally.sh | |
# Legacy misspelling kept to cover any existing accidental commits | |
run_localy.sh |
🤖 Prompt for AI Agents
In the .gitignore file at line 162, the script name "run_localy.sh" is
misspelled and should be corrected to "run_locally.sh" to properly ignore the
intended file. Update the entry to include the correct spelling "run_locally.sh"
and retain the incorrect "run_localy.sh" line if it was previously committed,
ensuring backward compatibility while fixing the ignore rule.
ActionInputs._owner, ActionInputs._repo_name = ActionInputs.get_github_repository().split("/") | ||
else: | ||
owner = repo_name = "" | ||
ActionInputs._owner = ActionInputs._repo_name = "" | ||
|
||
if not isinstance(owner, str) or not owner.strip() or not isinstance(repo_name, str) or not repo_name.strip(): | ||
if not isinstance(ActionInputs._owner, str) or not ActionInputs._owner.strip() or not isinstance(ActionInputs._repo_name, str) or not ActionInputs._repo_name.strip(): | ||
errors.append("Owner and Repo must be a non-empty string.") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent repository parsing logic.
The parsing logic differs between the new caching methods and the validation logic. When there's no "/" in the repository ID, the new methods set both owner and repo name to the full repository ID, but validation sets both to empty string.
Apply this diff to make the logic consistent:
- if "/" in repository_id:
- ActionInputs._owner, ActionInputs._repo_name = ActionInputs.get_github_repository().split("/")
- else:
- ActionInputs._owner = ActionInputs._repo_name = ""
+ ActionInputs._owner = ActionInputs.get_github_owner()
+ ActionInputs._repo_name = ActionInputs.get_github_repo_name()
📝 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.
ActionInputs._owner, ActionInputs._repo_name = ActionInputs.get_github_repository().split("/") | |
else: | |
owner = repo_name = "" | |
ActionInputs._owner = ActionInputs._repo_name = "" | |
if not isinstance(owner, str) or not owner.strip() or not isinstance(repo_name, str) or not repo_name.strip(): | |
if not isinstance(ActionInputs._owner, str) or not ActionInputs._owner.strip() or not isinstance(ActionInputs._repo_name, str) or not ActionInputs._repo_name.strip(): | |
errors.append("Owner and Repo must be a non-empty string.") | |
ActionInputs._owner = ActionInputs.get_github_owner() | |
ActionInputs._repo_name = ActionInputs.get_github_repo_name() | |
if not isinstance(ActionInputs._owner, str) or not ActionInputs._owner.strip() or not isinstance(ActionInputs._repo_name, str) or not ActionInputs._repo_name.strip(): | |
errors.append("Owner and Repo must be a non-empty string.") |
🤖 Prompt for AI Agents
In release_notes_generator/action_inputs.py around lines 335 to 340, the
repository parsing logic is inconsistent: the new caching methods assign both
owner and repo name to the full repository ID if no "/" is present, but the
validation sets both to empty strings. To fix this, update the validation logic
to match the caching methods by assigning both owner and repo name to the full
repository ID string when no "/" is found, ensuring consistent handling across
the code.
_owner = "" | ||
_repo_name = "" | ||
|
||
@staticmethod | ||
def get_github_owner() -> str: | ||
""" | ||
Get the GitHub owner from the action inputs. | ||
""" | ||
if ActionInputs._owner: | ||
return ActionInputs._owner | ||
|
||
repository_id = get_action_input(GITHUB_REPOSITORY) or "" | ||
if "/" in repository_id: | ||
ActionInputs._owner, _ = repository_id.split("/", 1) | ||
else: | ||
ActionInputs._owner = repository_id | ||
|
||
return ActionInputs._owner | ||
|
||
@staticmethod | ||
def get_github_repo_name() -> str: | ||
""" | ||
Get the GitHub repository name from the action inputs. | ||
""" | ||
if ActionInputs._repo_name: | ||
return ActionInputs._repo_name | ||
|
||
repository_id = get_action_input(GITHUB_REPOSITORY) or "" | ||
if "/" in repository_id: | ||
_, ActionInputs._repo_name = repository_id.split("/", 1) | ||
else: | ||
ActionInputs._repo_name = repository_id | ||
|
||
return ActionInputs._repo_name |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Fix formatting and address logic inconsistency.
The pipeline shows Black formatting issues that need to be resolved. Additionally, there's an inconsistency in parsing logic:
- In
get_github_owner()
andget_github_repo_name()
: when no "/" exists, both methods set their respective values to the fullrepository_id
- In
validate_inputs()
(line 337): when no "/" exists, both_owner
and_repo_name
are set to empty string
Apply this diff to fix the formatting and logic consistency:
- _owner = ""
- _repo_name = ""
+ _owner = ""
+ _repo_name = ""
And run Black to fix formatting issues:
🏁 Script executed:
#!/bin/bash
# Fix Black formatting issues
black release_notes_generator/action_inputs.py
Length of output: 144
🏁 Script executed:
#!/bin/bash
# Locate and display the validate_inputs method to verify parsing logic
rg -n "def validate_inputs" -A 5 -B 5 release_notes_generator/action_inputs.py
Length of output: 522
#!/bin/bash
# Display the full validate_inputs method to verify owner/repo parsing logic
rg -n "def validate_inputs" -A 50 release_notes_generator/action_inputs.py
🏁 Script executed:
#!/bin/bash
# Extract the complete validate_inputs method to inspect parsing logic
rg -n -C50 "def validate_inputs" release_notes_generator/action_inputs.py
Length of output: 4987
Ensure consistent GitHub owner/repo parsing in validate_inputs and fix formatting
The getters get_github_owner()
and get_github_repo_name()
fall back to the full repository string when no “/” is present, but validate_inputs()
currently:
- Calls a non-existent
get_github_repository()
at line 330 - Splits and assigns
_owner
/_repo_name
only when “/” exists, else resets both to""
- This diverges from the getters and will overwrite your cached values
• In release_notes_generator/action_inputs.py, update validate_inputs()
(around lines 330–337):
- repository_id = ActionInputs.get_github_repository()
- if not isinstance(repository_id, str) or not repository_id.strip():
- errors.append("Repository ID must be a non-empty string.")
-
- if "/" in repository_id:
- ActionInputs._owner, ActionInputs._repo_name = ActionInputs.get_github_repository().split("/")
- else:
- ActionInputs._owner = ActionInputs._repo_name = ""
+ # Use the new getters for consistent parsing
+ owner = ActionInputs.get_github_owner()
+ repo_name = ActionInputs.get_github_repo_name()
+ repository_id = f"{owner}/{repo_name}" if owner and repo_name else owner or repo_name
+ if not repository_id:
+ errors.append("Repository ID must be a non-empty string.")
+ # Persist the values returned by the getters
+ ActionInputs._owner = owner
+ ActionInputs._repo_name = repo_name
• Run Black on release_notes_generator/action_inputs.py
(or install/configure Black in CI) to resolve formatting errors.
🤖 Prompt for AI Agents
In release_notes_generator/action_inputs.py around lines 330 to 337, update the
validate_inputs() function to use the existing get_github_owner() and
get_github_repo_name() methods instead of calling the non-existent
get_github_repository(). Ensure that the owner and repo name are assigned
consistently by relying on these getters rather than manually splitting the
repository string. Also, run Black code formatter on the entire file to fix any
formatting issues and maintain consistent style.
|
||
## todo romove |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove the TODO comment and outdated function.
The TODO comment indicates the extract_issue_numbers_from_body
function should be removed, but it's still present in the code. If this function is no longer needed, please remove it to avoid confusion.
🤖 Prompt for AI Agents
In release_notes_generator/utils/pull_request_utils.py at line 30, remove the
TODO comment and delete the entire extract_issue_numbers_from_body function if
it is no longer needed, to keep the code clean and avoid confusion.
@lru_cache(maxsize=None) | ||
def get_issues_for_pr(pull_number: int) -> list[int]: | ||
"""Update the placeholder values and formate the graphQL query""" | ||
github_api_url = "https://api.github.com/graphql" | ||
query = ISSUES_FOR_PRS.format(number=pull_number, | ||
owner=ActionInputs.get_github_owner(), | ||
name=ActionInputs.get_github_repo_name(), | ||
first=LINKED_ISSUES_MAX) | ||
headers = { | ||
"Authorization": f"Bearer {ActionInputs.get_github_token()}", | ||
"Content-Type": "application/json", | ||
} | ||
response = requests.post(github_api_url, json={"query": query}, headers=headers) | ||
response.raise_for_status() # Raise an error for HTTP issues | ||
numbers = [node['number'] for node in response.json()['data']['repository']['pullRequest']['closingIssuesReferences']['nodes']] | ||
return numbers | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add proper error handling for GraphQL API calls.
The function lacks robust error handling for several potential failure scenarios:
- Invalid pull request number
- GraphQL API returning unexpected structure
- Network failures or API rate limits
Consider adding validation and error handling:
@lru_cache(maxsize=None)
def get_issues_for_pr(pull_number: int) -> list[int]:
"""Update the placeholder values and formate the graphQL query"""
+ if not isinstance(pull_number, int) or pull_number <= 0:
+ raise ValueError(f"Invalid pull request number: {pull_number}")
+
github_api_url = "https://api.github.com/graphql"
query = ISSUES_FOR_PRS.format(number=pull_number,
owner=ActionInputs.get_github_owner(),
name=ActionInputs.get_github_repo_name(),
first=LINKED_ISSUES_MAX)
headers = {
"Authorization": f"Bearer {ActionInputs.get_github_token()}",
"Content-Type": "application/json",
}
- response = requests.post(github_api_url, json={"query": query}, headers=headers)
- response.raise_for_status() # Raise an error for HTTP issues
- numbers = [node['number'] for node in response.json()['data']['repository']['pullRequest']['closingIssuesReferences']['nodes']]
- return numbers
+ try:
+ response = requests.post(github_api_url, json={"query": query}, headers=headers)
+ response.raise_for_status()
+ data = response.json()
+
+ if 'errors' in data:
+ logger.warning("GraphQL errors: %s", data['errors'])
+ return []
+
+ pr_data = data.get('data', {}).get('repository', {}).get('pullRequest')
+ if not pr_data:
+ logger.warning("Pull request %d not found", pull_number)
+ return []
+
+ closing_issues = pr_data.get('closingIssuesReferences', {}).get('nodes', [])
+ return [node['number'] for node in closing_issues if 'number' in node]
+ except requests.exceptions.RequestException as e:
+ logger.error("Failed to fetch issues for PR %d: %s", pull_number, e)
+ return []
📝 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.
@lru_cache(maxsize=None) | |
def get_issues_for_pr(pull_number: int) -> list[int]: | |
"""Update the placeholder values and formate the graphQL query""" | |
github_api_url = "https://api.github.com/graphql" | |
query = ISSUES_FOR_PRS.format(number=pull_number, | |
owner=ActionInputs.get_github_owner(), | |
name=ActionInputs.get_github_repo_name(), | |
first=LINKED_ISSUES_MAX) | |
headers = { | |
"Authorization": f"Bearer {ActionInputs.get_github_token()}", | |
"Content-Type": "application/json", | |
} | |
response = requests.post(github_api_url, json={"query": query}, headers=headers) | |
response.raise_for_status() # Raise an error for HTTP issues | |
numbers = [node['number'] for node in response.json()['data']['repository']['pullRequest']['closingIssuesReferences']['nodes']] | |
return numbers | |
@lru_cache(maxsize=None) | |
def get_issues_for_pr(pull_number: int) -> list[int]: | |
"""Update the placeholder values and formate the graphQL query""" | |
if not isinstance(pull_number, int) or pull_number <= 0: | |
raise ValueError(f"Invalid pull request number: {pull_number}") | |
github_api_url = "https://api.github.com/graphql" | |
query = ISSUES_FOR_PRS.format( | |
number=pull_number, | |
owner=ActionInputs.get_github_owner(), | |
name=ActionInputs.get_github_repo_name(), | |
first=LINKED_ISSUES_MAX, | |
) | |
headers = { | |
"Authorization": f"Bearer {ActionInputs.get_github_token()}", | |
"Content-Type": "application/json", | |
} | |
try: | |
response = requests.post(github_api_url, json={"query": query}, headers=headers) | |
response.raise_for_status() | |
data = response.json() | |
if "errors" in data: | |
logger.warning("GraphQL errors: %s", data["errors"]) | |
return [] | |
pr_data = data.get("data", {}).get("repository", {}).get("pullRequest") | |
if not pr_data: | |
logger.warning("Pull request %d not found", pull_number) | |
return [] | |
closing_issues = pr_data.get("closingIssuesReferences", {}).get("nodes", []) | |
return [node["number"] for node in closing_issues if "number" in node] | |
except requests.exceptions.RequestException as e: | |
logger.error("Failed to fetch issues for PR %d: %s", pull_number, e) | |
return [] |
🤖 Prompt for AI Agents
In release_notes_generator/utils/pull_request_utils.py around lines 49 to 65,
the function get_issues_for_pr lacks error handling for invalid pull request
numbers, unexpected GraphQL response structures, and network or API errors. Add
try-except blocks around the API call and JSON parsing to catch requests
exceptions and KeyError or TypeError for unexpected data. Validate the response
structure before accessing nested keys and handle cases where the pull request
or issues data is missing. Log or raise informative errors to make failures
clear and prevent unhandled exceptions.
def __init__(self, release_version: Optional[str] = None): | ||
self.release_version = release_version |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Remove unused constructor parameter.
The release_version
parameter is not used anywhere in the FilterByRelease
class. Either implement its usage or remove it to avoid confusion.
-def __init__(self, release_version: Optional[str] = None):
- self.release_version = release_version
+def __init__(self):
+ pass
📝 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.
def __init__(self, release_version: Optional[str] = None): | |
self.release_version = release_version | |
def __init__(self): | |
pass |
🤖 Prompt for AI Agents
In release_notes_generator/filter.py around lines 45 to 46, the constructor of
the FilterByRelease class has an unused parameter release_version. To fix this,
remove the release_version parameter from the __init__ method and also remove
the assignment to self.release_version to avoid confusion and keep the code
clean.
def filter(self, data: MinedData): | ||
""" | ||
Filters issues, pull requests, and commits based on the latest release date. | ||
If the release is not None, it filters out closed issues, merged pull requests, and commits | ||
that occurred before the release date. | ||
@param data: The mined data containing issues, pull requests, commits, and release information. | ||
@return: The filtered mined data. | ||
""" | ||
|
||
issues_list = data.issues | ||
pulls_list = data.pull_requests | ||
commits_list = data.commits | ||
|
||
if data.release is not None: | ||
logger.info("Starting issue, prs and commit reduction by the latest release since time.") | ||
|
||
# filter out closed Issues before the date | ||
data.issues = list( | ||
filter(lambda issue: issue.closed_at is not None and issue.closed_at >= data.since, issues_list) | ||
) | ||
logger.debug("Count of issues reduced from %d to %d", len(issues_list), len(data.issues)) | ||
|
||
# filter out merged PRs and commits before the date | ||
data.pull_requests = list( | ||
filter(lambda pull: pull.merged_at is not None and pull.merged_at >= data.since, pulls_list) | ||
) | ||
logger.debug("Count of pulls reduced from %d to %d", len(pulls_list), len(data.pull_requests)) | ||
|
||
data.commits = list(filter(lambda commit: commit.commit.author.date > data.since, commits_list)) | ||
logger.debug("Count of commits reduced from %d to %d", len(commits_list), len(data.commits)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix missing return statement and inconsistent filtering logic.
The method has two critical issues:
- Missing return statement - the method should return the filtered
MinedData
- Inconsistent comparison operators for commits (
>
) vs issues/PRs (>=
)
Apply this fix:
-def filter(self, data: MinedData):
+def filter(self, data: MinedData) -> MinedData:
"""
Filters issues, pull requests, and commits based on the latest release date.
If the release is not None, it filters out closed issues, merged pull requests, and commits
that occurred before the release date.
@param data: The mined data containing issues, pull requests, commits, and release information.
@return: The filtered mined data.
"""
issues_list = data.issues
pulls_list = data.pull_requests
commits_list = data.commits
if data.release is not None:
logger.info("Starting issue, prs and commit reduction by the latest release since time.")
# filter out closed Issues before the date
data.issues = list(
filter(lambda issue: issue.closed_at is not None and issue.closed_at >= data.since, issues_list)
)
logger.debug("Count of issues reduced from %d to %d", len(issues_list), len(data.issues))
# filter out merged PRs and commits before the date
data.pull_requests = list(
filter(lambda pull: pull.merged_at is not None and pull.merged_at >= data.since, pulls_list)
)
logger.debug("Count of pulls reduced from %d to %d", len(pulls_list), len(data.pull_requests))
- data.commits = list(filter(lambda commit: commit.commit.author.date > data.since, commits_list))
+ data.commits = list(filter(lambda commit: commit.commit.author.date >= data.since, commits_list))
logger.debug("Count of commits reduced from %d to %d", len(commits_list), len(data.commits))
+
+ return data
📝 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.
def filter(self, data: MinedData): | |
""" | |
Filters issues, pull requests, and commits based on the latest release date. | |
If the release is not None, it filters out closed issues, merged pull requests, and commits | |
that occurred before the release date. | |
@param data: The mined data containing issues, pull requests, commits, and release information. | |
@return: The filtered mined data. | |
""" | |
issues_list = data.issues | |
pulls_list = data.pull_requests | |
commits_list = data.commits | |
if data.release is not None: | |
logger.info("Starting issue, prs and commit reduction by the latest release since time.") | |
# filter out closed Issues before the date | |
data.issues = list( | |
filter(lambda issue: issue.closed_at is not None and issue.closed_at >= data.since, issues_list) | |
) | |
logger.debug("Count of issues reduced from %d to %d", len(issues_list), len(data.issues)) | |
# filter out merged PRs and commits before the date | |
data.pull_requests = list( | |
filter(lambda pull: pull.merged_at is not None and pull.merged_at >= data.since, pulls_list) | |
) | |
logger.debug("Count of pulls reduced from %d to %d", len(pulls_list), len(data.pull_requests)) | |
data.commits = list(filter(lambda commit: commit.commit.author.date > data.since, commits_list)) | |
logger.debug("Count of commits reduced from %d to %d", len(commits_list), len(data.commits)) | |
def filter(self, data: MinedData) -> MinedData: | |
""" | |
Filters issues, pull requests, and commits based on the latest release date. | |
If the release is not None, it filters out closed issues, merged pull requests, and commits | |
that occurred before the release date. | |
@param data: The mined data containing issues, pull requests, commits, and release information. | |
@return: The filtered mined data. | |
""" | |
issues_list = data.issues | |
pulls_list = data.pull_requests | |
commits_list = data.commits | |
if data.release is not None: | |
logger.info("Starting issue, prs and commit reduction by the latest release since time.") | |
# filter out closed Issues before the date | |
data.issues = list( | |
filter(lambda issue: issue.closed_at is not None and issue.closed_at >= data.since, issues_list) | |
) | |
logger.debug("Count of issues reduced from %d to %d", len(issues_list), len(data.issues)) | |
# filter out merged PRs before the date | |
data.pull_requests = list( | |
filter(lambda pull: pull.merged_at is not None and pull.merged_at >= data.since, pulls_list) | |
) | |
logger.debug("Count of pulls reduced from %d to %d", len(pulls_list), len(data.pull_requests)) | |
# filter out commits before the date (using >= for consistency) | |
data.commits = list( | |
filter(lambda commit: commit.commit.author.date >= data.since, commits_list) | |
) | |
logger.debug("Count of commits reduced from %d to %d", len(commits_list), len(data.commits)) | |
return data |
🤖 Prompt for AI Agents
In release_notes_generator/filter.py between lines 48 and 78, the filter method
lacks a return statement and uses inconsistent comparison operators for
filtering commits versus issues and pull requests. Fix this by adding a return
statement at the end to return the filtered data object, and change the commit
filtering comparison operator from '>' to '>=' to match the inclusive filtering
logic used for issues and pull requests.
@@ -105,7 +76,7 @@ def generate(self) -> Optional[str]: | |||
if data.is_empty(): | |||
return None | |||
|
|||
self._filter_by_release(data) | |||
filterer.filter(data=data) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Use the return value from the filter method.
The filter method returns the filtered MinedData
object, but the code doesn't use the return value. While the current implementation works because the filter modifies the data in-place, it's better to follow the contract and use the return value.
-filterer.filter(data=data)
+data = filterer.filter(data=data)
📝 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.
filterer.filter(data=data) | |
data = filterer.filter(data=data) |
🤖 Prompt for AI Agents
In release_notes_generator/generator.py at line 79, the filter method's return
value, which is the filtered MinedData object, is not being used. Update the
code to assign the result of filterer.filter(data=data) back to a variable
(e.g., data) to properly use the returned filtered object instead of relying on
in-place modification.
@@ -21,7 +21,7 @@ | |||
import logging | |||
import re | |||
|
|||
from typing import Optional, AnyStr, Any | |||
from typing import Optional, AnyStr, Any, Dict, List |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import.
The AnyStr
import is not used in the file and should be removed.
-from typing import Optional, AnyStr, Any, Dict, List
+from typing import Optional, Any, Dict, List
📝 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.
from typing import Optional, AnyStr, Any, Dict, List | |
from typing import Optional, Any, Dict, List |
🧰 Tools
🪛 Ruff (0.11.9)
24-24: typing.AnyStr
imported but unused
Remove unused import: typing.AnyStr
(F401)
🤖 Prompt for AI Agents
In release_notes_generator/model/record.py at line 24, the import statement
includes AnyStr which is not used anywhere in the file. Remove AnyStr from the
import list to clean up unused imports and improve code clarity.
from github.Commit import Commit | ||
|
||
from model.record import IssueRecord, PullRequestRecord |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix import path for record subclasses.
The import statement is missing the full module path. It should include the release_notes_generator
prefix to match the other imports.
-from model.record import IssueRecord, PullRequestRecord
+from release_notes_generator.model.record import IssueRecord, PullRequestRecord
📝 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.
from model.record import IssueRecord, PullRequestRecord | |
from release_notes_generator.model.record import IssueRecord, PullRequestRecord |
🤖 Prompt for AI Agents
In release_notes_generator/record/record_factory.py at line 28, the import
statement for IssueRecord and PullRequestRecord is missing the full module path.
Update the import to include the release_notes_generator prefix so it matches
the other imports, changing it to import from
release_notes_generator.model.record instead of just model.record.
Summary by CodeRabbit
New Features
Refactor
Bug Fixes
Tests
Checklist:
Additional Context:
Fixes or Closes #(issue)