Skip to content

Conversation

@pushpitkamboj
Copy link
Contributor

Checklist

  • I have read the OpenWISP Contributing Guidelines.
  • I have manually tested the changes proposed in this pull request.
  • I have written new test cases for new code and/or updated existing tests for changes to existing code.
  • I have updated the documentation.

Reference to Existing Issue

Closes #523

Description of Changes

Changelog Bot Workflow

1. TRIGGER

  • Maintainer after approving the PR comments @openwisp-bot changelog on it

2. WORKFLOW ACTIVATION

  • changelog-bot.yml detects the event
  • Validates user has write access to repo
  • Calls reusable-changelog-bot.yml

3. ACKNOWLEDGMENT

  • Adds 🚀 reaction to the maintainer's comment as a sign of acknowledging
  • Signals that the request is being processed

4. ACTION SETUP

  • Checkout step: Clones openwisp/openwisp-utils repo with sparse-checkout (only changelong directory)
  • Sets up Python 3.11
  • Installs google-genai dependency
  • Passes environment variables:
    • GITHUB_TOKEN, GEMINI_API_KEY
    • PR_NUMBER, REPO_NAME, LLM_MODEL

5. CHANGELOG GENERATION (generate_changelog.py)

  • Fetches PR details (title, description, labels)
  • Fetches PR diff (truncated to 15k chars)
  • Fetches commit messages
  • Fetches linked issues from PR body
  • Builds prompt with all context
  • Calls Gemini API to generate RST changelog entry

6. POST COMMENT

  • Posts generated changelog as PR comment
  • Entry is wrapped in \``rst` code block
  • Maintainer can copy directly to CHANGES.rst

7. TESTING

  • Also updated the runtests.py to include the test cases for generate_changlog.py.
  • 27 test cases were written with the help of AI assistance

8. DOCUMENTATION

  • wrote the detailed process in docs/developer/changelog-bot.rst

@coderabbitai
Copy link

coderabbitai bot commented Feb 3, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds an automated changelog generator: a workflow triggered on pull request review submission calls a reusable workflow that validates PR state and conditionally runs a composite action. The composite action checks out the PR, collects PR metadata (title, body, labels, diff, up to 10 commits, up to 3 linked issues), builds a prompt, calls Google Gemini to generate a RestructuredText changelog entry, and posts the result as a comment on the PR. Also adds tests, docs, test dependency, and includes the generator action as a reusable component.

Sequence Diagram(s)

sequenceDiagram
    actor Reviewer
    participant GitHub as GitHub
    participant Reusable as Reusable<br/>Changelog Workflow
    participant Action as Changelog<br/>Generator Action
    participant Gemini as Google<br/>Gemini API
    participant GitHubAPI as GitHub<br/>API

    Reviewer->>GitHub: Submit PR review
    GitHub->>Reusable: Trigger workflow (pull_request_review)
    Reusable->>Reusable: Validate preconditions (PR state, commits, approval)
    alt noteworthy commits present
        Reusable->>Action: Run generator action (pass PR number, repo, model, secret)
        Action->>GitHubAPI: Fetch PR details, diff, commits, linked issues
        GitHubAPI->>Action: Return PR metadata
        Action->>Action: Build structured prompt
        Action->>Gemini: Request RST changelog (model, API key)
        Gemini->>Action: Return generated changelog
        Action->>GitHubAPI: Post changelog as PR comment
        GitHubAPI->>GitHub: Comment created
    else skip
        Reusable->>GitHub: Skip generator (no noteworthy commits)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • nemesifier
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main feature being added: a changelog bot using Gemini AI to generate RST entries for pull requests.
Description check ✅ Passed The PR description covers all required template sections with complete checklists, linked issue reference, and detailed description of the implementation workflow and testing approach.
Linked Issues check ✅ Passed The implementation fulfills all core requirements from #523: automated changelog generation in RST format, posting as GitHub comment, AI-powered analysis, and selective triggering for specific PR types.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing the changelog bot feature: GitHub Actions workflows, the Python changelog generator script, comprehensive tests, documentation, and minor test runner updates.

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

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

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

🤖 Fix all issues with AI agents
In `@docs/developer/changelog-bot.rst`:
- Around line 126-129: The docs claim an automatic trigger on PR approval but
the reusable workflow lacks a pull_request_review event; either remove that docs
section or add support by updating the reusable workflow to handle the
pull_request_review event (add a top-level pull_request_review: entry) and route
it to the same job that handles manual triggers (e.g., the generate_changelog or
changelog-suggest job), guarding execution with a condition like if:
github.event.review.state == 'approved' (or equivalent) so only approvals invoke
the workflow.
- Around line 77-92: The docs list a non-existent trigger "pull_request_review";
update docs/developer/changelog-bot.rst to match the actual workflow by removing
the pull_request_review block and clarifying the bot triggers only on
issue_comment (created) with the trigger-phrase input (e.g., "@openwisp-bot
changelog"); ensure the example shows only the issue_comment trigger and
mentions the use of the trigger-phrase and GEMINI_API_KEY secret so it reflects
changelog-bot.yml's current behavior.
🧹 Nitpick comments (5)
.github/actions/changelog-generator/test_generate_changelog.py (2)

82-109: Consider adding a test for diff fetch failure.

The get_pr_diff function returns an empty string on HTTP/URL errors (graceful degradation). Adding a test case that verifies this behavior would improve coverage.

💡 Optional test for error handling
`@patch`("generate_changelog.urlopen")
def test_returns_empty_on_error(self, mock_urlopen):
    from urllib.error import HTTPError
    mock_urlopen.side_effect = HTTPError(None, 404, "Not Found", {}, None)
    
    result = get_pr_diff("org/repo", 123, "token")
    
    self.assertEqual(result, "")

361-372: Test scenario is slightly unrealistic but still validates error handling.

The test mocks urlopen to return a response with status 403, but in reality urlopen raises HTTPError for non-2xx status codes rather than returning a response object. The implementation's response.status != 201 check (in post_github_comment) would only trigger for unusual cases like status 200 or 204.

However, this test still validates that non-201 responses are rejected, so the overall behavior is correct.

💡 More realistic error test
`@patch`("generate_changelog.urlopen")
def test_raises_on_http_error(self, mock_urlopen):
    from urllib.error import HTTPError
    mock_urlopen.side_effect = HTTPError(
        "https://api.github.com/...", 403, "Forbidden", {}, None
    )

    with self.assertRaises(HTTPError):
        post_github_comment("org/repo", 123, "Test comment", "token")
.github/actions/changelog-generator/generate_changelog.py (1)

140-141: Consider logging the exception for debugging purposes.

The broad except Exception with continue is reasonable here since linked issues are supplementary data, but logging the error would help diagnose issues in production.

💡 Optional: Log the exception
         except Exception:
+            print(f"Warning: Could not fetch issue #{issue_num}", file=sys.stderr)
             continue
.github/actions/changelog-generator/action.yml (1)

31-33: Consider pinning the google-genai dependency version.

The pip install google-genai command installs the latest version, which could lead to unexpected behavior if the SDK introduces breaking changes.

💡 Pin dependency version
     - name: Install dependencies
       shell: bash
-      run: pip install google-genai
+      run: pip install google-genai==1.0.0

Replace 1.0.0 with the tested version.

.github/workflows/reusable-changelog-bot.yml (1)

24-27: The fallback in the condition is redundant.

The || '@openwisp-bot changelog' fallback at line 27 is unnecessary since trigger-phrase already has a default value defined at line 15. The condition can be simplified.

💡 Simplify condition
     if: |
       github.event_name == 'issue_comment' && 
       github.event.issue.pull_request && 
-      contains(github.event.comment.body, inputs.trigger-phrase || '@openwisp-bot changelog')
+      contains(github.event.comment.body, inputs.trigger-phrase)
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b754e2e and 2374648.

📒 Files selected for processing (7)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • runtests.py
🧰 Additional context used
🧬 Code graph analysis (2)
.github/actions/changelog-generator/test_generate_changelog.py (1)
.github/actions/changelog-generator/generate_changelog.py (9)
  • build_prompt (170-242)
  • call_gemini (145-167)
  • get_env_or_exit (37-43)
  • get_linked_issues (114-142)
  • get_pr_commits (102-111)
  • get_pr_details (66-78)
  • get_pr_diff (81-99)
  • post_github_comment (245-260)
  • main (263-283)
runtests.py (1)
.github/actions/changelog-generator/generate_changelog.py (1)
  • main (263-283)
🪛 Ruff (0.14.14)
.github/actions/changelog-generator/generate_changelog.py

[error] 54-54: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 56-56: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 89-89: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 91-91: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 140-141: try-except-continue detected, consider logging the exception

(S112)


[warning] 140-140: Do not catch blind exception: Exception

(BLE001)


[warning] 161-161: Abstract raise to an inner function

(TRY301)


[warning] 161-161: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 163-163: Consider moving this statement to an else block

(TRY300)


[warning] 165-165: Do not catch blind exception: Exception

(BLE001)


[error] 255-255: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 256-256: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 283-283: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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). (3)
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
🔇 Additional comments (24)
.github/actions/changelog-generator/test_generate_changelog.py (7)

1-22: LGTM! Well-structured test suite.

The test file is well-organized with clear test classes for each function, proper use of mocking, and good coverage of edge cases including empty inputs, missing fields, and error conditions.


24-42: LGTM!

Complete coverage for environment variable validation including the edge case of empty strings.


45-79: LGTM!

Good coverage of PR details extraction with proper handling of missing fields.


112-138: LGTM!

Good coverage of commit extraction including SHA truncation and the 10-commit limit.


141-180: LGTM!

Good coverage of issue linking patterns and limits. The empty body test correctly expects an empty list without needing API mocking.


183-279: LGTM!

Comprehensive test coverage for the Gemini API integration including success paths, configuration validation, and error handling.


282-342: LGTM!

Good coverage of prompt construction including all components and truncation behavior.

.github/actions/changelog-generator/generate_changelog.py (9)

37-43: LGTM!

Clean implementation for required environment variable validation.


46-63: LGTM!

The static analysis warning (S310) about URL scheme audit is a false positive here since the URL is always constructed with the hardcoded https://api.github.com prefix.


66-78: LGTM!

Good use of defensive .get() calls with defaults for safe field extraction.


81-99: LGTM!

Good graceful degradation on error, and sensible truncation to prevent overwhelming the LLM with large diffs.


102-111: LGTM!

Clean extraction of commit information with sensible limits.


145-167: LGTM!

Appropriate error handling for external API calls. The static analysis warnings (TRY301, TRY003, TRY300, BLE001) are stylistic preferences; the current implementation is clear and functional.


170-242: LGTM!

Well-structured prompt with comprehensive instructions for RST changelog generation and sensible truncation limits.


245-260: LGTM!

Proper comment posting implementation. Note that urlopen typically raises HTTPError for 4xx/5xx status codes before reaching the status check, but the check provides additional defense for unexpected non-201 success codes.


263-287: LGTM!

Clean orchestration of the changelog generation flow with proper error propagation.

.github/actions/changelog-generator/action.yml (1)

1-49: LGTM overall!

Well-structured composite action with clear input definitions and proper environment variable mapping.

runtests.py (1)

20-27: LGTM!

Clean extension of pytest test discovery to include the new changelog generator tests.

.github/workflows/changelog-bot.yml (1)

1-15: LGTM!

Clean entry-point workflow that delegates to the reusable workflow with appropriate inputs and secrets.

docs/developer/changelog-bot.rst (1)

1-195: LGTM overall!

Comprehensive documentation covering setup, configuration, usage, troubleshooting, and security considerations. The example RST format and workflow configuration are helpful.

.github/workflows/reusable-changelog-bot.yml (4)

41-56: LGTM!

Good permission validation ensuring only users with write access can trigger changelog generation.


58-65: LGTM!

Efficient use of sparse checkout to fetch only the required action directory.


67-77: LGTM!

Good UX providing immediate feedback via the rocket reaction.


79-87: LGTM!

Proper invocation of the changelog generator action with all required inputs.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from ebf86f4 to 10b3e9e Compare February 3, 2026 12:52
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

🤖 Fix all issues with AI agents
In @.github/actions/changelog-generator/generate_changelog.py:
- Around line 145-167: The long system instruction string in call_gemini exceeds
line length (E501); split or wrap the string passed to
types.GenerateContentConfig.system_instruction (inside the call_gemini function)
into multiple shorter string literals (e.g., use implicit concatenation across
multiple quoted parts or join a list) so the combined instruction remains the
same but no single line exceeds the limit; ensure you only change the string
formatting and keep the same content, model, and other config fields.
- Around line 114-142: The get_linked_issues function can abort the process
because github_api_request raises SystemExit on HTTP/URL errors; update
get_linked_issues to guard calls to github_api_request by catching SystemExit in
addition to Exception (or replace the call with a non-exiting wrapper), so a
failing fetch for any issue_num does not terminate the workflow; modify the
try/except around github_api_request("/repos/{repo}/issues/{issue_num}", token)
in get_linked_issues to handle SystemExit (and continue) and ensure issue_data
failures simply skip that issue.

In @.github/actions/changelog-generator/test_generate_changelog.py:
- Around line 4-21: The test module triggers flake8 E402 because sys.path.insert
runs before importing generate_changelog; fix by suppressing the linter for that
legitimate import order: add "# noqa: E402" to the "from generate_changelog
import ( build_prompt, call_gemini, get_env_or_exit, get_linked_issues,
get_pr_commits, get_pr_details, get_pr_diff, post_github_comment, )" import line
so the linter ignores the post-statement import while keeping sys.path.insert
and the referenced symbols unchanged.
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2374648 and 38a62ed.

📒 Files selected for processing (7)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • runtests.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • .github/workflows/changelog-bot.yml
  • runtests.py
  • .github/actions/changelog-generator/action.yml
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/changelog-generator/test_generate_changelog.py (1)
.github/actions/changelog-generator/generate_changelog.py (9)
  • build_prompt (170-242)
  • call_gemini (145-167)
  • get_env_or_exit (37-43)
  • get_linked_issues (114-142)
  • get_pr_commits (102-111)
  • get_pr_details (66-78)
  • get_pr_diff (81-99)
  • post_github_comment (245-260)
  • main (263-283)
🪛 GitHub Actions: OpenWISP Utils CI Build
.github/actions/changelog-generator/test_generate_changelog.py

[error] 12-12: Flake8: E402 module level import not at top of file.

.github/actions/changelog-generator/generate_changelog.py

[error] 154-154: E501 line too long (174 > 110 characters).

🪛 Ruff (0.14.14)
.github/actions/changelog-generator/generate_changelog.py

[error] 54-54: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 56-56: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 89-89: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 91-91: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 140-141: try-except-continue detected, consider logging the exception

(S112)


[warning] 140-140: Do not catch blind exception: Exception

(BLE001)


[warning] 161-161: Abstract raise to an inner function

(TRY301)


[warning] 161-161: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 163-163: Consider moving this statement to an else block

(TRY300)


[warning] 165-165: Do not catch blind exception: Exception

(BLE001)


[error] 255-255: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 256-256: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 283-283: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (19)
.github/actions/changelog-generator/test_generate_changelog.py (9)

24-42: Good coverage for env var validation.

Covers present, missing, and empty cases cleanly.


45-80: PR detail parsing tests look solid.

Nice coverage for defaults and label extraction.


82-112: Diff retrieval and truncation tests are clear.

Validates both normal and oversized diffs.


114-141: Commit parsing tests are on point.

Covers message truncation and commit limit.


143-183: Linked-issue extraction tests look good.

Covers keyword patterns, max count, and empty body.


185-283: Gemini integration tests are thorough.

Covers model selection, config usage, and error paths.


284-345: Prompt construction tests are comprehensive.

Good coverage for labels, commits, issues, and truncation.


347-375: Comment posting tests look good.

Covers success and non-201 error behavior.


377-378: Main guard is fine.

.github/actions/changelog-generator/generate_changelog.py (10)

1-35: Module header/imports look good.


37-43: Env var guard is clean and direct.


46-63: GitHub API helper is straightforward.


66-78: PR details extraction looks good.


81-99: Diff fetch + truncation is handled well.


102-111: Commit parsing logic is clear.


170-242: Prompt assembly is well structured.


245-261: PR comment posting logic looks good.


263-283: Main flow is clear and cohesive.


286-287: Entry point is fine.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch 5 times, most recently from 2b1b98f to d0d513d Compare February 3, 2026 13:41
@coveralls
Copy link

coveralls commented Feb 3, 2026

Coverage Status

coverage: 97.249%. remained the same
when pulling cc4dfb1 on pushpitkamboj:feature/AI_created_changelog
into 24e9775 on openwisp:master.

@atif09
Copy link

atif09 commented Feb 3, 2026

A comment by code rabbit hasn't been resolved, apply the changes if you think theyre worth addressing, or just mention why you dont agree to it by replying to it

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from 9efcef1 to 5f1333c Compare February 3, 2026 15:42
How It Works
------------

1. A maintainer approves a PR or comments ``@openwisp-bot changelog``
Copy link
Member

Choose a reason for hiding this comment

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

Let's make sure the following points are true, covered by unit tests and documented:

  • If PR is approved and @openwisp-bot changelog was explicitly called after the latest changes were pushed, don't do anything
  • If PR is approved and @openwisp-bot changelog was not explicitly called after the latest changes were pushed, the action is triggered only if the PR title contains: [feature, [fix or [change - I think we can skip this for [deps as they don't require much change log writing
  • If @openwisp-bot changelog is called, it will execute regardless of the PR title

BTW: approved by who? Anyone? No, it should be triggered only when approved by a member of the @openwisp organization.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the maintainer of the repo, yes that I have done. Basically who has the write access to the openwisp's repos.
Line 43 - 48 in the .github/workflows/reusable-changelog-bot.py has this logic

Copy link
Contributor Author

@pushpitkamboj pushpitkamboj Feb 3, 2026

Choose a reason for hiding this comment

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

yes 3rd point has been implemented, the changeling will be generated regardless of PR title, but I will also add a test case which covers this case for safe side.

  • if I understand point 2 correctly you want the log to be generated automatically without mentioning @openwisp-bot changelog if the PR title contains feature, fix, change ?

  • I will have to fix the logic to follow point 1, that was a nice catch!

freezegun
pytest
pytest-mock
google-genai
Copy link
Member

Choose a reason for hiding this comment

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

runtests.py Outdated
pytest.main(
[
os.path.join("openwisp_utils/releaser/tests"),
os.path.join(".github/actions/changelog-generator"),
Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

anything specific which you want to me to look in #562 in relation to runtests.py file?
If not, yes I am reading the changes to understand the PR in general

@nemesifier nemesifier added the github_actions Pull requests that update GitHub Actions code label Feb 3, 2026
@github-project-automation github-project-automation bot moved this from To do (general) to In progress in OpenWISP Contributor's Board Feb 3, 2026
@nemesifier nemesifier added the releaser Related to the OpenWISP Release Tool label Feb 3, 2026
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

🤖 Fix all issues with AI agents
In @.github/actions/changelog-generator/generate_changelog.py:
- Around line 66-78: The PR body returned by get_pr_details can be null which
later causes get_linked_issues to crash when calling .lower(); update
get_pr_details to coalesce the PR body to an empty string (e.g., use
pr_data.get("body") or "") when constructing the returned dict so "body" is
never None, and optionally add a defensive check in get_linked_issues to handle
empty or non-string bodies before calling .lower(); reference get_pr_details and
get_linked_issues when applying the change.
- Around line 175-247: The prompt in build_prompt does not supply a canonical
repository/PR URL so the LLM may hallucinate links; update build_prompt to read
the PR URL from pr_details (e.g., pr_details['html_url'] or a repo_url derived
from pr_details) and inject that into the prompt so the RST link rule references
the real URL variable, and update the RULE 4 example/reference to use that
variable (e.g., replace <repo_url> with the injected pr_html_url) so the model
always uses the provided canonical PR URL when rendering `#<number> <...>`
links.

In @.github/workflows/reusable-changelog-bot.yml:
- Around line 39-55: Add a review-approval gate to the existing check-permission
step: use the GitHub API to list PR reviews for the pull request (e.g., via
github.rest.pulls.listReviews using context.repo and context.issue.number),
compute a boolean hasApproval by checking if any review.state === 'APPROVED',
and require hasApproval along with hasAccess before proceeding; if not approved
call core.setFailed('Pull request must be approved to trigger changelog
generation') (retain existing hasAccess logic and variable names like hasAccess,
hasApproval, and the step id check-permission to locate where to insert the
check).
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 38a62ed and 5c11cbc.

📒 Files selected for processing (8)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • requirements-test.txt
  • runtests.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/developer/changelog-bot.rst
  • .github/workflows/changelog-bot.yml
  • runtests.py
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/changelog-generator/test_generate_changelog.py (1)
.github/actions/changelog-generator/generate_changelog.py (9)
  • build_prompt (175-247)
  • call_gemini (146-172)
  • get_env_or_exit (37-43)
  • get_linked_issues (114-143)
  • get_pr_commits (102-111)
  • get_pr_details (66-78)
  • get_pr_diff (81-99)
  • post_github_comment (250-265)
  • main (268-288)
🪛 Ruff (0.14.14)
.github/actions/changelog-generator/generate_changelog.py

[error] 54-54: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 56-56: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 89-89: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 91-91: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 140-142: try-except-continue detected, consider logging the exception

(S112)


[warning] 140-140: Do not catch blind exception: Exception

(BLE001)


[warning] 166-166: Abstract raise to an inner function

(TRY301)


[warning] 166-166: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 168-168: Consider moving this statement to an else block

(TRY300)


[warning] 170-170: Do not catch blind exception: Exception

(BLE001)


[error] 260-260: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 261-261: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 288-288: Avoid specifying long messages outside the exception class

(TRY003)

.github/actions/changelog-generator/test_generate_changelog.py

[warning] 10-10: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 11-11: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 13-13: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)

⏰ 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). (12)
  • GitHub Check: Python==3.10 | django~=5.0.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.0.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
🔇 Additional comments (19)
requirements-test.txt (1)

1-6: LGTM.
Adds the test dependency needed by the new generator tests.

.github/actions/changelog-generator/generate_changelog.py (8)

37-43: LGTM.
Straightforward env validation and exit behavior.


46-63: LGTM.
Clear error handling for GitHub API failures.


81-99: LGTM.
Diff truncation keeps prompt size bounded without failing the run.


102-111: LGTM.
Commit summary and 10‑item cap are sensible.


114-143: LGTM.
Issue extraction is clear and appropriately capped.


146-173: LGTM.
Good handling for empty responses and API errors.


250-265: LGTM.
Posting logic is straightforward and fails fast on non‑201 responses.


268-288: LGTM.
Clear orchestration of the end‑to‑end flow.

.github/actions/changelog-generator/action.yml (1)

1-45: LGTM.
Inputs are mapped cleanly to the generator’s expected environment variables.

.github/workflows/reusable-changelog-bot.yml (1)

56-84: LGTM.
Sparse checkout and the reaction acknowledgment look good.

.github/actions/changelog-generator/test_generate_changelog.py (8)

25-44: LGTM.
Covers success and failure paths for env handling.


46-81: LGTM.
Good coverage for PR details defaults and parsing.


83-113: LGTM.
Diff retrieval and truncation behavior are well tested.


115-142: LGTM.
Commit parsing and the 10‑item cap are validated.


144-184: LGTM.
Linked‑issue extraction cases are covered, including limits.


186-283: LGTM.
Gemini call behavior is thoroughly mocked and asserted.


285-347: LGTM.
Prompt construction tests cover labels, commits, issues, and truncation.


348-379: LGTM.
Posting behavior and non‑201 failures are validated.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@nemesifier nemesifier changed the title [feature] Add changelog bot to generate RST entries using Gemini AI. [feature/bots] Add changelog bot to generate RST entries using Gemini AI. Feb 3, 2026
@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from 5c11cbc to 0ac1017 Compare February 4, 2026 07:04
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 4, 2026
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In @.github/actions/changelog-generator/test_generate_changelog.py:
- Around line 4-22: Remove the unused "# noqa: E402" suppressions in the test
file: delete the E402 noqa markers on the unittest, unittest.mock, and
generate_changelog import lines (the top sys.path manipulation makes them
unnecessary). Update the imports to be plain (e.g., "import unittest", "from
unittest.mock import ...", and "from generate_changelog import ...") so Ruff no
longer reports RUF100; if you prefer keeping a suppression, instead add a
per-file Ruff ignore for E402 at the top of the file.
🧹 Nitpick comments (1)
.github/actions/changelog-generator/generate_changelog.py (1)

114-143: Make linked-issue selection deterministic.
Using a set makes issue selection order unpredictable; sorting before slicing keeps prompts stable.

♻️ Proposed fix
-    for issue_num in list(issue_numbers)[:3]:  # Limit to 3 issues
+    for issue_num in sorted(issue_numbers, key=int)[:3]:  # Limit to 3 issues
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5c11cbc and 0ac1017.

📒 Files selected for processing (8)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • requirements-test.txt
  • runtests.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • docs/developer/changelog-bot.rst
  • .github/workflows/changelog-bot.yml
  • runtests.py
  • requirements-test.txt
  • .github/actions/changelog-generator/action.yml
🧰 Additional context used
🧬 Code graph analysis (1)
.github/actions/changelog-generator/test_generate_changelog.py (1)
.github/actions/changelog-generator/generate_changelog.py (9)
  • build_prompt (175-250)
  • call_gemini (146-172)
  • get_env_or_exit (37-43)
  • get_linked_issues (114-143)
  • get_pr_commits (102-111)
  • get_pr_details (66-78)
  • get_pr_diff (81-99)
  • post_github_comment (253-268)
  • main (271-291)
🪛 Ruff (0.14.14)
.github/actions/changelog-generator/generate_changelog.py

[error] 54-54: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 56-56: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 89-89: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 91-91: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 140-142: try-except-continue detected, consider logging the exception

(S112)


[warning] 140-140: Do not catch blind exception: Exception

(BLE001)


[warning] 166-166: Abstract raise to an inner function

(TRY301)


[warning] 166-166: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 168-168: Consider moving this statement to an else block

(TRY300)


[warning] 170-170: Do not catch blind exception: Exception

(BLE001)


[error] 263-263: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 264-264: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 291-291: Avoid specifying long messages outside the exception class

(TRY003)

.github/actions/changelog-generator/test_generate_changelog.py

[warning] 10-10: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 11-11: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 13-13: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)

⏰ 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). (14)
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.0.0
  • GitHub Check: Python==3.10 | django~=5.0.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.11 | django~=5.0.0
  • GitHub Check: Python==3.11 | django~=4.2.0
🔇 Additional comments (25)
.github/actions/changelog-generator/generate_changelog.py (11)

1-24: Clear module documentation.
Nicely documents usage, env vars, and changelog format expectations.


37-43: Solid env guard.
Straightforward validation and exit path.


46-63: API wrapper looks good.
Error handling and timeouts are clear.


66-78: PR details normalization is tidy.
Reasonable defaults and defensive coalescing.


81-99: Diff fetch with truncation is sensible.
Keeps prompt size bounded without hard-failing.


102-111: Commit parsing is clean.
Limits and message extraction are straightforward.


146-172: Response validation and error handling look fine.
Nice guard for empty responses.


175-250: Prompt assembly is well structured.
Clear sections and rules for consistent output.


253-268: Comment posting flow is straightforward.
Simple and clear response validation.


271-291: Main flow wiring looks good.
Good end-to-end sequence.


294-295: Entry-point hook is fine.
Standard and clear.

.github/actions/changelog-generator/test_generate_changelog.py (9)

25-44: Env handling tests are solid.
Covers presence, missing, and empty values.


46-81: PR detail tests look comprehensive.
Covers both populated and missing fields.


83-113: Diff tests cover truncation well.
Good validation of large diff handling.


115-142: Commit parsing tests are clear.
Limits and message extraction are validated.


144-184: Linked-issue extraction tests are thorough.
Good coverage of patterns and limits.


186-283: Gemini call tests are well mocked.
Nice coverage of config, errors, and outputs.


285-347: Prompt construction tests are clear.
Checks labels, commits, issues, and body truncation.


348-376: Comment posting tests look good.
Covers success and non‑201 failures.


378-379: Test runner hook is fine.
Standard unittest entry point.

.github/workflows/reusable-changelog-bot.yml (5)

1-20: Inputs and secrets are clearly defined.
Defaults and required secret look good.


21-33: Job guard and permissions look appropriate.
Clear trigger gating and scoped permissions.


35-37: PR number extraction is straightforward.
Simple and readable.


39-99: Pre-checks are comprehensive.
Covers merge status, recent changes, permissions, and approvals.


100-128: Checkout/reaction/generation sequence looks good.
Flow is clear and easy to follow.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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

🤖 Fix all issues with AI agents
In @.github/actions/changelog-generator/generate_changelog.py:
- Around line 175-249: The prompt in build_prompt constructs Rule 4 and the
example using four backticks for RST inline code (e.g. the substring showing
"````code````" inside the prompt variable) which is invalid; update the prompt
string in build_prompt to use the correct RST inline markup (double backticks)
wherever four backticks appear (both in the "RST FORMAT RULES" bullet and the
example output block) so the LLM emits valid RST; ensure you edit the prompt
variable inside the build_prompt function and keep the rest of the formatting
unchanged.

In @.github/workflows/reusable-changelog-bot.yml:
- Around line 57-73: The current pulls.listCommits call paginates and only
returns the first page (missing latest commit on large PRs); replace that logic
by fetching the PR head commit directly using the PR head SHA (use pr.head.sha)
and retrieve its commit date (via github.rest.repos.getCommit or
github.rest.git.getCommit) to compare against context.payload.comment.created_at
(commentCreatedAt); update the code around pulls.listCommits and
latestCommitDate to call the single-commit API with pr.head.sha, parse
commit.committer.date into latestCommitDate, and keep the existing
comparison/failed message if latestCommitDate > commentCreatedAt, with a
fallback if the commit lookup fails.
- Around line 86-97: The current approval check uses
github.rest.pulls.listReviews and the hasApproval boolean (reviews.some(...))
which looks at review history; replace this with a GraphQL query for the
pullRequest.reviewDecision and use that aggregated field to gate changelog
generation: remove the listReviews call and hasApproval logic, fetch the pull
request via GitHub GraphQL (query pullRequest(number: ...) { reviewDecision })
and only proceed when reviewDecision === "APPROVED" (treat other values like
"CHANGES_REQUESTED"/"REVIEW_REQUIRED"/null as not approved), and update any
logging/messages to reference reviewDecision instead of listReviews.
🧹 Nitpick comments (2)
.github/actions/changelog-generator/generate_changelog.py (1)

114-143: Consider logging skipped issue fetches for traceability.
Swallowing all exceptions makes debugging harder when linked issues fail.

💡 Suggested tweak
-        except (Exception, SystemExit):
-            # Skip issues that fail to fetch (including SystemExit from github_api_request)
-            continue
+        except (Exception, SystemExit) as e:
+            # Skip issues that fail to fetch (including SystemExit from github_api_request)
+            print(f"Warning: skipped linked issue #{issue_num}: {e}", file=sys.stderr)
+            continue
.github/actions/changelog-generator/action.yml (1)

31-33: Pin google-genai for reproducible action runs.
Unpinned installs can break CI when upstream releases introduce changes.

🔧 Suggested change (plus add a pinned requirements file)
-      run: pip install google-genai
+      run: pip install -r ${{ github.action_path }}/requirements.txt

Please choose and pin a specific version (or narrow range) in requirements.txt after confirming the latest stable release.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ac1017 and 57107b7.

📒 Files selected for processing (8)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • requirements-test.txt
  • runtests.py
✅ Files skipped from review due to trivial changes (1)
  • docs/developer/changelog-bot.rst
🚧 Files skipped from review as they are similar to previous changes (2)
  • requirements-test.txt
  • runtests.py
🧰 Additional context used
🪛 Ruff (0.14.14)
.github/actions/changelog-generator/generate_changelog.py

[error] 54-54: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 56-56: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 89-89: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 91-91: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 140-142: try-except-continue detected, consider logging the exception

(S112)


[warning] 140-140: Do not catch blind exception: Exception

(BLE001)


[warning] 166-166: Abstract raise to an inner function

(TRY301)


[warning] 166-166: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 168-168: Consider moving this statement to an else block

(TRY300)


[warning] 170-170: Do not catch blind exception: Exception

(BLE001)


[error] 263-263: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 264-264: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 291-291: Avoid specifying long messages outside the exception class

(TRY003)

.github/actions/changelog-generator/test_generate_changelog.py

[warning] 10-10: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 11-11: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 13-13: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)

🔇 Additional comments (20)
.github/actions/changelog-generator/generate_changelog.py (9)

1-24: Clear module overview and usage notes.
Docstring is precise and sets expectations well.


37-43: Env validation helper looks solid.
Direct, clear failure mode for missing config.


46-63: GitHub API wrapper is straightforward and consistent.
Headers, timeout, and error handling are appropriate for a CLI script.


66-78: PR detail parsing is safe and defensive.
Defaults and null-coalescing prevent downstream errors.


81-99: Diff retrieval with truncation is well handled.
Graceful fallback when the diff is unavailable keeps the flow resilient.


102-111: Commit summary extraction is clean and bounded.
Limiting to 10 and truncating SHA keeps prompts compact.


146-173: Gemini call setup is clear and appropriately guarded.
Good use of system instruction and bounded generation parameters.


253-268: Comment posting logic is concise and correct.
Explicitly checking for 201 keeps failures visible.


271-295: Main flow wiring is clean and readable.
The sequence is easy to follow and matches the workflow intent.

.github/actions/changelog-generator/action.yml (2)

1-22: Inputs and metadata are well structured.
Clear inputs make the action self-documenting.


35-45: Runtime wiring to the generator looks correct.
Env mapping is complete and consistent with the script.

.github/actions/changelog-generator/test_generate_changelog.py (6)

25-81: Env/PR detail tests are thorough and well scoped.
Covers happy paths and missing-field defaults.


83-143: Diff/commit tests cover truncation and bounds nicely.
Good balance of functional checks without overfitting.


144-184: Linked-issue extraction tests look solid.
Patterns and limit behavior are exercised.


186-284: Gemini integration tests are comprehensive.
Mocks validate model selection, config, and error handling.


285-347: Prompt construction tests are well targeted.
Validates labels, commits, issues, and truncation.


348-376: PR comment posting tests are succinct and effective.
Covers both success and failure paths.

.github/workflows/reusable-changelog-bot.yml (2)

21-33: Permissions are scoped appropriately.
Good minimum access for PR commenting.


100-128: Checkout and action invocation wiring looks correct.
Sparse checkout and composite action usage are clean.

.github/workflows/changelog-bot.yml (1)

1-15: Wrapper workflow is concise and clear.
Delegation to the reusable workflow is clean.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from 57107b7 to e9f7290 Compare February 4, 2026 07:36
coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 4, 2026
@pushpitkamboj
Copy link
Contributor Author

  • addressed all the issues by coderabbit, implemented the point 1 and 3 changes @nemesifier suggested about here
  • Can you more clarity about point2 which is -> If PR is approved and @openwisp-bot changelog was not explicitly called after the latest changes were pushed, the action is triggered only if the PR title contains: [feature, [fix or [change - I think we can skip this for [deps as they don't require much change log writing

@nemesifier
Copy link
Member

  • addressed all the issues by coderabbit, implemented the point 1 and 3 changes @nemesifier suggested about here
  • Can you more clarity about point2 which is -> If PR is approved and @openwisp-bot changelog was not explicitly called after the latest changes were pushed, the action is triggered only if the PR title contains: [feature, [fix or [change - I think we can skip this for [deps as they don't require much change log writing

Thre's two ways to trigger this right?

  1. Explicitly by maintainers when they request it by mentioning the bot, right? My comment DOES NOT refer to this option.
  2. Implicitly when a PR is flagged as approved by a maintainer (member of the org) My comment refers to this option.

We must avoid triggering the action when it is not needed. For example:

  • Another maintainer has already approved, there were no new changes and the bot has already sent a suggestion
  • The PR has just been approved, but it's not relevant to the change log, because its title starts [chores, [deps, [tests, these minor changes are not included in the change log by the releaser tool. We only include commits starting with [feature, [fix or [change, these are the noteworthy changes that need to be carefully explained.

Is it clearer now?

@pushpitkamboj
Copy link
Contributor Author

pushpitkamboj commented Feb 6, 2026

  • cool, I will change the logic such that bot automatically comments the changelogs after the PR has been approved. And yes it will only do if the PR has the said tags!

  • And in case on a day u want to generate changelogs for the pr which is none of [feature, [fix or [change then u can just write a comment @openwisp-bot changelog comment and then only bot will be triggered.

}
req = Request(url, headers=headers)
try:
with urlopen(req, timeout=30) as response:
Copy link
Member

Choose a reason for hiding this comment

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

here we should use retryable_request.

}


def get_pr_diff(repo: str, pr_number: int, token: str) -> str:
Copy link
Member

Choose a reason for hiding this comment

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

in the CI we have the git repo, getting the diff should be a matter of just doing:

git diff $GITHUB_BASE_REF

client = genai.Client(api_key=api_key)

try:
response = client.models.generate_content(
Copy link
Member

Choose a reason for hiding this comment

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

let's make sure HTTP requests are retried if they fail

return ""


def get_pr_commits(repo: str, pr_number: int, token: str) -> list:
Copy link
Member

Choose a reason for hiding this comment

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

I think this is also something I believe could be solved with a git command, providing faster execution and less prone to temporary network errors.

}

result = get_linked_issues("org/repo", "Closes #123", "token")

Copy link
Member

Choose a reason for hiding this comment

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

Too many needless blank lines.

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

🤖 Fix all issues with AI agents
In @.github/actions/changelog-generator/action.yml:
- Around line 31-33: Update the "Install dependencies" step in action.yml (the
step with name "Install dependencies" and run command "pip install
google-genai") to pin the package to a known-good version by changing the pip
install invocation to include a specific version (e.g., pip install
google-genai==1.62.0) so the action uses the tested release and avoids future
breakages.

In @.github/actions/changelog-generator/generate_changelog.py:
- Around line 114-127: The get_linked_issues function is over-catching because
the third pattern r"#(\d+)" will match any `#number` reference (including PRs or
incidental mentions) and body_lower = pr_body.lower() is redundant with
re.IGNORECASE; fix by removing or narrowing the third pattern (e.g., drop
r"#(\d+)" entirely or replace it with a context-restricted pattern that requires
explicit verbs like "(?:see|related to)\s+#(\d+)" or word boundaries to avoid
generic matches) and stop lowercasing the PR body (use pr_body directly) since
re.IGNORECASE is already used; update the patterns list and remove body_lower
usage accordingly in get_linked_issues.

In @.github/workflows/reusable-changelog-bot.yml:
- Line 119: Fix the typo in the workflow comment string that reads "send rocket
imoji to the comment to acknowledge, before calling the generator changelog
logic" by changing "imoji" to "emoji" so the comment reads "send rocket emoji to
the comment to acknowledge, before calling the generator changelog logic";
update the corresponding comment in the reusable-changelog-bot.yml workflow (the
comment line containing "send rocket imoji...") accordingly.
🧹 Nitpick comments (3)
.github/actions/changelog-generator/test_generate_changelog.py (1)

334-345: Weak assertion for body truncation test.

The test only asserts that the full 3000-char string is absent, but doesn't positively verify the truncated (2000-char) version is present. Consider adding a positive assertion:

💡 Suggested improvement
         result = build_prompt(pr_details, "", [], [])
 
         # Body should be truncated to 2000 chars
         self.assertNotIn("x" * 3000, result)
+        self.assertIn("x" * 2000, result)
.github/actions/changelog-generator/generate_changelog.py (2)

253-268: urlopen raises HTTPError for 4xx/5xx, so the status check on line 265 is misleading.

urlopen will raise an HTTPError exception for non-2xx responses (e.g., 403, 500) before entering the with block body. The if response.status != 201 guard would only trigger for unexpected 2xx codes (200, 204, etc.), which is an unlikely edge case for this endpoint.

This means:

  • The test test_raises_on_non_201_status (which mocks urlopen to return status 403 without raising) doesn't reflect real urlopen behavior.
  • In production, 4xx/5xx errors bubble up as HTTPError from the urlopen call itself, which main() already catches.

The current code isn't broken, but the status check gives a false sense of coverage. Consider either removing it or wrapping urlopen in a try/except for HTTPError to handle error responses explicitly.


37-63: github_api_request calling sys.exit(1) makes the function difficult to test and reuse.

This is mitigated in get_linked_issues by catching SystemExit, but it's a code smell. For a script like this it's acceptable, but if this module grows, consider raising exceptions instead and letting main() handle exits.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57107b7 and 9fbc2c7.

📒 Files selected for processing (7)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • requirements-test.txt
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/changelog-bot.yml
  • requirements-test.txt
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-04T07:19:40.541Z
Learnt from: pushpitkamboj
Repo: openwisp/openwisp-utils PR: 584
File: .github/actions/changelog-generator/test_generate_changelog.py:4-22
Timestamp: 2026-02-04T07:19:40.541Z
Learning: In `.github/actions/changelog-generator/test_generate_changelog.py`, the sys.path manipulation before imports and use of absolute imports is intentional and preferred for readability, even though relative imports could work.

Applied to files:

  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/actions/changelog-generator/generate_changelog.py
🧬 Code graph analysis (1)
.github/actions/changelog-generator/test_generate_changelog.py (1)
.github/actions/changelog-generator/generate_changelog.py (9)
  • build_prompt (175-250)
  • call_gemini (146-172)
  • get_env_or_exit (37-43)
  • get_linked_issues (114-143)
  • get_pr_commits (102-111)
  • get_pr_details (66-78)
  • get_pr_diff (81-99)
  • post_github_comment (253-268)
  • main (271-291)
🪛 Ruff (0.14.14)
.github/actions/changelog-generator/test_generate_changelog.py

[warning] 10-10: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 11-11: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 13-13: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)

.github/actions/changelog-generator/generate_changelog.py

[error] 54-54: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 56-56: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 89-89: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 91-91: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 140-142: try-except-continue detected, consider logging the exception

(S112)


[warning] 140-140: Do not catch blind exception: Exception

(BLE001)


[warning] 166-166: Abstract raise to an inner function

(TRY301)


[warning] 166-166: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 168-168: Consider moving this statement to an else block

(TRY300)


[warning] 170-170: Do not catch blind exception: Exception

(BLE001)


[error] 263-263: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[error] 264-264: Audit URL open for permitted schemes. Allowing use of file: or custom schemes is often unexpected.

(S310)


[warning] 291-291: Avoid specifying long messages outside the exception class

(TRY003)

⏰ 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). (14)
  • GitHub Check: Python==3.12 | django~=4.2.0
  • GitHub Check: Python==3.11 | django~=5.1.0
  • GitHub Check: Python==3.12 | django~=5.1.0
  • GitHub Check: Python==3.13 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=4.2.0
  • GitHub Check: Python==3.13 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.2.0
  • GitHub Check: Python==3.12 | django~=5.0.0
  • GitHub Check: Python==3.11 | django~=5.2.0
  • GitHub Check: Python==3.10 | django~=5.2.0
  • GitHub Check: Python==3.11 | django~=4.2.0
  • GitHub Check: Python==3.10 | django~=5.1.0
  • GitHub Check: Python==3.10 | django~=5.0.0
  • GitHub Check: Python==3.11 | django~=5.0.0
🔇 Additional comments (6)
docs/developer/changelog-bot.rst (1)

1-185: Documentation is well-structured and covers the key aspects.

Clear sections for setup, configuration, usage, troubleshooting, and security. The RST formatting and examples look correct.

.github/actions/changelog-generator/test_generate_changelog.py (1)

25-376: Good coverage of the public API surface.

Tests cover environment handling, PR detail parsing, diff truncation, commit limits, linked issue extraction, Gemini integration (including error/empty responses), prompt construction, and comment posting. Nice work.

.github/actions/changelog-generator/generate_changelog.py (1)

175-250: Prompt construction is well-structured.

The prompt includes clear rules, proper RST format guidance, the canonical PR URL, and an example. The truncation of body to 2000 chars and diff to 15000 chars are sensible limits.

.github/actions/changelog-generator/action.yml (1)

1-49: Action definition looks good overall.

Inputs are well-defined with sensible defaults, and the composite steps are straightforward.

.github/workflows/reusable-changelog-bot.yml (2)

23-26: Verify the || fallback behavior in the contains() expression.

Line 26: contains(github.event.comment.body, inputs.trigger-phrase || '@openwisp-bot changelog') — this uses || as a null-coalescing operator. In GitHub Actions expressions, this should work correctly: if inputs.trigger-phrase is provided (non-empty), it's used; otherwise the default literal is used. Since the input also has default: "@openwisp-bot changelog", the || fallback is technically redundant but acts as a safety net. Looks correct.


34-138: Workflow preconditions are thorough and well-implemented.

The checks cover: PR not merged, no post-trigger pushes (using head SHA to avoid pagination), write-access verification, and GraphQL-based approval status. The sparse checkout, rocket reaction, and action invocation are cleanly wired.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from 9fbc2c7 to 38e0e4f Compare February 9, 2026 06:27
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

🤖 Fix all issues with AI agents
In @.github/actions/changelog-generator/generate_changelog.py:
- Around line 311-314: The try/except in main() is catching HTTPError and
URLError but post_github_comment raises RuntimeError (via
retryable_request/requests), so update the exception handling to catch
RuntimeError (or include RuntimeError alongside HTTPError, URLError) when
calling post_github_comment in main(); also remove the unused imports
urllib.error and urllib.request from the top of the file. Locate
post_github_comment and main() to change the except clause to catch the correct
exception types and ensure the raised RuntimeError is handled, and delete the
unused urllib imports.

In @.github/workflows/reusable-changelog-bot.yml:
- Around line 35-43: The step "Check for noteworthy commits" (id: check-commits)
is inspecting commit messages but needs to check the PR title; update the step
to read github.event.pull_request.title and test it against the regex for titles
starting with "[feature", "[fix", or "[change" (case-sensitive as required),
then set the output has_noteworthy=true or false accordingly; keep the existing
outputs (pr_number and has_noteworthy) and replace the git log/grep logic with a
conditional that matches the PR title.
🧹 Nitpick comments (1)
.github/actions/changelog-generator/test_generate_changelog.py (1)

299-300: Consider adding a test for build_prompt with html_url missing or empty.

build_prompt uses pr_details.get("html_url") or "" — a test verifying the prompt is still valid when html_url is absent would strengthen coverage. The current test_builds_basic_prompt always provides it.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fbc2c7 and 38e0e4f.

📒 Files selected for processing (8)
  • .github/actions/changelog-generator/action.yml
  • .github/actions/changelog-generator/generate_changelog.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/workflows/changelog-bot.yml
  • .github/workflows/reusable-changelog-bot.yml
  • docs/developer/changelog-bot.rst
  • requirements-test.txt
  • runtests.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • requirements-test.txt
  • .github/workflows/changelog-bot.yml
  • docs/developer/changelog-bot.rst
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2026-02-04T07:19:40.541Z
Learnt from: pushpitkamboj
Repo: openwisp/openwisp-utils PR: 584
File: .github/actions/changelog-generator/test_generate_changelog.py:4-22
Timestamp: 2026-02-04T07:19:40.541Z
Learning: In `.github/actions/changelog-generator/test_generate_changelog.py`, the sys.path manipulation before imports and use of absolute imports is intentional and preferred for readability, even though relative imports could work.

Applied to files:

  • runtests.py
  • .github/actions/changelog-generator/test_generate_changelog.py
  • .github/actions/changelog-generator/generate_changelog.py
🧬 Code graph analysis (1)
.github/actions/changelog-generator/test_generate_changelog.py (1)
.github/actions/changelog-generator/generate_changelog.py (9)
  • build_prompt (190-265)
  • call_gemini (161-187)
  • get_env_or_exit (39-45)
  • get_linked_issues (129-158)
  • get_pr_commits (107-126)
  • get_pr_details (74-86)
  • get_pr_diff (89-104)
  • post_github_comment (268-290)
  • main (293-314)
🪛 Ruff (0.14.14)
.github/actions/changelog-generator/test_generate_changelog.py

[warning] 10-10: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 11-11: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)


[warning] 13-13: Unused noqa directive (non-enabled: E402)

Remove unused noqa directive

(RUF100)

.github/actions/changelog-generator/generate_changelog.py

[error] 92-92: subprocess call: check for execution of untrusted input

(S603)


[error] 93-93: Starting a process with a partial executable path

(S607)


[warning] 101-101: Consider moving this statement to an else block

(TRY300)


[error] 110-110: subprocess call: check for execution of untrusted input

(S603)


[error] 111-111: Starting a process with a partial executable path

(S607)


[warning] 123-123: Consider moving this statement to an else block

(TRY300)


[error] 155-157: try-except-continue detected, consider logging the exception

(S112)


[warning] 155-155: Do not catch blind exception: Exception

(BLE001)


[warning] 181-181: Abstract raise to an inner function

(TRY301)


[warning] 181-181: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 183-183: Consider moving this statement to an else block

(TRY300)


[warning] 185-185: Do not catch blind exception: Exception

(BLE001)


[warning] 290-290: Avoid specifying long messages outside the exception class

(TRY003)


[warning] 314-314: Avoid specifying long messages outside the exception class

(TRY003)

🔇 Additional comments (12)
runtests.py (1)

20-27: LGTM!

The sys.exit(pytest.main([...])) pattern correctly propagates the test exit code, and the new changelog-generator test path is properly included.

.github/actions/changelog-generator/generate_changelog.py (5)

39-45: LGTM!

Simple and clear environment variable validation with appropriate exit behavior.


48-71: LGTM!

Good use of retryable_request with sensible retry configuration for GitHub API calls.


74-86: LGTM!

Null body is properly coalesced with or "" on line 79.


246-264: Minor: prompt example section header underline length doesn't match.

In the prompt template, the example shows ~~~~~~~~ (8 tildes) under "Features" (8 chars), which is correct. However, looking at the CHANGE TYPE SECTIONS in the rules (lines 239-244), "Breaking Changes" and "Dependencies" have different lengths. The LLM should handle this, but worth noting the example only demonstrates one section type.


268-290: LGTM on post_github_comment implementation.

Good use of retryable_request and proper exception chaining with from e.

.github/actions/changelog-generator/test_generate_changelog.py (3)

284-297: LGTM — good coverage of build_prompt.

Tests cover basic prompt construction, commits, issues, empty body, and long body truncation.


336-366: LGTM — post_github_comment tests cover success and error paths.


192-278: LGTM — thorough Gemini integration tests.

Good coverage of model selection, system instructions, generation config, empty response, and API error scenarios.

.github/actions/changelog-generator/action.yml (1)

44-45: LGTM — proper use of github.action_path for script resolution.

.github/workflows/reusable-changelog-bot.yml (2)

19-21: LGTM — approval gate is correctly implemented.

The if condition properly checks for pull_request_review event with approved state.


29-33: LGTM — full clone with fetch-depth: 0 is needed for diff/log operations.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch 2 times, most recently from 489322f to d413754 Compare February 9, 2026 07:18
@pushpitkamboj
Copy link
Contributor Author

I have made a lot of changes now since the last commit.

  • kept only 1 workflow which gets triggered when the PR is approved by the maintainer and the PR title has one of feature, fix, change; so changed the yml accordingly.
  • simplified context gathering about the PR(for LLM) using git commands wherever possible.
  • used retryable_request for api call to github.
  • Also updated the documentation in docs/developer accordingly.

Its ready for review. @nemesifier

coderabbitai[bot]
coderabbitai bot previously approved these changes Feb 9, 2026
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

Great progress @pushpitkamboj 👍

I spotted a few more details to improve. But it's on the right track.

- The bot only triggers on PR approval events
- API keys are stored as GitHub secrets (encrypted)
- The bot cannot modify code, only post comments
- PR diffs are truncated to prevent sending too much data to the LLM
Copy link
Member

Choose a reason for hiding this comment

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

This is really too long and bloated, nobody will read this, not even I. We already have a dedicated page for github actions, we just need to mention this bot there briefly and add basic instructions to use the github actions in the different openwisp repos.

Please refrain from autogenerating this with AI.

Write it by hand, then ask AI to revise. If you don't do this, we'll end up with this kind of bloat nobody wants to deal with.

url=url,
timeout=(4, 8),
max_retries=3,
backoff_factor=1,
Copy link
Member

Choose a reason for hiding this comment

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

wait more time on failures: 3 seconds, applies to all occurrences of retryable_request

method="post",
url=url,
timeout=(4, 8),
max_retries=3,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
max_retries=3,
max_retries=5,

if pr_details["labels"]:
labels_text = f"\nLabels: {', '.join(pr_details['labels'])}"

prompt = f"""Analyze this Pull Request and generate a changelog entry in RestructuredText format.
Copy link
Member

Choose a reason for hiding this comment

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

Actually some repos use markdown for practical reasons, eg: change log needs to be included in npm/ansible galaxy/other platforms which expect markdown.

Can you check whether CHANGES.rst or CHANGES.md exist and describe the format accordingly?

raise ValueError("Empty response from Gemini")

return response.text

Copy link
Member

Choose a reason for hiding this comment

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

don't leave so many blank lines

max_output_tokens=1000,
),
)

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

def call_gemini(prompt: str, api_key: str, model: str = "gemini-2.0-flash") -> str:
"""Call Google Gemini API to generate changelog using google-genai SDK."""
client = genai.Client(api_key=api_key)

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

for pattern in patterns:
matches = re.findall(pattern, pr_body, re.IGNORECASE)
issue_numbers.update(matches)

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change

timeout=(4, 8),
max_retries=3,
backoff_factor=1,
backoff_jitter=0.0,
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
backoff_jitter=0.0,
backoff_jitter=0.5,

Applies to other occurrences.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch 5 times, most recently from 5c77d2d to 22f33bc Compare February 10, 2026 09:38
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

@pushpitkamboj the commit message looks very wrong:

Image

I fixed the issues with commitizen in #593.
If the CI build fails for reasons that are not related to your changes you don't have to fight with it and definitely must not try to bundle unrelated changes. Please keep in mind.

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from ba9c297 to 17bc238 Compare February 11, 2026 03:44
@pushpitkamboj
Copy link
Contributor Author

understood, sorry for this.
reverted the unrelated changes.
please review

@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from 81f7380 to 984c837 Compare February 11, 2026 07:08
Copy link
Member

@nemesifier nemesifier left a comment

Choose a reason for hiding this comment

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

@pushpitkamboj the commit here is flagged as if I authored it.

- Added GitHub Action to generate changelog entries using Google Gemini
- Auto-detects CHANGES.rst vs CHANGES.md format
- Added reusable workflow for other OpenWISP repos
- Added documentation in reusable-github-utils.rst
- Added tests for the changelog generator
@pushpitkamboj pushpitkamboj force-pushed the feature/AI_created_changelog branch from 984c837 to cc4dfb1 Compare February 11, 2026 16:09
@pushpitkamboj
Copy link
Contributor Author

pushpitkamboj commented Feb 11, 2026

idk how that happened, but I have fixed it now.
@nemesifier

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

github_actions Pull requests that update GitHub Actions code releaser Related to the OpenWISP Release Tool

Projects

Status: In progress

Development

Successfully merging this pull request may close these issues.

[feature] Have AI propose change log entry / commit message

4 participants