diff options
| author | Natalia <124304+nessita@users.noreply.github.com> | 2026-03-27 22:41:10 -0300 |
|---|---|---|
| committer | nessita <124304+nessita@users.noreply.github.com> | 2026-04-16 12:45:03 -0300 |
| commit | 4593ae93ae39d0c33ef9edc667fe3425fcf61b28 (patch) | |
| tree | b63e75e7e8f20d140aa01bc7b0b3f90cad4b14c2 /scripts/pr_quality/errors.py | |
| parent | 82a2465f71a1f5cbfe3811952c0d07482dea71c6 (diff) | |
Added automated quality checks for PRs as a GitHub Actions workflow.
This work adds automated PR quality checks as a GitHub Actions workflow
to enforce contribution requirements consistently and reduce the manual
burden on reviewers for incoming PRs.
Thanks to the many reviewers providing meaningful feedback.
Co-authored-by: Frank Wiles <frank@revsys.com>
Diffstat (limited to 'scripts/pr_quality/errors.py')
| -rw-r--r-- | scripts/pr_quality/errors.py | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/scripts/pr_quality/errors.py b/scripts/pr_quality/errors.py new file mode 100644 index 0000000000..8b98873f6d --- /dev/null +++ b/scripts/pr_quality/errors.py @@ -0,0 +1,160 @@ +"""Error and notification messages for PR quality checks. + +Message constants are plain 2-tuples of (title, body). Body strings use +str.format() placeholders; kwargs are supplied at Message() construction time. + +""" + +LEVEL_ERROR = ("🛑", "Error") +LEVEL_WARNING = ("⚠️", "Warning") + + +class Message: + """A PR quality check message bound to its runtime formatting kwargs.""" + + def __init__(self, title, body, **kwargs): + self.title = title + self.body = body + self.kwargs = kwargs + + def as_details(self, level=LEVEL_ERROR): + body = self.body.format(**self.kwargs) if self.kwargs else self.body + symbol, label = level + return ( + f"<details>\n<summary><strong>{symbol} {label}: {self.title}" + f"</strong></summary>\n\n{body}\n\n</details>" + ) + + +CONTRIBUTING_URL = "https://docs.djangoproject.com/en/dev/internals/contributing/" +SUBMITTING_URL = f"{CONTRIBUTING_URL}writing-code/submitting-patches/" +TRIAGING_URL = f"{CONTRIBUTING_URL}triaging-tickets/" +FORUM_URL = "https://forum.djangoproject.com" +TRAC_URL = "https://code.djangoproject.com" + + +CHECKS_HEADER = ( + "Thank you for your contribution to Django! This pull request has one or more " + "items that need attention before it can be accepted for review." +) + +CHECKS_FOOTER = ( + "If you have questions about these requirements, please review the " + f"[contributing guidelines]({SUBMITTING_URL}) or ask for help on the " + f"[Django Forum]({FORUM_URL}/c/internals/mentorship/10)." +) + +INCOMPLETE_CHECKLIST = ( + "Incomplete Checklist", + "The items in the **Checklist** section must be all understood and checked " + "accordingly before this PR can be accepted. These items confirm that you have " + f"read and followed the [Django contributing guidelines]({SUBMITTING_URL}).\n\n" + "**What to do:**\n\n" + "- Review each item and change `[ ]` to `[x]` once you have completed it.", +) + +INVALID_TRAC_STATUS = ( + "Trac Ticket Not Ready for a Pull Request", + "The referenced ticket **ticket-{ticket_id}** is not ready for a pull request. " + "A ticket must be in the *Accepted* stage, *assigned* status, and have no " + "resolution.\n\n" + "**Current state:** {current_state}\n\n" + "**What to do:**\n\n" + f"1. Check the ticket at {TRAC_URL}/ticket/{{ticket_id}}.\n" + "2. If *Unreviewed*, wait for a community member to accept it. " + "A ticket cannot be accepted by its reporter.\n" + "3. If *Ready for Checkin*, there is already a solution for it.\n" + "4. If *Someday/Maybe*, discuss on the " + f"[Django Forum]({FORUM_URL}/c/internals/5) before proceeding.\n" + "5. If resolved or closed, it is not eligible for a PR.\n" + "6. If not *assigned*, claim it by setting yourself as the owner.\n\n" + f"For more information on the Django triage process see {TRIAGING_URL}.", +) + +MISSING_AI_DESCRIPTION = ( + "AI Tool Usage Not Described", + "You indicated that AI tools were used in preparing this PR, but you have not " + "provided a description of which tools you used or how you used them. At least " + "5 words of description are required.\n\n" + "**What to do:**\n\n" + "Add a brief description below the checked AI disclosure checkbox in your PR " + "description. For example:\n\n" + "```\n" + "- [x] **If AI tools were used**, I have disclosed which ones, and fully reviewed " + "and verified their output.\n\n" + "Used GitHub Copilot for initial code generation. All output was manually " + "reviewed, tested, and verified for correctness.\n" + "```\n\n" + "Your description should cover:\n\n" + "- Which AI tool(s) and versions you used (e.g. GitHub Copilot, ChatGPT, Claude)\n" + "- How you used them (e.g. code generation, writing tests, drafting commit " + "messages)\n" + "- How you reviewed the generated result\n\n" + "Please note that you are fully responsible for the correctness and quality of all " + "content in this PR, regardless of how it was generated.", +) + +MISSING_AI_DISCLOSURE = ( + "AI Tool Usage Not Disclosed", + "You must select exactly one checkbox in the AI Assistance Disclosure section of " + "your PR description.\n\n" + "**What to do:**\n\n" + "Change `[ ]` to `[x]` for either:\n\n" + '1. "**No AI tools were used** in preparing this PR."\n\n' + "OR\n\n" + '2. "**If AI tools were used**, I have disclosed which ones, and fully reviewed ' + 'and verified their output."\n\n' + "**Key Requirements:**\n\n" + "- Select only one option\n" + "- Do not check both boxes\n" + "- Do not leave both unchecked\n" + "- If selecting the second option, provide details of which AI tools were used.", +) + +MISSING_DESCRIPTION = ( + "Missing PR Description", + "Your PR description must be substantive and meaningful. The placeholder text " + '"*Provide a concise overview of the issue or rationale behind the proposed ' + 'changes.*" is not acceptable.\n\n' + "**What to do:**\n\n" + "Write a description that contains at least 5 words and addresses:\n\n" + "- What problem does this PR solve?\n" + "- Why is this change necessary?\n" + "- What approach did you take?\n\n" + "A meaningful description helps reviewers understand the intent of your change " + "quickly and increases the likelihood that your PR will be reviewed promptly.", +) + +MISSING_HAS_PATCH_FLAG = ( + "Incorrect Trac Ticket Flag", + "The referenced ticket **ticket-{ticket_id}** does not have the *Has patch* " + "flag set in Trac. This flag must be checked before a pull request can be " + "reviewed.\n\n" + "**What to do:**\n\n" + f"1. Open the ticket at {TRAC_URL}/ticket/{{ticket_id}}.\n" + "2. Scroll down and check the *Has patch* checkbox on the ticket.\n" + "3. Save the ticket.\n\n" + f"For more information see {TRIAGING_URL}#has-patch.", +) + +MISSING_TICKET_IN_PR_TITLE = ( + "Ticket Number Missing from PR Title", + "The PR title does not include the ticket number **#{ticket_id}**. Including " + "it allows Trac to automatically link this pull request to the ticket.\n\n" + "**What to do:**\n\n" + "Edit the PR title to follow the commit message format, for example:\n\n" + "`Fixed #{ticket_id} -- <description>.`", +) + +MISSING_TRAC_TICKET = ( + "Missing Trac Ticket", + "This PR does not include a valid Trac ticket reference.\n\n" + "**What to do:**\n\n" + f"1. Visit {TRAC_URL} and find or file the appropriate ticket for your change.\n" + "2. Wait for the ticket to be triaged and accepted before submitting a PR. " + "Patches submitted against unreviewed tickets are unlikely to be merged.\n" + "3. Edit the **Trac ticket number** section of your PR description to include " + "the ticket in the format `ticket-NNNNN` (e.g. `ticket-36991`).\n\n" + "For PRs with fewer than {threshold} lines changed (additions + deletions), you " + "may write `N/A` in the ticket field instead (e.g. `N/A - typo fix`).", +) |
