Skip to content

[ci] Flag invalid pull requests that do not reference validate #709 - #716

Merged
nemesifier merged 11 commits into
openwisp:masterfrom
prathmeshkulkarni-coder:feature/709-flag-invalid-prs
Jul 29, 2026
Merged

[ci] Flag invalid pull requests that do not reference validate #709#716
nemesifier merged 11 commits into
openwisp:masterfrom
prathmeshkulkarni-coder:feature/709-flag-invalid-prs

Conversation

@prathmeshkulkarni-coder

@prathmeshkulkarni-coder prathmeshkulkarni-coder commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Checklist

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

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.

Maintainers, org members, and collaborators are unaffected — they can keep opening PRs as before, with or without a linked issue.

How it works

  1. When an external contributor opens or updates a PR, the bot checks if it references an issue that is:

    • open
    • labeled with something other than invalid or wontfix
    • in the openwisp organization (same repo or another org repo)
    • assigned to one of the approved contributor project boards
  2. If no such issue is referenced, the bot:

    • adds the invalid label
    • posts a comment explaining why, and how to fix it
  3. A daily scheduled check re-evaluates flagged PRs:

    • if the PR now links to a validated issue → invalid is removed automatically
    • if it's still unresolved 24 hours after the warning comment → the PR is closed automatically

Changes in this PR

  • Workflow triggers (bot-autoassign-pr-issue-link.yml): now also runs on edited and ready_for_review events, so contributors don't have to wait for the daily job to get re-checked after fixing their PR.
  • Issue reference parsing (utils.py): added extract_all_linked_issues, which recognizes same-repo (#123), cross-repo (org/repo#123), and full issue URL references.
  • Validation logic (base.py): added validate_pr_issues, which applies the checks above and exempts owners, members, collaborators, and trusted bots (e.g. Dependabot).
  • Bot behavior: labels invalid PRs, posts the warning comment, and automatically removes the label once the PR is updated to reference a validated issue.
  • Scheduled cleanup (stale_pr_bot.py): automatically closes PRs still flagged invalid 24 hours after the warning comment.
  • Tests: added coverage in test_issue_assignment_bot.py and test_stale_pr_bot.py for the positive, negative, and edge cases.
  • Docs: updated docs/developer/reusable-github-utils.rst to describe the new triggers, requirements, and policy.
  • Tokens:Split the token into read and write for least privilege

Screenshot

N/A

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@nemesifier, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 10 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 70d2b772-28f7-44ef-a28a-a0c45ead3031

📥 Commits

Reviewing files that changed from the base of the PR and between 89e1004 and e8a498e.

📒 Files selected for processing (2)
  • .github/workflows/bot-autoassign-pr-issue-link.yml
  • docs/developer/reusable-github-utils.rst

Note

Reviews paused

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

Use the following commands to manage reviews:

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

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The bot validates external pull requests against linked, open, labeled issues assigned to required contributor projects. Assignment and stale-PR workflows use separate validation and write clients, manage invalid labels and marker comments, and close unresolved invalid pull requests after 24 hours. Reusable workflows provide scoped tokens, with tests and documentation covering the behavior.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PullRequestEvent
  participant IssueAssignmentBot
  participant GitHubBot
  participant GitHubAPI
  PullRequestEvent->>IssueAssignmentBot: handle_pull_request(event)
  IssueAssignmentBot->>GitHubAPI: fetch pull request
  IssueAssignmentBot->>GitHubBot: validate_pr_issues(pr)
  GitHubBot->>GitHubAPI: query linked issues and project assignments
  GitHubAPI-->>GitHubBot: issue metadata and project node IDs
  alt valid
    IssueAssignmentBot->>GitHubAPI: assign linked issues and remove invalid label
  else invalid
    IssueAssignmentBot->>GitHubAPI: add invalid label and warning comment
  end
Loading

Possibly related PRs

Suggested labels: enhancement, github_actions, helper-bots

Suggested reviewers: eeshu-yadav, nemesifier


Caution

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

  • Ignore

❌ Failed checks (1 error)

Check name Status Explanation Resolution
General Rules ❌ Error docs/developer/reusable-github-utils.rst still says the bot does not auto-close PRs and shows an outdated concurrency example, so the PR leaves incorrect docs. Update the docs to note that only invalid PRs auto-close after 24h, and sync the PR Issue Link example concurrency stanza with the workflow.
✅ Passed checks (7 passed)
Check name Status Explanation
Title check ✅ Passed The title is descriptive and matches the PR's main goal of flagging invalid PRs tied to issue validation.
Description check ✅ Passed The description largely matches the template, including checklist, issue reference, change summary, and screenshot section.
Linked Issues check ✅ Passed The changes satisfy #709 by validating linked issues, exempting maintainers, handling invalid labeling/comments, stale closure, and API error retries.
Out of Scope Changes check ✅ Passed No clear out-of-scope code changes were introduced beyond the linked issue's workflows, bot logic, tests, docs, and token handling.
Bug Fixes ✅ Passed PASS: This is workflow/bot automation, not a core user-facing bug fix; the added tests are mocked unit tests, so the UI/regression criteria don’t apply.
Features ✅ Passed Linked issue #709 exists; docs add a PR validation section; new tests cover validation/stale flows; no UI work, so screenshots aren’t needed.
Changes ✅ Passed Docs were updated for the new validation behavior, tests cover the non-trivial paths, and there are no UI changes or clear public API breaks.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

coveralls commented Jul 7, 2026

Copy link
Copy Markdown

Coverage Status

coverage: 97.645%. remained the same — prathmeshkulkarni-coder:feature/709-flag-invalid-prs into openwisp:master

@prathmeshkulkarni-coder

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot added enhancement github_actions Pull requests that update GitHub Actions code helper-bots Helper bots, release management automation labels Jul 7, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9f3639a and f227393.

📒 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.yml
  • docs/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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

_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

View job details

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

View job details

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

View job details

-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

View job details

 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

View job details

-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

View job details

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

View job details

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

View job details

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

View job details

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

View job details

 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

View job details

_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 & Availability

Let comment lookup failures surface. Returning False here 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 Correctness

Make sure bot_env["repo"] is the repo returned by bot.github.get_repo(...). These tests only stub bot_env["repo"].get_issue(...); if validate_pr_issues uses 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) from opened/reopened/edited/ready_for_review (validate → toggle label → conditionally comment), and has_bot_comment prevents duplicate warning comments across repeated invalid states. This matches the described contract in validate_pr_issues/has_bot_comment (base.py) and the accompanying tests.

.github/workflows/bot-autoassign-pr-issue-link.yml (1)

5-5: 🔒 Security & Privacy

Verify no PR-head checkout occurs downstream now that edited/ready_for_review also trigger this privileged workflow.

zizmor flags pull_request_target as 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 for opened/reopened/closed), but expanding it to edited/ready_for_review increases 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 via actions/checkout@v7 without overriding ref, 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 win

Validate 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!

Comment thread .github/actions/bot-autoassign/issue_assignment_bot.py Outdated
Comment thread .github/actions/bot-autoassign/stale_pr_bot.py Outdated
Comment thread .github/actions/bot-autoassign/stale_pr_bot.py Outdated
Comment thread .github/actions/bot-autoassign/stale_pr_bot.py Outdated
Comment thread .github/actions/bot-autoassign/stale_pr_bot.py Outdated
Comment thread .github/actions/bot-autoassign/stale_pr_bot.py
@prathmeshkulkarni-coder
prathmeshkulkarni-coder force-pushed the feature/709-flag-invalid-prs branch from f227393 to 976b572 Compare July 7, 2026 13:48

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between f227393 and 976b572.

📒 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.yml
  • docs/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 PyGithub GithubException(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 TestPRValidation coverage (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 the validate_pr_issues/handle_pull_request contract shown in base.py and issue_assignment_bot.py.

.github/actions/bot-autoassign/tests/test_stale_pr_bot.py (1)

865-950: LGTM!

The four TestStalePRBotInvalidCheck cases correctly map onto the branches in process_stale_prs for 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_target widened to edited/ready_for_review.

zizmor flags pull_request_target as 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 & Availability

No change needed: handle_pull_request() returning False still bubbles up to main() and sys.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!

Comment thread .github/actions/bot-autoassign/base.py Outdated
Comment thread .github/actions/bot-autoassign/stale_pr_bot.py Outdated
Comment thread .github/actions/bot-autoassign/tests/test_issue_assignment_bot.py
@prathmeshkulkarni-coder
prathmeshkulkarni-coder force-pushed the feature/709-flag-invalid-prs branch from 976b572 to cc80de8 Compare July 7, 2026 14:13

@nemesifier nemesifier left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

Comment thread .github/actions/bot-autoassign/base.py Outdated
f"Error fetching issue {owner}/{repo_name}#{issue_number}: {e}"
)
raise

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Too many blank lines, use blank lines sparingly.

@prathmeshkulkarni-coder

Copy link
Copy Markdown
Contributor Author

Sure thanks i'll give you update after testing

@prathmeshkulkarni-coder
prathmeshkulkarni-coder force-pushed the feature/709-flag-invalid-prs branch from cc80de8 to 035dcae Compare July 8, 2026 03:10

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

📥 Commits

Reviewing files that changed from the base of the PR and between 976b572 and 035dcae.

📒 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.yml
  • docs/developer/reusable-github-utils.rst
  • setup.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
Run openwisp-qa-format after editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, use python -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 & Privacy

PyGithub 2.5.0 is compatible here Requester.graphql_query still returns a (headers, data) tuple, and GithubException.status remains available for the 404 check.

Comment thread .github/actions/bot-autoassign/base.py Outdated
Comment thread .github/actions/bot-autoassign/base.py Outdated
Comment thread .github/actions/bot-autoassign/issue_assignment_bot.py Outdated
Comment thread .github/actions/bot-autoassign/tests/test_stale_pr_bot.py
Comment thread .github/actions/bot-autoassign/tests/test_stale_pr_bot.py Outdated
Comment thread docs/developer/reusable-github-utils.rst Outdated
@prathmeshkulkarni-coder

prathmeshkulkarni-coder commented Jul 8, 2026

Copy link
Copy Markdown
Contributor Author

@nemesifier , the bot is working but not as I thought

  1. test: collaborator pr test change bot-testing-ground#110 the bot didn't give invalid tag because i am collaborator
  2. Create test.md bot-testing-ground#111 here bot has given invalid tag because the PR didn't link to any issue (Testing for 24hr close PR)
  3. Test2 bot-testing-ground#114 here i was trying to start bot by linking valid issue and trying to remove invalid label but it didn't work
  4. Test3 bot-testing-ground#115 here i linked the PR to the valid issue but the bot still add invalid tag

I am still working on the bot

@prathmeshkulkarni-coder

Copy link
Copy Markdown
Contributor Author

I think here is the problem
Screenshot from 2026-07-08 12-57-51

Run if [ -n "$GITHUB_EVENT_PATH" ]; then
Issue Assignment Bot starting for event: pull_request_target
No linked issues found in PR body
Issue openwisp/openwisp-utils#709 is not assigned to any required project, skipping validation.
Added 'invalid' label to PR #115
Posted unvalidated issue warning comment on PR #115
Issue Assignment Bot completed

the bot can't able to access project board and giving false flag to issue

@nemesifier nemesifier left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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

@prathmeshkulkarni-coder

Copy link
Copy Markdown
Contributor Author

@nemesifier
Yes it is permission issue, the github can't able to give anything in return .It is very strict
there are two ways either giving permission or If member manually added label to issue like bug or accepted or verified
In second case if member add label then we can remove the project's board code completely.

@nemesifier

Copy link
Copy Markdown
Member

I updated the permissions of the github app to allow reading projects, please double check on your end.

@prathmeshkulkarni-coder
prathmeshkulkarni-coder force-pushed the feature/709-flag-invalid-prs branch from 035dcae to 52e3df0 Compare July 8, 2026 17:42
@coderabbitai coderabbitai Bot added bug and removed enhancement labels Jul 8, 2026
@prathmeshkulkarni-coder
prathmeshkulkarni-coder force-pushed the feature/709-flag-invalid-prs branch from 52e3df0 to c843e8d Compare July 8, 2026 17:50

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (3)
docs/developer/reusable-github-utils.rst (1)

76-82: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify "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 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. 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 win

Move timedelta import to the top of the file.

The local from datetime import timedelta import still 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. 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 timedelta to the existing top-level datetime import:

-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

📥 Commits

Reviewing files that changed from the base of the PR and between 035dcae and 52e3df0.

📒 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.yml
  • docs/developer/reusable-github-utils.rst
  • setup.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
Run openwisp-qa-format after editing
Prefer in-process tests so coverage tools can measure changed code
When checking coverage for a changed module, use python -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 & Privacy

PyGithub 2.5.0 already matches the expected GraphQL API
requester.graphql_query() returns the (headers, data) tuple that get_issue_projects unpacks, so this pin is fine.

			> Likely an incorrect or invalid review comment.
.github/actions/bot-autoassign/base.py (6)

39-46: projectItems/projectV2Items fetch only the first 10 items.

first: 10 can miss the approved OpenWISP board when an issue is on more boards, falsely marking a valid PR invalid. Paginate via pageInfo/endCursor.


107-146: Move method-local imports to module scope.

extract_all_linked_issues (line 108) and GithubException (line 132) are imported inside the method without a deferred-import need.

As per coding guidelines, **/*.py files 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 before validate_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 labeled invalid, since auto_assign_issues_from_prget_valid_linked_issues does not check project-board membership.

Gate the auto-assign on validity, e.g. load pr_obj/is_valid first and only call auto_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

Comment thread .github/actions/bot-autoassign/base.py Outdated
Comment thread .github/actions/bot-autoassign/base.py Outdated
This was referenced Jul 29, 2026
@openwisp-companion

Copy link
Copy Markdown

Hi @prathmeshkulkarni-coder,

Thank you for your interest in contributing to OpenWISP.

This pull request has been flagged because external contributors must target an issue validated by maintainers before requesting review.

Please link this pull request to a validated issue by adding Fixes #ISSUE_NUMBER, Closes #ISSUE_NUMBER, or Related to #ISSUE_NUMBER to the pull request description. The issue may be in this repository or another OpenWISP repository.

If there is no validated issue yet, please open one first and wait for maintainer validation before continuing with this pull request.

An issue is considered validated when it is open, has an appropriate label other than invalid or wontfix, and is assigned to one of the project boards mentioned in the OpenWISP Contributing Guidelines.

Please see the OpenWISP Anti AI Spam Policy.

Feel free to join the OpenWISP dev chatroom to coordinate with the development team.

If this is not resolved within 24 hours, this pull request will be closed automatically. Thank you for your understanding.

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

Labels

enhancement github_actions Pull requests that update GitHub Actions code helper-bots Helper bots, release management automation invalid

Development

Successfully merging this pull request may close these issues.

[feature] Flag invalid pull requests that do not reference validated issues

3 participants