How to Find Stale GitHub Issues in Python
Filter a list of GitHub issues to find those not updated within a configurable number of days using Python datetime arithmetic.
Python code
47 linesimport os
from datetime import datetime, timezone, timedelta
import re
# Simulated GitHub issue data structure
SAMPLE_ISSUES = [
{"number": 101, "title": "Login button not working", "updated_at": "2025-06-01T12:00:00Z", "assignee": "alice"},
{"number": 102, "title": "Fix database migration error", "updated_at": "2025-10-20T08:30:00Z", "assignee": None},
{"number": 103, "title": "Update API documentation", "updated_at": "2025-10-15T14:15:00Z", "assignee": "bob"},
{"number": 104, "title": "Refactor authentication module", "updated_at": "2025-09-01T09:00:00Z", "assignee": None},
]
STALE_DAYS = 30 # Consider issues stale if not updated in this many days
def parse_github_date(date_str):
"""Parse GitHub ISO 8601 date string to datetime object."""
# Remove trailing Z and add UTC timezone
if date_str.endswith('Z'):
date_str = date_str[:-1] + '+00:00'
return datetime.fromisoformat(date_str)
def is_stale(issue, stale_days):
"""Check if an issue is stale based on last update time."""
last_updated = parse_github_date(issue["updated_at"])
now = datetime.now(timezone.utc)
return now - last_updated > timedelta(days=stale_days)
def find_stale_issues(issues, stale_days=STALE_DAYS):
"""Filter list of issues to find only stale ones."""
stale_issues = []
for issue in issues:
if is_stale(issue, stale_days):
stale_issues.append(issue)
return stale_issues
if __name__ == "__main__":
stale_issues = find_stale_issues(SAMPLE_ISSUES)
print(f"Checking for issues stale more than {STALE_DAYS} days...\n")
if not stale_issues:
print("No stale issues found.")
else:
print(f"Found {len(stale_issues)} stale issue(s):\n")
for issue in stale_issues:
assignee = issue.get("assignee", "unassigned")
print(f" #{issue['number']}: {issue['title']} (last updated: {issue['updated_at']}, assignee: {assignee})")
Output
Checking for issues stale more than 30 days...
Found 2 stale issue(s):
#101: Login button not working (last updated: 2025-06-01T12:00:00Z, assignee: alice)
#104: Refactor authentication module (last updated: 2025-09-01T09:00:00Z, assignee: unassigned)
How it works
The script parses GitHub's ISO 8601 date strings into datetime objects using datetime.fromisoformat() after converting the trailing 'Z' to '+00:00'. It subtracts the parsed update time from the current UTC time and compares the resulting timedelta to a stale_days threshold. The is_stale helper encapsulates the check, and find_stale_issues builds a filtered list. This approach works entirely with the Python standard library so no third-party packages are needed.
Common mistakes
- Forgetting to handle the trailing 'Z' in GitHub's date strings before parsing with `fromisoformat`.
- Comparing naive datetimes without timezone info to aware datetimes, causing a `TypeError`.
- Hardcoding the stale-days threshold instead of making it a parameter like `stale_days`.
Variations
- Use `dateutil.parser.isoparse` from the `python-dateutil` package for more robust date parsing.
- Wrap the logic in a class like `StaleIssueDetector` to manage configuration and issue data together.
Real-world use cases
- Running a weekly cron job to tag issues that haven't been updated in 30 days and assign them to a triage team.
- Integrating into a CI/CD pipeline that auto-closes stale PRs or issues after a grace period.
- Building a dashboard widget that highlights inactive items across your repositories for maintainers.
Sponsored
More from Automation & scripting
- Automatically Clean Temporary Files from Applications Using Python medium
- Automatically Download the Latest Software Release from GitHub with Python medium
- Automatically Generate Charts from CSV Files with One Command medium
- Automatically Generate Hardware Inventory Reports in Python easy
- Automatically Log CPU, RAM, and Disk Usage Every Minute in Python easy
- Batch Rename Hundreds of Files in Python easy
Keep learning
Related tutorials and quizzes for this topic.