summaryrefslogtreecommitdiff
path: root/scripts/pr_quality/errors.py
diff options
context:
space:
mode:
authorNatalia <124304+nessita@users.noreply.github.com>2026-03-27 22:41:10 -0300
committernessita <124304+nessita@users.noreply.github.com>2026-04-16 12:45:03 -0300
commit4593ae93ae39d0c33ef9edc667fe3425fcf61b28 (patch)
treeb63e75e7e8f20d140aa01bc7b0b3f90cad4b14c2 /scripts/pr_quality/errors.py
parent82a2465f71a1f5cbfe3811952c0d07482dea71c6 (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.py160
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`).",
+)