1818- Scans open, non-draft PRs.
1919- A PR is considered "linked" if GitHub's GraphQL `closingIssuesReferences` returns > 0
2020 (covers both `Fixes #N` keywords in the body and issues linked via the GitHub UI).
21- - If a PR is not linked, the script posts up to 3 reminder comments spaced 7 days apart.
21+ - If a PR is not linked and no prior reminder is present, the script posts a single
22+ friendly reminder comment.
2223- PRs labeled `no-issue-needed` and bot-authored PRs are skipped.
2324"""
2425
2526import os
26- from datetime import datetime , timedelta , timezone
2727
2828import requests
2929from github import Github
3030
3131
3232REPO = "huggingface/diffusers"
3333REMINDER_MARKER = "<!-- pr-link-issue-reminder -->"
34- REMINDER_INTERVAL = timedelta (days = 7 )
35- MAX_REMINDERS = 3
3634BYPASS_LABELS = {"no-issue-needed" }
3735CONTRIBUTION_GUIDE_URL = "https://huggingface.co/docs/diffusers/main/en/conceptual/contribution#coding-with-ai-agents"
3836
@@ -62,39 +60,20 @@ def has_linked_issue(token, owner, name, number):
6260 return payload ["data" ]["repository" ]["pullRequest" ]["closingIssuesReferences" ]["totalCount" ] > 0
6361
6462
65- def reminder_history (pr ):
66- reminders = [c for c in pr .get_issue_comments () if REMINDER_MARKER in (c .body or "" )]
67- reminders .sort (key = lambda c : c .created_at )
68- return reminders
63+ def has_existing_reminder (pr ):
64+ return any (REMINDER_MARKER in (c .body or "" ) for c in pr .get_issue_comments ())
6965
7066
71- def reminder_body (author , count ):
72- remaining = MAX_REMINDERS - count
73- lines = [
74- REMINDER_MARKER ,
75- f"Hi @{ author } , this PR does not appear to link an issue it fixes. "
67+ def reminder_body (author ):
68+ return (
69+ f"{ REMINDER_MARKER } \n "
70+ f"Hi @{ author } , thanks for the PR! It does not appear to link an issue it fixes. "
7671 "If this PR addresses an existing issue, please add a closing keyword "
7772 "(e.g. `Fixes #1234`) to the PR description so the issue is linked. "
78- f"See the [contribution guide]({ CONTRIBUTION_GUIDE_URL } ) for more details." ,
79- "" ,
80- f"Reminder **{ count } /{ MAX_REMINDERS } **." ,
81- ]
82- if remaining > 0 :
83- lines [- 1 ] += (
84- f" If no linked issue is added within { REMINDER_INTERVAL .days } days, "
85- f"you will receive { remaining } more reminder(s)."
86- )
87- else :
88- lines [- 1 ] += (
89- " This is the final reminder. If this PR intentionally does not fix "
90- "a tracked issue, a maintainer can add the `no-issue-needed` label "
91- "to bypass this check."
92- )
93- return "\n " .join (lines )
94-
95-
96- def aware (ts ):
97- return ts if ts .tzinfo is not None else ts .replace (tzinfo = timezone .utc )
73+ f"See the [contribution guide]({ CONTRIBUTION_GUIDE_URL } ) for more details. "
74+ "If this PR intentionally does not fix a tracked issue, a maintainer can "
75+ "add the `no-issue-needed` label to silence this reminder."
76+ )
9877
9978
10079def main ():
@@ -103,8 +82,6 @@ def main():
10382 repo = g .get_repo (REPO )
10483 owner , name = REPO .split ("/" , 1 )
10584
106- now = datetime .now (timezone .utc )
107-
10885 for pr in repo .get_pulls (state = "open" ):
10986 if pr .draft :
11087 continue
@@ -118,21 +95,9 @@ def main():
11895 continue
11996 if has_linked_issue (token , owner , name , pr .number ):
12097 continue
121-
122- reminders = reminder_history (pr )
123- count = len (reminders )
124-
125- if count == 0 :
126- pr .create_issue_comment (reminder_body (author , 1 ))
98+ if has_existing_reminder (pr ):
12799 continue
128-
129- if count >= MAX_REMINDERS :
130- continue
131-
132- if now - aware (reminders [- 1 ].created_at ) < REMINDER_INTERVAL :
133- continue
134-
135- pr .create_issue_comment (reminder_body (author , count + 1 ))
100+ pr .create_issue_comment (reminder_body (author ))
136101
137102
138103if __name__ == "__main__" :
0 commit comments