[feature] Flag invalid pull requests that do not reference validate #709#716
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (10)
📜 Recent review details🧰 Additional context used📓 Path-based instructions (3)**/*.py📄 CodeRabbit inference engine (AGENTS.md)
Files:
**/*📄 CodeRabbit inference engine (Custom checks)
Files:
**/*.{md,rst,txt}📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧠 Learnings (1)📚 Learning: 2026-06-15T22:45:01.529ZApplied to files:
🪛 ast-grep (0.44.1).github/actions/bot-autoassign/utils.py[warning] 133-133: XPath query is request-/variable-derived; use parameterized XPath to prevent injection. (xpath-injection-python) [warning] 133-133: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling. (redos-non-literal-regex-python) 🪛 zizmor (1.26.1).github/workflows/bot-autoassign-pr-issue-link.yml[error] 9-9: overly broad permissions (excessive-permissions): issues: write is overly broad at the workflow level (excessive-permissions) [error] 10-10: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level (excessive-permissions) [warning] 9-9: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment (undocumented-permissions) [error] 29-29: dangerous use of GitHub App tokens (github-app): token granted access to all repositories for this owner's app installation (github-app) .github/workflows/reusable-bot-autoassign.yml[error] 49-49: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy) (unpinned-uses) [error] 46-46: dangerous use of GitHub App tokens (github-app): token granted access to all repositories for this owner's app installation (github-app) 🔇 Additional comments (14)
📝 WalkthroughWalkthroughThis PR adds linked-issue parsing and validation for external pull requests, including project-board checks, invalid-label handling, and bot warning comments. The stale PR bot rechecks invalid PRs, removes the label when they become valid, or closes them after 24 hours if they remain invalid. Workflows, tests, documentation, and the PyGithub minimum version were also updated. Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Contributor
participant IssueAssignmentBot
participant GitHubBot
participant GitHubAPI
Contributor->>IssueAssignmentBot: open or edit PR
IssueAssignmentBot->>GitHubBot: validate_pr_issues(pr)
GitHubBot->>GitHubAPI: fetch linked issue and project data
GitHubAPI-->>GitHubBot: issue data
GitHubBot-->>IssueAssignmentBot: valid or invalid
alt invalid
IssueAssignmentBot->>GitHubAPI: add invalid label
IssueAssignmentBot->>GitHubAPI: create warning comment
else valid
IssueAssignmentBot->>GitHubAPI: remove invalid label
end
sequenceDiagram
participant StalePRBot
participant GitHubBot
participant GitHubAPI
StalePRBot->>GitHubBot: validate_pr_issues(pr)
alt valid
StalePRBot->>GitHubAPI: remove invalid label
else still invalid
StalePRBot->>GitHubBot: get_bot_comment(invalid_unvalidated_issue)
alt comment older than 24h
StalePRBot->>GitHubAPI: post close comment
StalePRBot->>GitHubAPI: close PR
else no qualifying comment
StalePRBot->>GitHubAPI: post warning comment
end
end
Possibly related PRs
Suggested labels: Suggested reviewers: Caution Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional.
❌ Failed checks (1 error)
✅ Passed checks (7 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
3c1ead7 to
f227393
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 7
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/actions/bot-autoassign/base.py:
- Around line 29-59: The issue is that get_issue_projects in base.py calls a
non-existent self.github.raw_graphql API, which will fail with an AttributeError
during the project lookup. Update get_issue_projects to use the supported
GraphQL path on self.github, such as self.github.requester.graphql_query (or an
explicit GraphQL POST), and adjust the result parsing accordingly because
graphql_query returns a data payload plus headers rather than the current
dict-with-errors shape. Keep the existing projectItems/node extraction logic
intact in get_issue_projects after normalizing the response.
In @.github/actions/bot-autoassign/issue_assignment_bot.py:
- Around line 456-462: The label handling in issue_assignment_bot.py mixes
production logic with Mock-detection via pr_obj.labels and the assert_called
heuristic, which should be removed. Update the labels branch in the pull-request
processing flow to rely only on the existing try/except around the list
comprehension in the relevant handler, so it safely handles non-iterable values
without checking for unittest.mock internals. Keep the behavior in the
surrounding pull-request label processing logic the same, but simplify the
condition to avoid any test-framework-specific branching.
In @.github/actions/bot-autoassign/stale_pr_bot.py:
- Around line 444-469: The warning-comment body is duplicated in
`stale_pr_bot.py` and `issue_assignment_bot.py`, so the message can drift over
time. Extract the full `comment_body` text into a shared helper or constant on
`GitHubBot` in `base.py`, then have `handle_pull_request` and the corresponding
stale-PR path call that shared symbol instead of building the string inline.
- Line 443: The greeting built from stale_pr_bot.py’s PR author lookup can
become malformed when pr.user is missing because pr_author falls back to an
empty string and later produces a bad mention. Update the author resolution in
the PR comment flow around pr_author so it only formats the greeting when a
valid login exists, and use a safe fallback message or skip the mention entirely
when pr.user is falsy.
- Around line 418-419: Remove the redundant per-iteration datetime import inside
the loop in stale_pr_bot.py and rely on the existing module-level import
instead. Update the loop logic that uses datetime/timezone so it references the
already imported symbols consistently, matching how get_days_since_activity uses
datetime.now(timezone.utc).
- Around line 405-416: The warning-comment lookup in stale_pr_bot.py is
duplicating the logic from has_bot_comment and uses a weaker bot check. Update
the auto-close path to reuse has_bot_comment(pr, "invalid_unvalidated_issue")
from the existing helper in stale_pr_bot.py, and extend that helper if needed to
return the matched comment or timestamp so this block can still gate the 24h
timer. Make sure the identification relies on self.bot_login rather than
comment.user.type == "Bot" so only this bot’s own warning comment is matched.
- Around line 398-476: The `process_stale_prs` flow is swallowing failures from
the `invalid` PR handling path, so GitHub/API permission errors in
`validate_pr_issues` get logged but the run still appears successful. In the
`invalid` label branch of `stale_pr_bot.py`, make `validate_pr_issues` failures
propagate by re-raising the exception or having the outer PR loop return `False`
on failure, so the job fails fast instead of silently skipping the PR. Use the
`validate_pr_issues` call and the surrounding per-PR exception handling in
`process_stale_prs` to locate the fix.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: e0d7c777-05c7-4d9d-b099-3feb0f5adcb8
📒 Files selected for processing (8)
.github/actions/bot-autoassign/base.py.github/actions/bot-autoassign/issue_assignment_bot.py.github/actions/bot-autoassign/stale_pr_bot.py.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py.github/actions/bot-autoassign/tests/test_stale_pr_bot.py.github/actions/bot-autoassign/utils.py.github/workflows/bot-autoassign-pr-issue-link.ymldocs/developer/reusable-github-utils.rst
📜 Review details
⚠️ CI failures not shown inline (28)
GitHub Actions: OpenWISP Utils CI Build / Python==3.10 django~=5.0.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.11 django~=5.1.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.11 django~=4.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.13 django~=5.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.10 django~=5.1.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.12 django~=5.0.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.11 django~=5.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.13 django~=5.1.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.12 django~=5.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.10 django~=5.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.12 django~=4.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.10 django~=4.2.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.12 django~=5.1.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / Python==3.11 django~=5.0.0: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Post job cleanup.
[command]/usr/bin/tar --posix -cf cache.tzst --exclude cache.tzst -P -C /home/runner/work/openwisp-utils/openwisp-utils --files-from manifest.txt --use-compress-program zstdmt
/usr/bin/tar: ../../../../../var/cache/apt/archives/partial: Cannot open: Permission denied
/usr/bin/tar: ../../../../../var/cache/apt/archives/lock: Cannot open: Permission denied
/usr/bin/tar: Exiting with failure status due to previous errors
##[warning]Failed to save: "/usr/bin/tar" failed with error: The process '/usr/bin/tar' failed with exit code 2
GitHub Actions: OpenWISP Utils CI Build / 3_Python==3.10 django~=5.1.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
inux_2_28_x86_64.whl (243 kB)
Using cached netaddr-1.3.0-py3-none-any.whl (2.3 MB)
Using cached oauthlib-3.3.1-py3-none-any.whl (160 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached paramiko-5.0.0-py3-none-any.whl (208 kB)
Using cached phonenumbers-9.0.34-py2.py3-none-any.whl (2.6 MB)
Using cached pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (6.9 MB)
Using cached pyjwt-2.13.0-py3-none-any.whl (31 kB)
Using cached pyopenssl-26.3.0-py3-none-any.whl (56 kB)
Using cached cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (4.7 MB)
Using cached rcssmin-1.2.2-cp310-cp310-manylinux1_x86_64.whl (51 kB)
Using cached requests-2.34.2-py3-none-any.whl (73 kB)
Downloading charset_normalizer-3.4.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (223 kB)
Using cached idna-3.18-py3-none-any.whl (65 kB)
Using cached rjsmin-1.2.5-cp310-cp310-manylinux1_x86_64.whl (35 kB)
Using cached scp-0.15.0-py2.py3-none-any.whl (8.8 kB)
Using cached shortuuid-1.0.13-py3-none-any.whl (10 kB)
Using cached swapper-1.4.0-py2.py3-none-any.whl (7.1 kB)
Using cached urllib3-2.7.0-py3-none-any.whl (131 kB)
Using cached vine-5.1.0-py3-none-any.whl (9.6 kB)
Using cached yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (106 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
Using cached pluggy-1.6.0-py3-none-any.whl (20 kB)
Using cached pytest_mock-3.15.1-py3-none-any.whl (10 kB)
Using cached aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (218 kB)
Using cached click_didyo...
GitHub Actions: OpenWISP Utils CI Build / 2_Python==3.10 django~=5.0.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
ltidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (243 kB)
Using cached netaddr-1.3.0-py3-none-any.whl (2.3 MB)
Using cached oauthlib-3.3.1-py3-none-any.whl (160 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached paramiko-5.0.0-py3-none-any.whl (208 kB)
Using cached phonenumbers-9.0.34-py2.py3-none-any.whl (2.6 MB)
Using cached pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (6.9 MB)
Using cached pyjwt-2.13.0-py3-none-any.whl (31 kB)
Using cached pyopenssl-26.3.0-py3-none-any.whl (56 kB)
Using cached cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (4.7 MB)
Using cached rcssmin-1.2.2-cp310-cp310-manylinux1_x86_64.whl (51 kB)
Using cached requests-2.34.2-py3-none-any.whl (73 kB)
Downloading charset_normalizer-3.4.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (223 kB)
Using cached idna-3.18-py3-none-any.whl (65 kB)
Using cached rjsmin-1.2.5-cp310-cp310-manylinux1_x86_64.whl (35 kB)
Using cached scp-0.15.0-py2.py3-none-any.whl (8.8 kB)
Using cached shortuuid-1.0.13-py3-none-any.whl (10 kB)
Using cached swapper-1.4.0-py2.py3-none-any.whl (7.1 kB)
Using cached urllib3-2.7.0-py3-none-any.whl (131 kB)
Using cached vine-5.1.0-py3-none-any.whl (9.6 kB)
Using cached yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (106 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
Using cached pluggy-1.6.0-py3-none-any.whl (20 kB)
Using cached pytest_mock-3.15.1-py3-none-any.whl (10 kB)
Using cached aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp310-cp310-manylin...
GitHub Actions: OpenWISP Utils CI Build / 1_Python==3.11 django~=5.1.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (217 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp311-cp311-manylinux_2_28_x86_64.whl (472 kB)
Using cached frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (231 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (58 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached pytz-2026.2-py2.py3-none-any.whl (510 kB)
Using cached pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (806 kB)
Using cached redis-8.0.1-py3-none-any.whl (502 kB)
Using cached referencing-0.37.0-py3-none-any.whl (26 kB)
Using cached rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (371 kB)
Using cached...
GitHub Actions: OpenWISP Utils CI Build / 5_Python==3.11 django~=4.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
B)
Using cached urllib3-2.7.0-py3-none-any.whl (131 kB)
Using cached vine-5.1.0-py3-none-any.whl (9.6 kB)
Using cached yarl-1.24.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (106 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
Using cached pluggy-1.6.0-py3-none-any.whl (20 kB)
Using cached pytest_mock-3.15.1-py3-none-any.whl (10 kB)
Downloading aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (217 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp311-cp311-manylinux_2_28_x86_64.whl (472 kB)
Using cached frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (231 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp311-cp311-manylinux2...
GitHub Actions: OpenWISP Utils CI Build / 4_Python==3.13 django~=5.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
balls-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (221 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp313-cp313-manylinux_2_28_x86_64.whl (467 kB)
Using cached frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (234 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (61 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached pytz-2026.2-py2.py3-none-any.whl (510 kB)
Using cached pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (801 kB)
Using cached redis-8.0.1-...
GitHub Actions: OpenWISP Utils CI Build / 7_Python==3.12 django~=5.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (105 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
Using cached pluggy-1.6.0-py3-none-any.whl (20 kB)
Using cached pytest_mock-3.15.1-py3-none-any.whl (10 kB)
Using cached aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (221 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp312-cp312-manylinux_2_28_x86_64.whl (468 kB)
Using cached frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (242 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (61 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached p...
GitHub Actions: OpenWISP Utils CI Build / 8_Python==3.12 django~=5.0.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
kB)
Downloading cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (221 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp312-cp312-manylinux_2_28_x86_64.whl (468 kB)
Using cached frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (242 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (61 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached pytz-2026.2-py2.py3-none-any.whl (510 kB)
Using cached pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (807 kB)
Using cached redis-8.0.1-py3-none-any.whl (502 kB)
Using cached referencing-0.37.0-py3-none-any.whl (26 kB)
Using cached rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (366 kB)
Using cached six-1.17.0-py2.py3-none-any.whl (11 kB)
Using cached sqlparse-0.5.5-py3-...
GitHub Actions: OpenWISP Utils CI Build / 6_Python==3.13 django~=5.1.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp313-cp313-manylinux_2_28_x86_64.whl (467 kB)
Using cached frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (234 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (61 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached pytz-2026.2-py2.py3-none-any.whl (510 kB)
Using cached pyyaml-6.0.3-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (801 kB)
Using cached redis-8.0.1-py3-none-any.whl (502 kB)
Using cached referencing-0.37.0-py3-none-any.whl (26 kB)
Using cached rpds_py-2026.6.3-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (365 kB)
Using cached six-1.17.0-py2.py3-none-any.whl (11 kB)
Using cached sqlparse-0.5.5-py3-none-any.whl (46 kB)
Using cached tablib-3.9.0-py3-none-any.whl (49 kB)
Using cached twisted-26.4.0-py3-none-any.whl (3.2 MB)
Using cached automat-25.4.16-py3-none-any.whl (42 kB)
Using cached constantly-23.10.4-py3-none-any.whl (13 kB)
Using cached ...
GitHub Actions: OpenWISP Utils CI Build / 10_Python==3.12 django~=4.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Using cached urllib3-2.7.0-py3-none-any.whl (131 kB)
Using cached vine-5.1.0-py3-none-any.whl (9.6 kB)
Using cached yarl-1.24.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (105 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
Using cached pluggy-1.6.0-py3-none-any.whl (20 kB)
Using cached pytest_mock-3.15.1-py3-none-any.whl (10 kB)
Using cached aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (221 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp312-cp312-manylinux_2_28_x86_64.whl (468 kB)
Using cached frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (242 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp312-cp312-manylinux2014...
GitHub Actions: OpenWISP Utils CI Build / 9_Python==3.11 django~=5.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (106 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
Using cached pluggy-1.6.0-py3-none-any.whl (20 kB)
Using cached pytest_mock-3.15.1-py3-none-any.whl (10 kB)
Downloading aiohappyeyeballs-2.7.1-py3-none-any.whl (15 kB)
Using cached aiosignal-1.4.0-py3-none-any.whl (7.5 kB)
Using cached attrs-26.1.0-py3-none-any.whl (67 kB)
Using cached bcrypt-5.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (217 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp311-cp311-manylinux_2_28_x86_64.whl (472 kB)
Using cached frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (231 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (58 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pyn...
GitHub Actions: OpenWISP Utils CI Build / 12_Python==3.12 django~=5.1.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (221 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp312-cp312-manylinux_2_28_x86_64.whl (468 kB)
Using cached frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (242 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (61 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached pytz-2026.2-py2.py3-none-any.whl (510 kB)
Using cached pyyaml-6.0.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (807 kB)
Using cached redis-8.0.1-py3-none-any.whl (502 kB)
Using cached referencing-0.37.0-py3-none-any.whl (26 kB)
Using cached rpds_py-2026.6.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (366 kB)
Using cached ...
GitHub Actions: OpenWISP Utils CI Build / 13_Python==3.10 django~=4.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
ib-2.1-py3-none-any.whl (40 kB)
Using cached jinja2-3.1.6-py3-none-any.whl (134 kB)
Using cached jsonschema-4.26.0-py3-none-any.whl (90 kB)
Using cached markdown-3.10.2-py3-none-any.whl (108 kB)
Using cached maxminddb-3.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (98 kB)
Using cached msgpack-1.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (407 kB)
Using cached multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (243 kB)
Using cached netaddr-1.3.0-py3-none-any.whl (2.3 MB)
Using cached oauthlib-3.3.1-py3-none-any.whl (160 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached paramiko-5.0.0-py3-none-any.whl (208 kB)
Using cached phonenumbers-9.0.34-py2.py3-none-any.whl (2.6 MB)
Using cached pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (6.9 MB)
Using cached pyjwt-2.13.0-py3-none-any.whl (31 kB)
Using cached pyopenssl-26.3.0-py3-none-any.whl (56 kB)
Using cached cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (4.7 MB)
Using cached rcssmin-1.2.2-cp310-cp310-manylinux1_x86_64.whl (51 kB)
Using cached requests-2.34.2-py3-none-any.whl (73 kB)
Downloading charset_normalizer-3.4.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (223 kB)
Using cached idna-3.18-py3-none-any.whl (65 kB)
Using cached rjsmin-1.2.5-cp310-cp310-manylinux1_x86_64.whl (35 kB)
Using cached scp-0.15.0-py2.py3-none-any.whl (8.8 kB)
Using cached shortuuid-1.0.13-py3-none-any.whl (10 kB)
Using cached swapper-1.4.0-py2.py3-none-any.whl (7.1 kB)
Using cached urllib3-2.7.0-py3-none-any.whl (131 kB)
Using cached vine-5.1.0-py3-none-any.whl (9.6 kB)
Using cached yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (106 kB)
Using cached freezegun-1.5.5-py3-none-any.whl (19 kB)
Using cached pytest-9.1.1-py3-none-any.whl (386 kB)
U...
GitHub Actions: OpenWISP Utils CI Build / 11_Python==3.10 django~=5.2.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
Using cached geopy-2.4.1-py3-none-any.whl (125 kB)
Using cached geographiclib-2.1-py3-none-any.whl (40 kB)
Using cached jinja2-3.1.6-py3-none-any.whl (134 kB)
Using cached jsonschema-4.26.0-py3-none-any.whl (90 kB)
Using cached markdown-3.10.2-py3-none-any.whl (108 kB)
Using cached maxminddb-3.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (98 kB)
Using cached msgpack-1.2.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (407 kB)
Using cached multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (243 kB)
Using cached netaddr-1.3.0-py3-none-any.whl (2.3 MB)
Using cached oauthlib-3.3.1-py3-none-any.whl (160 kB)
Using cached openpyxl-3.1.5-py2.py3-none-any.whl (250 kB)
Using cached paramiko-5.0.0-py3-none-any.whl (208 kB)
Using cached phonenumbers-9.0.34-py2.py3-none-any.whl (2.6 MB)
Using cached pillow-12.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl (6.9 MB)
Using cached pyjwt-2.13.0-py3-none-any.whl (31 kB)
Using cached pyopenssl-26.3.0-py3-none-any.whl (56 kB)
Using cached cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl (4.7 MB)
Using cached rcssmin-1.2.2-cp310-cp310-manylinux1_x86_64.whl (51 kB)
Using cached requests-2.34.2-py3-none-any.whl (73 kB)
Downloading charset_normalizer-3.4.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (223 kB)
Using cached idna-3.18-py3-none-any.whl (65 kB)
Using cached rjsmin-1.2.5-cp310-cp310-manylinux1_x86_64.whl (35 kB)
Using cached scp-0.15.0-py2.py3-none-any.whl (8.8 kB)
Using cached shortuuid-1.0.13-py3-none-any.whl (10 kB)
Using cached swapper-1.4.0-py2.py3-none-any.whl (7.1 kB)
Using cached urllib3-2.7.0-py3-none-any.whl (131 kB)
Using cached vine-5.1.0-py3-none-any.whl (9.6 kB)
Using cached yarl-1.24.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (106 kB)
Using cached freezegun-1.5.5-py3...
GitHub Actions: OpenWISP Utils CI Build / 14_Python==3.11 django~=5.0.0.txt: [feature] Flag invalid pull requests that do not reference validate #709
Conclusion: failure
_x86_64.whl (278 kB)
Using cached certifi-2026.6.17-py3-none-any.whl (133 kB)
Downloading cffi-2.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (217 kB)
Using cached click_didyoumean-0.3.1-py3-none-any.whl (3.6 kB)
Using cached click_plugins-1.1.1.2-py2.py3-none-any.whl (11 kB)
Using cached click_repl-0.3.0-py3-none-any.whl (10 kB)
Using cached daphne-4.2.2-py3-none-any.whl (29 kB)
Using cached autobahn-26.6.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl (2.2 MB)
Downloading cbor2-6.1.3-cp311-cp311-manylinux_2_28_x86_64.whl (472 kB)
Using cached frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl (231 kB)
Using cached hyperlink-21.0.0-py2.py3-none-any.whl (74 kB)
Using cached inflection-0.5.1-py2.py3-none-any.whl (9.5 kB)
Using cached iniconfig-2.3.0-py3-none-any.whl (7.5 kB)
Using cached invoke-3.0.3-py3-none-any.whl (160 kB)
Using cached jsonschema_specifications-2025.9.1-py3-none-any.whl (18 kB)
Using cached kombu-5.6.2-py3-none-any.whl (214 kB)
Using cached amqp-5.3.1-py3-none-any.whl (50 kB)
Using cached markupsafe-3.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (22 kB)
Using cached prompt_toolkit-3.0.52-py3-none-any.whl (391 kB)
Using cached propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (58 kB)
Using cached pygments-2.20.0-py3-none-any.whl (1.2 MB)
Using cached pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl (1.4 MB)
Using cached python_dateutil-2.9.0.post0-py2.py3-none-any.whl (229 kB)
Using cached pytz-2026.2-py2.py3-none-any.whl (510 kB)
Using cached pyyaml-6.0.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl (806 kB)
Using cached redis-8.0.1-py3-none-any.whl (502 kB)
Using cached referencing-0.37.0-py3-none-any.whl (26 kB)
Using cached rpds_py-2026.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (371 kB)
Using cached...
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{md,rst,txt}
📄 CodeRabbit inference engine (AGENTS.md)
Update documentation when behavior, settings, public APIs, setup steps, QA rules, or supported versions change
Files:
docs/developer/reusable-github-utils.rst
🪛 ast-grep (0.44.1)
.github/actions/bot-autoassign/utils.py
[warning] 133-133: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').
(xpath-injection-python)
[warning] 133-133: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-1333] Inefficient Regular Expression Complexity.
(redos-non-literal-regex-python)
🪛 zizmor (1.26.1)
.github/workflows/bot-autoassign-pr-issue-link.yml
[error] 3-5: use of fundamentally insecure workflow trigger (dangerous-triggers): pull_request_target is almost always used insecurely
(dangerous-triggers)
🔇 Additional comments (13)
.github/actions/bot-autoassign/stale_pr_bot.py (1)
430-437: LGTM!.github/actions/bot-autoassign/utils.py (1)
115-149: LGTM!.github/actions/bot-autoassign/base.py (1)
191-213: 🩺 Stability & AvailabilityLet comment lookup failures surface. Returning
Falsehere makes callers treat a comment-listing/read error as “no bot comment yet,” which can lead to duplicate warning comments on retry..github/actions/bot-autoassign/tests/test_issue_assignment_bot.py (4)
888-916: LGTM!
917-945: LGTM!
1034-1085: LGTM!
946-1033: 🎯 Functional CorrectnessMake sure
bot_env["repo"]is the repo returned bybot.github.get_repo(...). These tests only stubbot_env["repo"].get_issue(...); ifvalidate_pr_issuesuses a different repo mock, the cases won’t exercise the intended issue path..github/actions/bot-autoassign/tests/test_stale_pr_bot.py (1)
863-950: LGTM!.github/actions/bot-autoassign/issue_assignment_bot.py (2)
445-450: LGTM!
452-500: Core validate/label/comment flow looks correct.Control flow correctly separates
closed(early return, no validation) fromopened/reopened/edited/ready_for_review(validate → toggle label → conditionally comment), andhas_bot_commentprevents duplicate warning comments across repeated invalid states. This matches the described contract invalidate_pr_issues/has_bot_comment(base.py) and the accompanying tests..github/workflows/bot-autoassign-pr-issue-link.yml (1)
5-5: 🔒 Security & PrivacyVerify no PR-head checkout occurs downstream now that
edited/ready_for_reviewalso trigger this privileged workflow.zizmor flags
pull_request_targetas a dangerous trigger since it runs with base-repo secrets/permissions while exposed to attacker-influenced event data. This trigger type predates this change (already used foropened/reopened/closed), but expanding it toedited/ready_for_reviewincreases how often the privileged job runs on external PR metadata (including PR body text, which is now parsed for linked-issue extraction). The steps referenced in the docs example checkout the base ref viaactions/checkout@v7without overridingref, which is safe, but this file's own job body (lines 6-16) isn't fully shown here.Please confirm the reusable workflow invoked by this job never checks out
github.event.pull_request.head.sha/ref or otherwise executes PR-controlled code with these elevated permissions.Source: Linters/SAST tools
docs/developer/reusable-github-utils.rst (2)
76-82: 🎯 Functional Correctness | ⚡ Quick winValidate the "24 hours" closure claim against the actual Stale PR job cadence.
This bullet (and the matching warning-comment text in
issue_assignment_bot.py) states invalid PRs are "automatically closed after 24 hours if the issue is not resolved." Per the unchanged "How Stale PR Detection Works" section below, the Stale PR job runs once daily. If the actual auto-close logic lives in that same daily job, worst-case latency could approach ~48 hours (grace period + up to a full day until the next scheduled run) rather than a strict 24 hours.Since the closing logic lives in
stale_pr_bot.py(not included in this review), please confirm the described SLA is accurate, or soften the wording (e.g., "closed on the next daily check after 24 hours have elapsed") to avoid setting an inaccurate expectation for contributors.
177-177: LGTM!
f227393 to
976b572
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/actions/bot-autoassign/base.py:
- Around line 34-61: The GraphQL helper in get_issue_projects uses
self.github.requester.graphql_query with named variables, which requires a newer
PyGithub than the current floor permits. Update the project’s PyGithub minimum
version to 2.5.0+ wherever the dependency constraint is defined, or change
get_issue_projects to avoid the newer requester/graphql API and use only
supported calls. Keep the fix centered on the get_issue_projects path so the
dependency requirement matches the code actually used.
In @.github/actions/bot-autoassign/stale_pr_bot.py:
- Around line 504-506: The per-PR exception handling in stale_pr_bot.py is too
broad: the blanket re-raise in the PR processing loop now stops the entire stale
run on any single PR failure. Narrow the fail-fast behavior in the PR-processing
logic around the validate_pr_issues path so only verification/API or permission
failures are re-raised, and keep unrelated per-PR errors handled in a way that
lets the loop continue to the next PR. Use the existing error handling block
inside the PR iteration to distinguish the intended blast radius.
In @.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py:
- Around line 982-1001: The test in IssueAssignmentBot.validate_pr_issues only
covers the empty/early-continue path and never reaches the mixed-label check.
Update test_validate_pr_issues_invalid_labels to add a case with both a valid
label and a disallowed label (for example, bug plus wontfix or invalid) so the
later any(lbl in ["invalid", "wontfix"]) branch is exercised. Keep the existing
mock_pr and mock_issue setup, and adjust mock_issue.labels to explicitly include
a mixed set before asserting the method returns false.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 5131bcfe-f36b-4350-819e-1d96164ac1c2
📒 Files selected for processing (8)
.github/actions/bot-autoassign/base.py.github/actions/bot-autoassign/issue_assignment_bot.py.github/actions/bot-autoassign/stale_pr_bot.py.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py.github/actions/bot-autoassign/tests/test_stale_pr_bot.py.github/actions/bot-autoassign/utils.py.github/workflows/bot-autoassign-pr-issue-link.ymldocs/developer/reusable-github-utils.rst
📜 Review details
⏰ Context from checks skipped due to timeout. (9)
- 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.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{md,rst,txt}
📄 CodeRabbit inference engine (AGENTS.md)
Update documentation when behavior, settings, public APIs, setup steps, QA rules, or supported versions change
Files:
docs/developer/reusable-github-utils.rst
🪛 ast-grep (0.44.1)
.github/actions/bot-autoassign/utils.py
[warning] 133-133: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-1333] Inefficient Regular Expression Complexity.
(redos-non-literal-regex-python)
[warning] 133-133: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').
(xpath-injection-python)
🪛 zizmor (1.26.1)
.github/workflows/bot-autoassign-pr-issue-link.yml
[error] 3-5: use of fundamentally insecure workflow trigger (dangerous-triggers): pull_request_target is almost always used insecurely
(dangerous-triggers)
🔇 Additional comments (11)
.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py (2)
918-925: LGTM!
GithubException(400, {...}, {})matches the PyGithubGithubException(status, data, headers)constructor signature used across supported 2.x versions, so this test correctly exercises the error propagation path.
926-1094: LGTM!Rest of
TestPRValidationcoverage (excluded authors, exempt association, missing/cross-org references, PR-vs-issue detection, closed-issue skip, project-membership checks, invalid-label/comment flow, and label removal on validation) aligns with thevalidate_pr_issues/handle_pull_requestcontract shown inbase.pyandissue_assignment_bot.py..github/actions/bot-autoassign/tests/test_stale_pr_bot.py (1)
865-950: LGTM!The four
TestStalePRBotInvalidCheckcases correctly map onto the branches inprocess_stale_prsfor the invalid-label recheck: label removal on revalidation, no-op under 24h, close-with-comment over 24h, and posting the missing warning comment..github/workflows/bot-autoassign-pr-issue-link.yml (1)
5-5: 🔒 Security & Privacy
pull_request_targetwidened toedited/ready_for_review.zizmor flags
pull_request_targetas an inherently risky trigger since it runs with base-repo permissions/secrets against untrusted PR metadata. The checkout step here (Line 31) doesn't check out the PR head ref, so arbitrary code execution risk is mitigated, but widening the trigger types does increase how often this privileged workflow runs on external PR content (title/body edits). Worth confirming the intended usage matches this exposure, and that no future step is added that checks out or evaluates PR head content.Source: Linters/SAST tools
docs/developer/reusable-github-utils.rst (1)
76-82: LGTM!Documentation updates accurately describe the new PR validation behavior (label/warning-comment/24h auto-close) and the expanded workflow trigger types, keeping docs in sync with the behavior change. As per coding guidelines, "Update documentation when behavior, settings, public APIs, setup steps, QA rules, or supported versions change."
Also applies to: 177-177
.github/actions/bot-autoassign/utils.py (1)
115-149: LGTM!.github/actions/bot-autoassign/base.py (3)
12-16: LGTM!
193-252: LGTM!
168-175: 🩺 Stability & AvailabilityNo change needed:
handle_pull_request()returningFalsestill bubbles up tomain()andsys.exit(1), so the workflow fails for retry even though the exception is swallowed.> Likely an incorrect or invalid review comment..github/actions/bot-autoassign/issue_assignment_bot.py (1)
448-474: LGTM!.github/actions/bot-autoassign/stale_pr_bot.py (1)
371-418: LGTM!
976b572 to
cc80de8
Compare
nemesifier
left a comment
There was a problem hiding this comment.
Thanks for picking this up @prathmeshkulkarni-coder, I gave you access to https://github.com/openwisp/bot-testing-ground, please add the required logic there, start testing PRs there, you may need to create another github account to test the case in which a PR is created by someone who's not a collaborator (as you should be a collaborator of that repo there once you accept the invite).
You're free to test anything you need on that repository without approval.
Let's test all the supported cases and report each one here with a link to the test PR.
| f"Error fetching issue {owner}/{repo_name}#{issue_number}: {e}" | ||
| ) | ||
| raise | ||
|
|
There was a problem hiding this comment.
Too many blank lines, use blank lines sparingly.
|
Sure thanks i'll give you update after testing |
cc80de8 to
035dcae
Compare
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/actions/bot-autoassign/base.py:
- Around line 96-123: Move the method-local imports in validate_pr_issues to the
module level: import extract_all_linked_issues and GithubException alongside the
other top-of-file imports in base.py, then remove the inline import statements
from the validation loop since there is no deferred-import need.
- Around line 39-55: The projectItems lookup in the GraphQL query is only
reading the first 10 items, which can miss the approved OpenWISP board and cause
valid PRs to be treated as invalid. Update the repository/issue fetch flow in
the bot-autoassign Base class to paginate projectItems using pageInfo and
endCursor, and keep requesting additional pages until all project items are
collected before checking the board title.
In @.github/actions/bot-autoassign/issue_assignment_bot.py:
- Around line 439-450: The control flow in issue_assignment_bot.py currently
auto-assigns linked issues before the PR validation check, allowing
`auto_assign_issues_from_pr()` to run ahead of `validate_pr_issues()` for
`opened`/`reopened`. Update the `handle_pull_request` path so
`validate_pr_issues()` runs first for relevant actions, and only call
`auto_assign_issues_from_pr()` when the PR is valid and not labeled invalid.
Keep the existing `action` handling, but reorder or gate the logic around
`pr_obj`, `is_valid`, `auto_assign_issues_from_pr`, and `validate_pr_issues` so
external PRs cannot assign issues before validation.
In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py:
- Around line 863-950: Add a test in TestStalePRBotInvalidCheck that covers the
ValidationAPIError path in process_stale_prs by patching validate_pr_issues to
raise ValidationAPIError and asserting the exception is re-raised. Reuse the
existing mock_pr setup (invalid label, repo.get_pulls, and any comment state
needed) so the test exercises the same invalid-PR flow, and verify no label
removal, comment creation, or close/edit actions happen when
StalePRBot.process_stale_prs encounters this error.
- Line 915: Move the deferred timedelta import into the module’s top-level
import block in test_stale_pr_bot.py, since it is not needed lazily. Update the
existing datetime import near the top so timedelta is imported there, and remove
the local import in the test body. Use the datetime import statements and any
test helper/function around the stale PR bot tests as the reference points when
relocating it.
In `@docs/developer/reusable-github-utils.rst`:
- Around line 76-82: Clarify the PR validation description so “if the issue is
not resolved” clearly refers to the pull request still missing a validated issue
reference, not the linked GitHub issue. Update the wording in the PR validation
section of reusable-github-utils.rst to state that the 24-hour timer starts when
the warning comment is posted and that the PR is closed only if it still lacks a
validated issue reference after that period.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 75db9ec5-92d0-4841-9bf5-f8cb1da82544
📒 Files selected for processing (10)
.github/actions/bot-autoassign/base.py.github/actions/bot-autoassign/issue_assignment_bot.py.github/actions/bot-autoassign/stale_pr_bot.py.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py.github/actions/bot-autoassign/tests/test_stale_pr_bot.py.github/actions/bot-autoassign/utils.py.github/workflows/bot-autoassign-pr-issue-link.yml.github/workflows/reusable-bot-autoassign.ymldocs/developer/reusable-github-utils.rstsetup.py
📜 Review details
⏰ Context from checks skipped due to timeout. (14)
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.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~=4.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.0.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{py,html,txt}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
setup.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)
Avoid unnecessary blank lines inside function and method bodies
Add or update tests for every behavior change
Runopenwisp-qa-formatafter editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, usepython -m pytest <test_path> --cov=<dotted.module.path> --cov-report=term-missing
Watch for unsafe file paths, unsafe subprocess usage, token or secret exposure, and changes that could weaken QA or release safeguards
Write comments and docstrings only when they explain why code is shaped a certain way; place comments before the relevant code block instead of scattering them inside it
Files:
setup.py
**/*.{md,rst,txt}
📄 CodeRabbit inference engine (AGENTS.md)
Update documentation when behavior, settings, public APIs, setup steps, QA rules, or supported versions change
Files:
docs/developer/reusable-github-utils.rst
🧠 Learnings (1)
📚 Learning: 2026-06-15T22:45:01.529Z
Learnt from: nemesifier
Repo: openwisp/openwisp-utils PR: 701
File: setup.py:72-72
Timestamp: 2026-06-15T22:45:01.529Z
Learning: When reviewing changes that require a minimum Selenium Python version for specific APIs (e.g., BiDi features like `Options.enable_bidi`, console/message handlers like `Script.add_console_message_handler`, or event handlers like `BrowsingContext.add_event_handler`), do not use the Selenium API docs site (`https://www.selenium.dev/selenium/docs/api/py/`) as the source of introduction/version history, since it reflects the latest API shape. Instead, confirm feature availability by checking the official Python changelog (`py/CHANGES` in the Selenium repo) and/or inspecting the Selenium source (tags/commits corresponding to candidate versions). Only accept the stated minimum version after verifying that the referenced APIs exist in that Selenium version (e.g., Selenium 4.32.0 includes the BiDi APIs used by openwisp-utils’ BiDi-related PR `#701` as of that version).
Applied to files:
setup.py
🪛 ast-grep (0.44.1)
.github/actions/bot-autoassign/utils.py
[warning] 133-133: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').
(xpath-injection-python)
[warning] 133-133: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-1333] Inefficient Regular Expression Complexity.
(redos-non-literal-regex-python)
🪛 zizmor (1.26.1)
.github/workflows/bot-autoassign-pr-issue-link.yml
[error] 3-5: use of fundamentally insecure workflow trigger (dangerous-triggers): pull_request_target is almost always used insecurely
(dangerous-triggers)
🔇 Additional comments (10)
.github/actions/bot-autoassign/utils.py (1)
115-149: LGTM!.github/actions/bot-autoassign/base.py (2)
12-16: LGTM!
193-252: LGTM!.github/actions/bot-autoassign/stale_pr_bot.py (1)
15-16: LGTM!Also applies to: 375-431, 516-517
.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py (1)
888-1100: LGTM!.github/actions/bot-autoassign/tests/test_stale_pr_bot.py (1)
863-901: LGTM!Also applies to: 929-950
.github/workflows/bot-autoassign-pr-issue-link.yml (1)
5-5: LGTM!.github/workflows/reusable-bot-autoassign.yml (1)
15-24: LGTM!Also applies to: 45-46
docs/developer/reusable-github-utils.rst (1)
177-177: LGTM!setup.py (1)
81-81: 🔒 Security & PrivacyPyGithub 2.5.0 is compatible here
Requester.graphql_querystill returns a(headers, data)tuple, andGithubException.statusremains available for the 404 check.
| projectItems(first: 10) { | ||
| nodes { | ||
| project { | ||
| title | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| """ | ||
| variables = {"owner": owner, "repo": repo_name, "issueNumber": issue_number} | ||
| headers, result = self.github.requester.graphql_query(query, variables) | ||
| repo_data = result.get("data", {}).get("repository", {}) or {} | ||
| issue_data = repo_data.get("issue", {}) or {} | ||
| project_items = issue_data.get("projectItems", {}) or {} | ||
| nodes = project_items.get("nodes", []) or [] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== file outline ==\n'
ast-grep outline .github/actions/bot-autoassign/base.py --view expanded || true
printf '\n== relevant lines ==\n'
nl -ba .github/actions/bot-autoassign/base.py | sed -n '1,180p'
printf '\n== search projectItems and autoassign validation ==\n'
rg -n "projectItems|OpenWISP|invalid label|auto-clos|approved contributor|project board" .github/actions/bot-autoassign -S || trueRepository: openwisp/openwisp-utils
Length of output: 740
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== base.py excerpt ==\n'
awk 'NR>=1 && NR<=140 {printf "%4d %s\n", NR, $0}' .github/actions/bot-autoassign/base.py
printf '\n== project validation references ==\n'
rg -n "projectItems|pageInfo|endCursor|hasNextPage|get_issue_projects|validate_pr_issues" .github/actions/bot-autoassign -SRepository: openwisp/openwisp-utils
Length of output: 11495
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== validate_pr_issues excerpt around project checks ==\n'
awk 'NR>=150 && NR<=210 {printf "%4d %s\n", NR, $0}' .github/actions/bot-autoassign/base.py
printf '\n== tests around get_issue_projects / validation ==\n'
awk 'NR>=880 && NR<=1065 {printf "%4d %s\n", NR, $0}' .github/actions/bot-autoassign/tests/test_issue_assignment_bot.pyRepository: openwisp/openwisp-utils
Length of output: 10896
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('.github/actions/bot-autoassign/base.py').read_text()
start = text.index(' def validate_pr_issues')
print(text[start:start+2600])
PYRepository: openwisp/openwisp-utils
Length of output: 2762
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
text = Path('.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py').read_text().splitlines()
for i in range(888, 1060):
if i < len(text):
print(f"{i+1:4d} {text[i]}")
PYRepository: openwisp/openwisp-utils
Length of output: 7455
🌐 Web query:
GitHub GraphQL issue projectItems connection first page pagination pageInfo endCursor documentation
💡 Result:
To paginate through the projectItems connection (available via ProjectV2.items) in the GitHub GraphQL API, you must use cursor-based pagination with the pageInfo object [1][2]. The items field accepts first (to specify the page size) and after (to specify the cursor) arguments [3][2]. The pageInfo object returned by the connection provides the necessary fields to navigate the dataset [1][3]: - endCursor: The opaque string representing the cursor of the last item in the current page [4][3]. - hasNextPage: A boolean indicating whether more items are available [3][2]. To retrieve subsequent pages, use the endCursor value from your current response as the after argument in your next request [2]. Example pattern: query { node(id: "PROJECT_ID") {... on ProjectV2 { items(first: 100, after: null) { pageInfo { endCursor hasNextPage } nodes { id #... rest of your query } } } } } In the next iteration, replace the after argument with the endCursor string obtained from the previous response [2]. Continue this process as long as hasNextPage remains true [3][2].
Citations:
- 1: https://docs.github.com/en/enterprise-cloud@latest/graphql/reference/projects
- 2: https://github.com/github/awesome-copilot/blob/main/skills/issue-fields-migration/references/projects-api.md
- 3: https://stackoverflow.com/questions/77528848/github-graphql-api-how-to-retrieve-issues-project-items
- 4: https://docs.github.com/en/graphql/guides/using-pagination-in-the-graphql-api
Paginate projectItems here
first: 10 can miss the approved OpenWISP board and falsely leave a valid PR marked invalid/auto-closed. Use pageInfo/endCursor pagination instead of relying on a single page.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/bot-autoassign/base.py around lines 39 - 55, The
projectItems lookup in the GraphQL query is only reading the first 10 items,
which can miss the approved OpenWISP board and cause valid PRs to be treated as
invalid. Update the repository/issue fetch flow in the bot-autoassign Base class
to paginate projectItems using pageInfo and endCursor, and keep requesting
additional pages until all project items are collected before checking the board
title.
| # For external contributors, extract linked issues | ||
| from utils import extract_all_linked_issues | ||
|
|
||
| pr_body = pr.body if isinstance(pr.body, str) else "" | ||
| linked_issues = extract_all_linked_issues(pr_body, self.repository_name) | ||
| if not linked_issues: | ||
| print("No linked issues found in PR body for external contributor.") | ||
| return False | ||
|
|
||
| current_org = self.repository_name.split("/")[0].lower() | ||
| required_projects = [ | ||
| "OpenWISP Contributor's Board", | ||
| "OpenWISP Priorities for next releases", | ||
| ] | ||
|
|
||
| for owner, repo_name, issue_number in linked_issues: | ||
| # 1. Check if the issue belongs to the same organization | ||
| if owner.lower() != current_org: | ||
| print( | ||
| f"Issue {owner}/{repo_name}#{issue_number} does not belong " | ||
| f"to organization {current_org}, skipping validation." | ||
| ) | ||
| continue | ||
|
|
||
| try: | ||
| from github import GithubException | ||
|
|
||
| target_repo = self.github.get_repo(f"{owner}/{repo_name}") |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
Move method-local imports to module scope.
extract_all_linked_issues and GithubException are imported inside validate_pr_issues without an evident deferred-import requirement.
♻️ Proposed cleanup
+from github import GithubException
+from utils import extract_all_linked_issues
+
...
- from utils import extract_all_linked_issues
-
pr_body = pr.body if isinstance(pr.body, str) else ""
...
- from github import GithubException
-
target_repo = self.github.get_repo(f"{owner}/{repo_name}")As per coding guidelines, **/*.py files should place imports at the top unless deferral is necessary.
📝 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.
| # For external contributors, extract linked issues | |
| from utils import extract_all_linked_issues | |
| pr_body = pr.body if isinstance(pr.body, str) else "" | |
| linked_issues = extract_all_linked_issues(pr_body, self.repository_name) | |
| if not linked_issues: | |
| print("No linked issues found in PR body for external contributor.") | |
| return False | |
| current_org = self.repository_name.split("/")[0].lower() | |
| required_projects = [ | |
| "OpenWISP Contributor's Board", | |
| "OpenWISP Priorities for next releases", | |
| ] | |
| for owner, repo_name, issue_number in linked_issues: | |
| # 1. Check if the issue belongs to the same organization | |
| if owner.lower() != current_org: | |
| print( | |
| f"Issue {owner}/{repo_name}#{issue_number} does not belong " | |
| f"to organization {current_org}, skipping validation." | |
| ) | |
| continue | |
| try: | |
| from github import GithubException | |
| target_repo = self.github.get_repo(f"{owner}/{repo_name}") | |
| from github import GithubException | |
| from utils import extract_all_linked_issues | |
| # For external contributors, extract linked issues | |
| pr_body = pr.body if isinstance(pr.body, str) else "" | |
| linked_issues = extract_all_linked_issues(pr_body, self.repository_name) | |
| if not linked_issues: | |
| print("No linked issues found in PR body for external contributor.") | |
| return False | |
| current_org = self.repository_name.split("/")[0].lower() | |
| required_projects = [ | |
| "OpenWISP Contributor's Board", | |
| "OpenWISP Priorities for next releases", | |
| ] | |
| for owner, repo_name, issue_number in linked_issues: | |
| # 1. Check if the issue belongs to the same organization | |
| if owner.lower() != current_org: | |
| print( | |
| f"Issue {owner}/{repo_name}#{issue_number} does not belong " | |
| f"to organization {current_org}, skipping validation." | |
| ) | |
| continue | |
| try: | |
| target_repo = self.github.get_repo(f"{owner}/{repo_name}") |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/bot-autoassign/base.py around lines 96 - 123, Move the
method-local imports in validate_pr_issues to the module level: import
extract_all_linked_issues and GithubException alongside the other top-of-file
imports in base.py, then remove the inline import statements from the validation
loop since there is no deferred-import need.
Source: Coding guidelines
|
|
||
|
|
||
| class TestStalePRBotInvalidCheck: | ||
| def test_invalid_pr_becomes_valid(self, bot_env): | ||
| bot = StalePRBot() | ||
| mock_label = Mock() | ||
| mock_label.name = "invalid" | ||
|
|
||
| mock_pr = Mock() | ||
| mock_pr.labels = [mock_label] | ||
| mock_pr.number = 100 | ||
| bot_env["repo"].get_pulls.return_value = [mock_pr] | ||
|
|
||
| with patch.object(bot, "validate_pr_issues", return_value=True): | ||
| assert bot.process_stale_prs() | ||
| mock_pr.remove_from_labels.assert_called_once_with("invalid") | ||
| mock_pr.edit.assert_not_called() | ||
|
|
||
| def test_invalid_pr_still_invalid_under_24h(self, bot_env): | ||
| bot = StalePRBot() | ||
| mock_label = Mock() | ||
| mock_label.name = "invalid" | ||
|
|
||
| mock_pr = Mock() | ||
| mock_pr.labels = [mock_label] | ||
| mock_pr.number = 100 | ||
| bot_env["repo"].get_pulls.return_value = [mock_pr] | ||
|
|
||
| mock_comment = Mock() | ||
| mock_comment.user.login = bot.bot_login | ||
| mock_comment.body = "<!-- bot:invalid_unvalidated_issue --> Warning comment" | ||
| mock_comment.created_at = datetime.now(timezone.utc) | ||
| mock_pr.get_issue_comments.return_value = [mock_comment] | ||
|
|
||
| with patch.object(bot, "validate_pr_issues", return_value=False): | ||
| assert bot.process_stale_prs() | ||
| mock_pr.remove_from_labels.assert_not_called() | ||
| mock_pr.edit.assert_not_called() | ||
|
|
||
| def test_invalid_pr_still_invalid_over_24h_closes(self, bot_env): | ||
| bot = StalePRBot() | ||
| mock_label = Mock() | ||
| mock_label.name = "invalid" | ||
|
|
||
| mock_pr = Mock() | ||
| mock_pr.labels = [mock_label] | ||
| mock_pr.number = 100 | ||
| bot_env["repo"].get_pulls.return_value = [mock_pr] | ||
|
|
||
| mock_comment = Mock() | ||
| mock_comment.user.login = bot.bot_login | ||
| mock_comment.body = "<!-- bot:invalid_unvalidated_issue --> Warning comment" | ||
| from datetime import timedelta | ||
|
|
||
| mock_comment.created_at = datetime.now(timezone.utc) - timedelta(hours=25) | ||
| mock_pr.get_issue_comments.return_value = [mock_comment] | ||
|
|
||
| with patch.object(bot, "validate_pr_issues", return_value=False): | ||
| assert bot.process_stale_prs() | ||
| mock_pr.remove_from_labels.assert_not_called() | ||
| mock_pr.create_issue_comment.assert_called_once() | ||
| assert ( | ||
| "automatically closed" in mock_pr.create_issue_comment.call_args[0][0] | ||
| ) | ||
| mock_pr.edit.assert_called_once_with(state="closed") | ||
|
|
||
| def test_invalid_pr_missing_warning_comment_posts_warning(self, bot_env): | ||
| bot = StalePRBot() | ||
| mock_label = Mock() | ||
| mock_label.name = "invalid" | ||
|
|
||
| mock_pr = Mock() | ||
| mock_pr.labels = [mock_label] | ||
| mock_pr.number = 100 | ||
| mock_pr.user.login = "contributor" | ||
| bot_env["repo"].get_pulls.return_value = [mock_pr] | ||
|
|
||
| mock_pr.get_issue_comments.return_value = [] | ||
|
|
||
| with patch.object(bot, "validate_pr_issues", return_value=False): | ||
| assert bot.process_stale_prs() | ||
| mock_pr.remove_from_labels.assert_not_called() | ||
| mock_pr.create_issue_comment.assert_called_once() | ||
| assert ( | ||
| "<!-- bot:invalid_unvalidated_issue -->" | ||
| in mock_pr.create_issue_comment.call_args[0][0] | ||
| ) | ||
| mock_pr.edit.assert_not_called() |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win
Add a test for ValidationAPIError re-raise when validate_pr_issues raises.
process_stale_prs re-raises ValidationAPIError when validate_pr_issues throws, but no test in TestStalePRBotInvalidCheck covers this error path. Per the PR objectives, API or permission errors should fail the workflow for retry. As per coding guidelines, tests should be added for every behavior change.
Would you like me to generate the missing test case?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py around lines 863 -
950, Add a test in TestStalePRBotInvalidCheck that covers the ValidationAPIError
path in process_stale_prs by patching validate_pr_issues to raise
ValidationAPIError and asserting the exception is re-raised. Reuse the existing
mock_pr setup (invalid label, repo.get_pulls, and any comment state needed) so
the test exercises the same invalid-PR flow, and verify no label removal,
comment creation, or close/edit actions happen when StalePRBot.process_stale_prs
encounters this error.
Source: Coding guidelines
| mock_comment = Mock() | ||
| mock_comment.user.login = bot.bot_login | ||
| mock_comment.body = "<!-- bot:invalid_unvalidated_issue --> Warning comment" | ||
| from datetime import timedelta |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Move timedelta import to the top of the file.
The local from datetime import timedelta import violates the project's coding guidelines, which require imports at the top of the file unless deferral is necessary. timedelta has no deferral justification here.
As per coding guidelines: "Place imports at the top of the file; only defer imports when necessary."
♻️ Proposed fix
- from datetime import timedelta
-
mock_comment.created_at = datetime.now(timezone.utc) - timedelta(hours=25)Add timedelta to the existing top-level datetime import (if not already present):
-from datetime import datetime, timezone
+from datetime import datetime, timedelta, timezone🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py at line 915, Move
the deferred timedelta import into the module’s top-level import block in
test_stale_pr_bot.py, since it is not needed lazily. Update the existing
datetime import near the top so timedelta is imported there, and remove the
local import in the test body. Use the datetime import statements and any test
helper/function around the stale PR bot tests as the reference points when
relocating it.
Source: Coding guidelines
|
@nemesifier , the bot is working but not as I thought
I am still working on the bot |
the bot can't able to access project board and giving false flag to issue |
nemesifier
left a comment
There was a problem hiding this comment.
@prathmeshkulkarni-coder is it a permission issue? Can you at least check if it can detect whether it is assigned to any project or not and if so, know at least the project ID? We don't need to access the project board, (infact, we have to avoid that completely as it's a waste of compute) we just need to know it is assigned to one of the two designated boards.
|
@nemesifier |
|
I updated the permissions of the github app to allow reading projects, please double check on your end. |
035dcae to
52e3df0
Compare
52e3df0 to
c843e8d
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (3)
docs/developer/reusable-github-utils.rst (1)
76-82: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick winClarify "if the issue is not resolved" wording.
"Automatically closed after 24 hours if the issue is not resolved" is ambiguous — a contributor could misread "the issue" as the linked GitHub issue rather than the PR's missing-reference problem. The 24-hour clock starts from the warning comment, and the closure condition is that the PR still lacks a validated issue reference. This was flagged in a previous review and remains unresolved.
📝 Proposed wording fix
- warning comment, and are automatically closed after 24 hours if the - issue is not resolved. + warning comment, and are automatically closed after 24 hours if the + PR is not updated to reference a validated issue.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/developer/reusable-github-utils.rst` around lines 76 - 82, Update the PR validation description to remove the ambiguous “if the issue is not resolved” wording and make it explicit that the 24-hour timer starts from the warning comment and the PR is closed only if it still lacks a validated issue reference. Use the existing “PR validation” section in reusable-github-utils.rst to rephrase the closure condition clearly so it refers to the PR’s missing-reference problem, not the linked GitHub issue..github/actions/bot-autoassign/tests/test_stale_pr_bot.py (2)
863-950: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for
ValidationAPIErrorre-raise whenvalidate_pr_issuesraises.
process_stale_prsre-raisesValidationAPIErrorwhenvalidate_pr_issuesthrows, but no test inTestStalePRBotInvalidCheckcovers this error path. Per the PR objectives, API or permission errors should fail the workflow for retry. This was flagged in a previous review and remains unresolved.As per coding guidelines: "Add or update tests for every behavior change."
Would you like me to generate the missing test case?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py around lines 863 - 950, Add a test in TestStalePRBotInvalidCheck to cover the error path where StalePRBot.process_stale_prs re-raises ValidationAPIError when validate_pr_issues throws. Mock a PR with the invalid label and patch bot.validate_pr_issues to raise ValidationAPIError, then assert the exception propagates instead of being swallowed. Place the new case alongside the existing invalid-pr tests so it is easy to find with process_stale_prs and validate_pr_issues.Source: Coding guidelines
915-915: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winMove
timedeltaimport to the top of the file.The local
from datetime import timedeltaimport still violates the project's coding guidelines, which require imports at the top of the file unless deferral is necessary.timedeltahas no deferral justification here. This was flagged in a previous review and remains unresolved.As per coding guidelines: "Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)."
♻️ Proposed fix
- from datetime import timedelta - mock_comment.created_at = datetime.now(timezone.utc) - timedelta(hours=25)Add
timedeltato the existing top-leveldatetimeimport:-from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py at line 915, The test file still has a deferred `timedelta` import inside the test scope, which violates the import-at-top convention. Move `timedelta` into the existing top-level `datetime` import near the other imports in `test_stale_pr_bot.py`, and remove the local import so `timedelta` is referenced only from the module-level import.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/actions/bot-autoassign/base.py:
- Around line 59-67: Remove the unnecessary intra-method blank lines in
base.py’s bot auto-assign logic, especially around the repository/project item
extraction in the method that builds repo_data, issue_data, v2_items,
classic_items, and nodes. Tighten the body by deleting the blank lines that
contain trailing whitespace so the method has no empty interior separators and
passes openwisp-qa-format/flake8 W293.
- Around line 58-72: The GraphQL fetch in the project-title lookup path
currently swallows API failures by returning an empty list; update the logic in
the method that calls github.requester.graphql_query to check the top-level
errors field in the GraphQL result and raise an exception when present instead
of continuing. Keep the existing parsing of
issue_data/projectV2Items/projectItems for successful responses, but fail fast
so validate_pr_issues does not treat permission or API errors as “no projects.”
---
Duplicate comments:
In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py:
- Around line 863-950: Add a test in TestStalePRBotInvalidCheck to cover the
error path where StalePRBot.process_stale_prs re-raises ValidationAPIError when
validate_pr_issues throws. Mock a PR with the invalid label and patch
bot.validate_pr_issues to raise ValidationAPIError, then assert the exception
propagates instead of being swallowed. Place the new case alongside the existing
invalid-pr tests so it is easy to find with process_stale_prs and
validate_pr_issues.
- Line 915: The test file still has a deferred `timedelta` import inside the
test scope, which violates the import-at-top convention. Move `timedelta` into
the existing top-level `datetime` import near the other imports in
`test_stale_pr_bot.py`, and remove the local import so `timedelta` is referenced
only from the module-level import.
In `@docs/developer/reusable-github-utils.rst`:
- Around line 76-82: Update the PR validation description to remove the
ambiguous “if the issue is not resolved” wording and make it explicit that the
24-hour timer starts from the warning comment and the PR is closed only if it
still lacks a validated issue reference. Use the existing “PR validation”
section in reusable-github-utils.rst to rephrase the closure condition clearly
so it refers to the PR’s missing-reference problem, not the linked GitHub issue.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 57b86e5f-f6e1-4398-9559-998ed5e377e8
📒 Files selected for processing (10)
.github/actions/bot-autoassign/base.py.github/actions/bot-autoassign/issue_assignment_bot.py.github/actions/bot-autoassign/stale_pr_bot.py.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py.github/actions/bot-autoassign/tests/test_stale_pr_bot.py.github/actions/bot-autoassign/utils.py.github/workflows/bot-autoassign-pr-issue-link.yml.github/workflows/reusable-bot-autoassign.ymldocs/developer/reusable-github-utils.rstsetup.py
📜 Review details
⏰ Context from checks skipped due to timeout. (14)
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.0.0
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.0.0
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{py,html,txt}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
setup.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)
Avoid unnecessary blank lines inside function and method bodies
Add or update tests for every behavior change
Runopenwisp-qa-formatafter editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, usepython -m pytest <test_path> --cov=<dotted.module.path> --cov-report=term-missing
Watch for unsafe file paths, unsafe subprocess usage, token or secret exposure, and changes that could weaken QA or release safeguards
Write comments and docstrings only when they explain why code is shaped a certain way; place comments before the relevant code block instead of scattering them inside it
Files:
setup.py
**/*.{md,rst,txt}
📄 CodeRabbit inference engine (AGENTS.md)
Update documentation when behavior, settings, public APIs, setup steps, QA rules, or supported versions change
Files:
docs/developer/reusable-github-utils.rst
🧠 Learnings (1)
📚 Learning: 2026-06-15T22:45:01.529Z
Learnt from: nemesifier
Repo: openwisp/openwisp-utils PR: 701
File: setup.py:72-72
Timestamp: 2026-06-15T22:45:01.529Z
Learning: When reviewing changes that require a minimum Selenium Python version for specific APIs (e.g., BiDi features like `Options.enable_bidi`, console/message handlers like `Script.add_console_message_handler`, or event handlers like `BrowsingContext.add_event_handler`), do not use the Selenium API docs site (`https://www.selenium.dev/selenium/docs/api/py/`) as the source of introduction/version history, since it reflects the latest API shape. Instead, confirm feature availability by checking the official Python changelog (`py/CHANGES` in the Selenium repo) and/or inspecting the Selenium source (tags/commits corresponding to candidate versions). Only accept the stated minimum version after verifying that the referenced APIs exist in that Selenium version (e.g., Selenium 4.32.0 includes the BiDi APIs used by openwisp-utils’ BiDi-related PR `#701` as of that version).
Applied to files:
setup.py
🪛 ast-grep (0.44.1)
.github/actions/bot-autoassign/utils.py
[warning] 133-133: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').
(xpath-injection-python)
[warning] 133-133: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-1333] Inefficient Regular Expression Complexity.
(redos-non-literal-regex-python)
🪛 zizmor (1.26.1)
.github/workflows/bot-autoassign-pr-issue-link.yml
[error] 3-5: use of fundamentally insecure workflow trigger (dangerous-triggers): pull_request_target is almost always used insecurely
(dangerous-triggers)
🔇 Additional comments (15)
.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py (1)
888-1100: LGTM!.github/workflows/bot-autoassign-pr-issue-link.yml (1)
5-5: LGTM!.github/workflows/reusable-bot-autoassign.yml (1)
15-24: LGTM!Also applies to: 45-46
docs/developer/reusable-github-utils.rst (1)
177-177: LGTM!setup.py (1)
81-81: 🔒 Security & PrivacyPyGithub 2.5.0 already matches the expected GraphQL API
requester.graphql_query()returns the(headers, data)tuple thatget_issue_projectsunpacks, so this pin is fine.> Likely an incorrect or invalid review comment..github/actions/bot-autoassign/base.py (6)
39-46:projectItems/projectV2Itemsfetch only the first 10 items.
first: 10can miss the approved OpenWISP board when an issue is on more boards, falsely marking a valid PR invalid. Paginate viapageInfo/endCursor.
107-146: Move method-local imports to module scope.
extract_all_linked_issues(line 108) andGithubException(line 132) are imported inside the method without a deferred-import need.As per coding guidelines,
**/*.pyfiles should place imports at the top unless deferral is necessary.Source: Coding guidelines
12-16: LGTM!
74-105: LGTM!
148-202: LGTM!
204-263: LGTM!.github/actions/bot-autoassign/utils.py (1)
115-149: LGTM!.github/actions/bot-autoassign/issue_assignment_bot.py (2)
439-450: Validate before auto-assigning linked issues.For
opened/reopened,auto_assign_issues_from_pr()(Line 440) still runs beforevalidate_pr_issues()(Line 450) — the block falls through rather than gating on validity. An external PR that references an unvalidated issue (open but not on a required project board) can still get that issue auto-assigned to the author before the PR is labeledinvalid, sinceauto_assign_issues_from_pr→get_valid_linked_issuesdoes not check project-board membership.Gate the auto-assign on validity, e.g. load
pr_obj/is_validfirst and only callauto_assign_issues_from_pr()when valid.
451-474: LGTM!.github/actions/bot-autoassign/stale_pr_bot.py (1)
15-16: LGTM!Also applies to: 375-431, 516-517
| repo_data = result.get("data", {}).get("repository", {}) or {} | ||
| issue_data = repo_data.get("issue", {}) or {} | ||
|
|
||
| # Combine both V2 and Classic projects just in case | ||
| v2_items = issue_data.get("projectV2Items", {}) or {} | ||
| classic_items = issue_data.get("projectItems", {}) or {} | ||
|
|
||
| nodes = (v2_items.get("nodes", []) or []) + (classic_items.get("nodes", []) or []) | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Remove blank lines with trailing whitespace inside the method body.
Lines 61, 65 (and the interior blank at 67) are unnecessary intra-body blank lines and carry trailing whitespace, which will fail openwisp-qa-format (flake8 W293).
As per coding guidelines: "Avoid unnecessary blank lines inside function and method bodies" and "Run openwisp-qa-format after editing".
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/bot-autoassign/base.py around lines 59 - 67, Remove the
unnecessary intra-method blank lines in base.py’s bot auto-assign logic,
especially around the repository/project item extraction in the method that
builds repo_data, issue_data, v2_items, classic_items, and nodes. Tighten the
body by deleting the blank lines that contain trailing whitespace so the method
has no empty interior separators and passes openwisp-qa-format/flake8 W293.
Source: Coding guidelines
ac4d021 to
2e39c9e
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
.github/actions/bot-autoassign/tests/test_stale_pr_bot.py (2)
915-915: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winMove
timedeltaimport to the top of the file.The local
from datetime import timedeltainside the test body violates the project's coding guidelines, which require imports at the top of the file unless deferral is necessary.timedeltahas no deferral justification here.As per coding guidelines: "Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)"
♻️ Proposed fix
- from datetime import timedelta - mock_comment.created_at = datetime.now(timezone.utc) - timedelta(hours=25)Add
timedeltato the existing top-leveldatetimeimport:-from datetime import datetime, timezone +from datetime import datetime, timedelta, timezone🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py at line 915, Move the deferred timedelta import in the test body to the module-level imports to match the project’s import rules. Update the existing top-of-file datetime import in test_stale_pr_bot.py to include timedelta, and remove the local import from the test method so the test logic still uses timedelta from the top-level scope.Source: Coding guidelines
863-950: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd a test for
ValidationAPIErrorre-raise whenvalidate_pr_issuesraises.
process_stale_prsre-raisesValidationAPIErrorwhenvalidate_pr_issuesthrows, but no test inTestStalePRBotInvalidCheckcovers this error path. Per the PR objectives, API or permission errors should fail the workflow for retry. As per coding guidelines, tests should be added for every behavior change.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py around lines 863 - 950, `TestStalePRBotInvalidCheck` is missing coverage for the `ValidationAPIError` re-raise path in `StalePRBot.process_stale_prs`. Add a test that patches `validate_pr_issues` to raise `ValidationAPIError`, then assert `process_stale_prs` propagates that exception and does not call label removal, issue comments, or `edit`. Use the existing `mock_pr`, `bot_env["repo"].get_pulls`, and `process_stale_prs` setup to keep the new test aligned with the other invalid-PR cases.Source: Coding guidelines
docs/developer/reusable-github-utils.rst (1)
74-79: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winClarify "if the issue is not resolved" wording.
Line 78-79 still says "automatically closed after 24 hours if the issue is not resolved." A contributor could misread "the issue" as the linked GitHub issue rather than the PR's missing-reference problem. While the detailed section at lines 110-116 is clear, this summary line remains ambiguous.
📝 Proposed wording fix
- warning comment, and are automatically closed after 24 hours if the - issue is not resolved. + warning comment, and are automatically closed after 24 hours if the + PR is not updated to reference a validated issue.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/developer/reusable-github-utils.rst` around lines 74 - 79, Clarify the PR validation summary text so “if the issue is not resolved” clearly refers to the pull request’s invalid-reference problem, not the linked GitHub issue. Update the summary wording in the reusable-github-utils documentation near the PR validation section to explicitly say the pull request is closed after 24 hours if the missing/invalid reference is not fixed, keeping it consistent with the detailed explanation below.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/actions/bot-autoassign/base.py:
- Around line 66-84: The GraphQL response handling in the repository lookup path
can still crash when result["data"] is null because repo_data is read via
result.get("data", {}).get("repository"); update the parsing in the same flow
that checks repo_data, issue_node, and project_items to use a safe fallback like
result.get("data") or {} before accessing nested fields. Also add a test
covering the {"data": null, "errors": [...]} shape in the GraphQL error path,
since the existing error test only exercises GithubException.
---
Duplicate comments:
In @.github/actions/bot-autoassign/tests/test_stale_pr_bot.py:
- Line 915: Move the deferred timedelta import in the test body to the
module-level imports to match the project’s import rules. Update the existing
top-of-file datetime import in test_stale_pr_bot.py to include timedelta, and
remove the local import from the test method so the test logic still uses
timedelta from the top-level scope.
- Around line 863-950: `TestStalePRBotInvalidCheck` is missing coverage for the
`ValidationAPIError` re-raise path in `StalePRBot.process_stale_prs`. Add a test
that patches `validate_pr_issues` to raise `ValidationAPIError`, then assert
`process_stale_prs` propagates that exception and does not call label removal,
issue comments, or `edit`. Use the existing `mock_pr`,
`bot_env["repo"].get_pulls`, and `process_stale_prs` setup to keep the new test
aligned with the other invalid-PR cases.
In `@docs/developer/reusable-github-utils.rst`:
- Around line 74-79: Clarify the PR validation summary text so “if the issue is
not resolved” clearly refers to the pull request’s invalid-reference problem,
not the linked GitHub issue. Update the summary wording in the
reusable-github-utils documentation near the PR validation section to explicitly
say the pull request is closed after 24 hours if the missing/invalid reference
is not fixed, keeping it consistent with the detailed explanation below.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: c6b24465-da5e-4c0e-9f7d-88f9d16ac2aa
📒 Files selected for processing (10)
.github/actions/bot-autoassign/base.py.github/actions/bot-autoassign/issue_assignment_bot.py.github/actions/bot-autoassign/stale_pr_bot.py.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py.github/actions/bot-autoassign/tests/test_stale_pr_bot.py.github/actions/bot-autoassign/utils.py.github/workflows/bot-autoassign-pr-issue-link.yml.github/workflows/reusable-bot-autoassign.ymldocs/developer/reusable-github-utils.rstsetup.py
📜 Review details
⏰ Context from checks skipped due to timeout. (14)
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.0.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | 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.13 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.1.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~=4.2.0
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{py,html,txt}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
setup.py
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Place imports at the top of the file; only defer imports when necessary (e.g., Django model imports inside functions or methods where the app registry is not yet ready)
Avoid unnecessary blank lines inside function and method bodies
Add or update tests for every behavior change
Runopenwisp-qa-formatafter editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, usepython -m pytest <test_path> --cov=<dotted.module.path> --cov-report=term-missing
Watch for unsafe file paths, unsafe subprocess usage, token or secret exposure, and changes that could weaken QA or release safeguards
Write comments and docstrings only when they explain why code is shaped a certain way; place comments before the relevant code block instead of scattering them inside it
Files:
setup.py
**/*.{md,rst,txt}
📄 CodeRabbit inference engine (AGENTS.md)
Update documentation when behavior, settings, public APIs, setup steps, QA rules, or supported versions change
Files:
docs/developer/reusable-github-utils.rst
🧠 Learnings (1)
📚 Learning: 2026-06-15T22:45:01.529Z
Learnt from: nemesifier
Repo: openwisp/openwisp-utils PR: 701
File: setup.py:72-72
Timestamp: 2026-06-15T22:45:01.529Z
Learning: When reviewing changes that require a minimum Selenium Python version for specific APIs (e.g., BiDi features like `Options.enable_bidi`, console/message handlers like `Script.add_console_message_handler`, or event handlers like `BrowsingContext.add_event_handler`), do not use the Selenium API docs site (`https://www.selenium.dev/selenium/docs/api/py/`) as the source of introduction/version history, since it reflects the latest API shape. Instead, confirm feature availability by checking the official Python changelog (`py/CHANGES` in the Selenium repo) and/or inspecting the Selenium source (tags/commits corresponding to candidate versions). Only accept the stated minimum version after verifying that the referenced APIs exist in that Selenium version (e.g., Selenium 4.32.0 includes the BiDi APIs used by openwisp-utils’ BiDi-related PR `#701` as of that version).
Applied to files:
setup.py
🪛 ast-grep (0.44.1)
.github/actions/bot-autoassign/utils.py
[warning] 133-133: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-1333] Inefficient Regular Expression Complexity.
(redos-non-literal-regex-python)
[warning] 133-133: XPath query is request-/variable-derived; use parameterized XPath to prevent injection.
Context: re.findall(pattern, pr_body, re.IGNORECASE)
Note: [CWE-643] Improper Neutralization of Data within XPath Expressions ('XPath Injection').
(xpath-injection-python)
🪛 zizmor (1.26.1)
.github/workflows/bot-autoassign-pr-issue-link.yml
[error] 9-9: overly broad permissions (excessive-permissions): issues: write is overly broad at the workflow level
(excessive-permissions)
[error] 10-10: overly broad permissions (excessive-permissions): pull-requests: write is overly broad at the workflow level
(excessive-permissions)
[warning] 9-9: permissions without explanatory comments (undocumented-permissions): needs an explanatory comment
(undocumented-permissions)
🔇 Additional comments (37)
.github/actions/bot-autoassign/tests/test_issue_assignment_bot.py (7)
888-919: LGTM!
922-983: LGTM!
984-1012: LGTM!
1013-1039: LGTM!
1040-1066: LGTM!
1067-1105: LGTM!
1107-1158: LGTM!.github/actions/bot-autoassign/tests/test_stale_pr_bot.py (4)
863-879: LGTM!
881-901: LGTM!
902-928: LGTM!
929-950: LGTM!.github/workflows/bot-autoassign-pr-issue-link.yml (2)
5-10: LGTM!
47-47: LGTM!.github/workflows/reusable-bot-autoassign.yml (3)
15-29: LGTM!
50-51: LGTM!
69-69: LGTM!docs/developer/reusable-github-utils.rst (13)
1-19: LGTM!
81-120: LGTM!
121-181: LGTM!
207-218: LGTM!
278-281: LGTM!
311-314: LGTM!
328-351: LGTM!
374-377: LGTM!
388-401: LGTM!
454-496: LGTM!
596-642: LGTM!
687-690: LGTM!
755-762: LGTM!setup.py (1)
81-81: LGTM!.github/actions/bot-autoassign/base.py (3)
51-51: 🎯 Functional CorrectnessPagination gap persists.
projectItems(first: 20)still reads only a single page; an issue assigned to more than 20 project items could miss the approved OpenWISP board and be treated as unvalidated. UsepageInfo/endCursorpagination.
126-126: 📐 Maintainability & Code QualityMove method-local imports to module scope.
extract_all_linked_issues(Line 126) andGithubException(Line 149) have no deferred-import requirement and should be imported at the top of the file. As per coding guidelines: "Place imports at the top of the file; only defer imports when necessary".Also applies to: 149-160
Source: Coding guidelines
5-24: LGTM!Also applies to: 41-44, 216-275
.github/actions/bot-autoassign/issue_assignment_bot.py (2)
446-449: 🗄️ Data Integrity & IntegrationAuto-assignment still runs before validation.
auto_assign_issues_from_pr()(Line 447) executes unconditionally beforevalidate_pr_issues()(Line 449), so an external PR referencing an unvalidated issue can still assign that issue to the author before the PR is labeledinvalid. Gateauto_assign_issues_from_pr()onis_valid.
439-445: LGTM!Also applies to: 450-473
.github/actions/bot-autoassign/utils.py (1)
115-149: LGTM!.github/actions/bot-autoassign/stale_pr_bot.py (1)
4-13: LGTM!Also applies to: 372-427, 513-514
| repo_data = result.get("data", {}).get("repository") | ||
| if not repo_data: | ||
| raise ValueError( | ||
| f"GraphQL could not access repository {owner}/{repo_name}; " | ||
| "possible GitHub API or permission error" | ||
| ) | ||
| issue_node = repo_data.get("issue") | ||
| if issue_node is None: | ||
| raise ValueError( | ||
| f"GraphQL could not access issue {owner}/{repo_name}#{issue_number}; " | ||
| "possible GitHub API or permission error" | ||
| ) | ||
| project_items = issue_node.get("projectItems") | ||
| if project_items is None: | ||
| raise ValueError( | ||
| f"GraphQL could not read project assignments for " | ||
| f"{owner}/{repo_name}#{issue_number}; " | ||
| "possible GitHub API or permission error" | ||
| ) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
p = Path('.github/actions/bot-autoassign/base.py')
lines = p.read_text().splitlines()
for i in range(1, min(len(lines), 260)+1):
if 55 <= i <= 95:
print(f"{i:4d}: {lines[i-1]}")
PY
printf '\n--- search usage/tests ---\n'
rg -n "get_issue_projects|GraphQL could not access repository|possible GitHub API or permission error|result.get\\(\"data\"" .github/actions/bot-autoassign -SRepository: openwisp/openwisp-utils
Length of output: 4099
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '--- tests around get_issue_projects ---'
sed -n '920,990p' .github/actions/bot-autoassign/tests/test_issue_assignment_bot.py
printf '\n%s\n' '--- graphql_query definitions/usages ---'
rg -n "def graphql_query|graphql_query\\(" .github/actions/bot-autoassign -SRepository: openwisp/openwisp-utils
Length of output: 3078
🌐 Web query:
GitHub GraphQL API error response data null errors response shape documentation
💡 Result:
When the GitHub GraphQL API encounters errors, the response structure follows the standard GraphQL specification [1][2]. A response containing errors will include a JSON object with a top-level errors key, and the data key may be present or set to null depending on the nature of the error [1][3]. The standard error response structure is as follows: { "data": null, "errors": [ { "message": "Error description message", "locations": [ { "line": 1, "column": 1 } ] } ] } Key aspects of the error response shape: 1. Data Key: If the error is severe enough to prevent execution of the query (such as a validation or syntax error), the data key will typically be null [1][3]. However, in GraphQL, partial successes are possible; if some parts of a query succeed while others fail, the data key may contain the successfully retrieved information alongside an errors array containing the specific issues [1]. 2. Errors Array: This is a list of error objects [1]. Each object typically contains a message field describing the error [4][1]. 3. Additional Fields: Depending on the type of error, the error objects may include a locations field (indicating where in the query the error occurred) or other debugging information such as reference codes for unexpected system bugs [1][3]. GitHub recommends checking for the presence of the errors key in the response before attempting to use the returned data in production environments [1][5].
Citations:
- 1: https://docs.github.com/en/enterprise-server@3.4/graphql/guides/using-the-explorer
- 2: https://github.com/orgs/community/discussions/78614
- 3: https://docs.github.com/en/graphql/guides/migrating-from-rest-to-graphql
- 4: https://github.blog/developer-skills/github/the-github-graphql-api/
- 5: https://github.com/orgs/community/discussions/114660
Handle GraphQL responses with data: null result.get("data", {}) still yields None for {"data": null, "errors": [...]}, so .get("repository") raises AttributeError before the intended ValueError. Use (result.get("data") or {}) and add a test for the null-data error shape; the current GraphQL error test only raises GithubException.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/actions/bot-autoassign/base.py around lines 66 - 84, The GraphQL
response handling in the repository lookup path can still crash when
result["data"] is null because repo_data is read via result.get("data",
{}).get("repository"); update the parsing in the same flow that checks
repo_data, issue_node, and project_items to use a safe fallback like
result.get("data") or {} before accessing nested fields. Also add a test
covering the {"data": null, "errors": [...]} shape in the GraphQL error path,
since the existing error test only exercises GithubException.
ec438f1 to
367db15
Compare
|
The issue is still there i am debugging the issue by checking what does the bot getting from project i hope it will be solve |
367db15 to
6efff15
Compare
|
@nemesifier , I got the bug
here the bot is getting no data from project and i also checked it for graphql V2 it is valid . |
|
@nemesifier , the PR get close by itself after 24hr |
6efff15 to
baaaa01
Compare
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Incremental review since the last reviewed commit ( The changed code is correct and null-safe on all changed lines: the GraphQL migration is valid, the added error handling short-circuits before parsing, and existing behavior tests were updated to match (success, single-project, GraphQL error, null Under lenient review scope, only critical bugs and security vulnerabilities are flagged; none apply here. Previously noted non-blocking concerns live on files/lines unchanged in this update and remain tracked by existing active review comments, so they are not duplicated. Files Reviewed (2 files, incremental)
Previous Review Summaries (5 snapshots, latest commit 991d6ee)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 991d6ee)Status: No Issues Found | Recommendation: Merge Incremental review since the last reviewed commit ( The change is purely cosmetic — the mock data is semantically identical and remains self-consistent with Previously noted non-blocking concerns live on files/lines unchanged in this update and remain tracked by existing active review comments, so they are not duplicated here. Files Reviewed (1 file, incremental)
Previous review (commit ba9f9e7)Status: No Issues Found | Recommendation: Merge Incremental review since the last reviewed commit ( The updated mocks are self-consistent with Previously noted non-blocking concerns live on files/lines unchanged in this update and remain tracked by existing active review comments, so they are not duplicated here. Files Reviewed (1 file, incremental)
Previous review (commit 3691a26)Status: No Issues Found | Recommendation: Merge Incremental review since the last reviewed commit ( Previously noted non-blocking concerns live on files/lines unchanged in this update and remain tracked by existing active review comments, so they are not duplicated here. Files Reviewed (1 file, incremental)
Previous review (commit f1b7ee1)Status: No Issues Found | Recommendation: Merge Incremental review since the last reviewed commit. The only change in this update is a single line in Previously noted non-blocking concerns live on files unchanged in this update and remain tracked by existing active review comments, so they are not duplicated here. Files Reviewed (1 file, incremental)
Previous review (commit baaaa01)Status: No Issues Found | Recommendation: Merge Lenient review focused on critical bugs and security vulnerabilities in changed lines only. The feature is well-scoped: it exempts maintainers/collaborators and configured bot authors, requires external PRs to reference a validated organization issue, labels invalid PRs, posts a single marker-tracked warning comment, and auto-closes after 24h via the stale job. Tests cover positive, negative, and edge cases, docs are updated, and the PR aligns with issue #709. No new critical or security issues were found on the changed lines. Notable non-blocking concerns (GraphQL Files Reviewed (10 files)
Reviewed by claude-opus-4.8 · Input: 30 · Output: 4.6K · Cached: 436.3K |
ba9f9e7 to
991d6ee
Compare
…issues openwisp#709 - Add pull_request_target edited/ready_for_review triggers - Implement regex issue references extraction helper - Add GraphQL project v2 board assignment check - Validate that PRs from external contributors link to open, labeled, and validated organization issues - Apply 'invalid' label and post warning comment if invalid - Automatically close invalid PRs after 24 hours in the stale PR workflow - Update documentation and add unit tests Closes openwisp#709
991d6ee to
e719d10
Compare
There was a problem hiding this comment.
I checked the project lookup again.
The openwisp-companion GitHub App installation now has organization_projects: read and repository_projects: read, and the GraphQL query returns the expected projects for openwisp/openwisp-utils#709.
Please rerun the test in openwisp/bot-testing-ground with the real app. That repo is using the reusable workflow from this branch, so it should now be able to read the project assignments.
Before merging, please also add owner: ${{ github.repository_owner }} to the direct actions/create-github-app-token@v3 step in .github/workflows/bot-autoassign-pr-issue-link.yml, so this repo uses the org installation token too.
The project-board check should stay. Labels alone are not enough for this feature because issue #709 explicitly requires the linked issue to be assigned to one of the contributor project boards.
a9442b9 to
ceeff76
Compare
|
@coderabbitai review |
✅ Action performedReview finished.
|
|
@nemesifier , the bot works perfectly now PR3 : When PR has issue link but issue is not valid ( Does not present on Project board) PR4 : When PR is updated with valid issue link bot removes the invalid tag . The bot works perfectly on my side and i tested every condition . |



Checklist
Reference to Existing Issue
Closes #709 .
Description of Changes
What this does
External contributors must now link their pull request to an issue that maintainers have already validated, before it can be reviewed. This helps filter out unsolicited, low-quality, or AI-generated PRs that don't address a problem maintainers actually want worked on.
How it works
When an external contributor opens or updates a PR, the bot checks if it references an issue that is:
invalidorwontfixopenwisporganization (same repo or another org repo)If no such issue is referenced, the bot:
invalidlabelA daily scheduled check re-evaluates flagged PRs:
invalidis removed automaticallyChanges in this PR
bot-autoassign-pr-issue-link.yml): now also runs oneditedandready_for_reviewevents, so contributors don't have to wait for the daily job to get re-checked after fixing their PR.utils.py): addedextract_all_linked_issues, which recognizes same-repo (#123), cross-repo (org/repo#123), and full issue URL references.base.py): addedvalidate_pr_issues, which applies the checks above and exempts owners, members, collaborators, and trusted bots (e.g. Dependabot).stale_pr_bot.py): automatically closes PRs still flaggedinvalid24 hours after the warning comment.test_issue_assignment_bot.pyandtest_stale_pr_bot.pyfor the positive, negative, and edge cases.docs/developer/reusable-github-utils.rstto describe the new triggers, requirements, and policy.Screenshot
N/A