Update script to ping only on waiting-for-author label (#4812)

* update script to ping only on certain waiting for author label

* Update .github/scripts/stale_issue_pr_ping.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update .github/scripts/stale_issue_pr_ping.py

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Fix docstring

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Evan Mattson
2026-03-20 19:39:22 +09:00
committed by GitHub
Unverified
parent 81e2336d47
commit 8edcb282f4
2 changed files with 30 additions and 17 deletions
+20 -11
View File
@@ -1,9 +1,11 @@
# Copyright (c) Microsoft. All rights reserved.
"""Scan open issues and PRs for stale follow-ups from external authors.
"""Scan open issues and PRs labeled 'waiting-for-author' for stale follow-ups.
If a team member commented and the external author hasn't replied within
DAYS_THRESHOLD days, post a reminder comment and add the 'needs-info' label.
Team members manually add the 'waiting-for-author' label when they need a
response from the external author. If the author hasn't replied within
DAYS_THRESHOLD days of the last team comment, post a reminder and add the
'requested-info' label to prevent duplicate pings.
"""
from __future__ import annotations
@@ -22,7 +24,8 @@ PING_COMMENT = (
"@{author}, friendly reminder — this issue is waiting on your response. "
"Please share any updates when you get a chance. (This is an automated message.)"
)
LABEL = "needs-info"
TRIGGER_LABEL = "waiting-for-author"
PINGED_LABEL = "requested-info"
def get_team_members(g: Github, org: str, team_slug: str) -> set[str]:
@@ -76,15 +79,21 @@ def should_ping(
days_threshold: int,
now: datetime,
) -> bool:
"""Determine whether this issue/PR should be pinged."""
"""Determine whether this issue/PR should be pinged.
Only issues/PRs carrying the 'waiting-for-author' label are candidates.
"""
author = issue.user.login
# Skip if the trigger label is not present
if not any(label.name == TRIGGER_LABEL for label in issue.labels):
return False
# Skip if author is a team member
if author in team_members:
return False
# Skip if already labeled
if any(label.name == LABEL for label in issue.labels):
# Skip if already pinged
if any(label.name == PINGED_LABEL for label in issue.labels):
return False
# Skip if no comments at all
@@ -112,7 +121,7 @@ def should_ping(
def ping(issue: Issue, dry_run: bool) -> bool:
"""Post a reminder comment and add the needs-info label. Returns True on success."""
"""Post a reminder comment and add the 'requested-info' label. Returns True on success."""
author = issue.user.login
kind = "PR" if issue.pull_request else "Issue"
@@ -129,7 +138,7 @@ def ping(issue: Issue, dry_run: bool) -> bool:
issue.create_comment(PING_COMMENT.format(author=author))
commented = True
if not labeled:
issue.add_to_labels(LABEL)
issue.add_to_labels(PINGED_LABEL)
labeled = True
print(f" Pinged {kind} #{issue.number} (@{author})")
return True
@@ -184,9 +193,9 @@ def main() -> None:
failed = []
scanned = 0
print(f"Scanning open issues and PRs (threshold: {days_threshold} days)...\n")
print(f"Scanning open issues and PRs labeled '{TRIGGER_LABEL}' (threshold: {days_threshold} days)...\n")
for issue in repo.get_issues(state="open"):
for issue in repo.get_issues(state="open", labels=[TRIGGER_LABEL]):
scanned += 1
if should_ping(issue, team_members, days_threshold, now):
+10 -6
View File
@@ -15,8 +15,9 @@ import pytest
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))
from stale_issue_pr_ping import (
LABEL,
PINGED_LABEL,
PING_COMMENT,
TRIGGER_LABEL,
author_replied_after,
find_last_team_comment,
get_team_members,
@@ -63,7 +64,10 @@ def _make_issue(
issue.user = MagicMock()
issue.user.login = author
issue.number = number
issue.labels = [_make_label(n) for n in (labels or [])]
# Default to having the trigger label, since the API query pre-filters.
if labels is None:
labels = [TRIGGER_LABEL]
issue.labels = [_make_label(n) for n in labels]
issue.comments = comment_count
issue.pull_request = MagicMock() if pull_request else None
if comments is not None:
@@ -136,11 +140,11 @@ class TestShouldPing:
assert should_ping(issue, TEAM, 4, NOW) is True
def test_skip_team_member_author(self):
issue = _make_issue(author="alice", comment_count=1)
issue = _make_issue(author="alice", labels=[TRIGGER_LABEL], comment_count=1)
assert should_ping(issue, TEAM, 4, NOW) is False
def test_skip_already_labeled(self):
issue = _make_issue(labels=[LABEL], comment_count=1)
def test_skip_already_pinged(self):
issue = _make_issue(labels=[TRIGGER_LABEL, PINGED_LABEL], comment_count=1)
assert should_ping(issue, TEAM, 4, NOW) is False
def test_skip_no_comments(self):
@@ -194,7 +198,7 @@ class TestPing:
issue = _make_issue()
assert ping(issue, dry_run=False) is True
issue.create_comment.assert_called_once()
issue.add_to_labels.assert_called_once_with(LABEL)
issue.add_to_labels.assert_called_once_with(PINGED_LABEL)
@patch("stale_issue_pr_ping.time.sleep")
def test_retry_on_failure(self, mock_sleep):