Last active
August 9, 2025 13:47
-
-
Save llimllib/921345e181dc8466dfa3cd7e6baa34e5 to your computer and use it in GitHub Desktop.
show all the PRs and issues you've filed on different repos
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 1. set GH_TOKEN to be a valid github API token. I used `GH_TOKEN=$(gh auth)` to set it | |
# 2. run `python count.py <username>` | |
# | |
# This script will print out each repository you've filed a PR on or submitted an issue to, | |
# and a link to no more than 5 of them | |
import sys | |
import os | |
import json | |
import urllib.request | |
import urllib.error | |
from datetime import datetime | |
from urllib.parse import quote | |
from collections import defaultdict | |
def get_repos_with_items(username, item_type, token=None, collect_urls=False): | |
"""Fetch all repositories a user has made pull requests or issues to. | |
Args: | |
username: GitHub username | |
item_type: 'pr' for pull requests or 'issue' for issues | |
token: GitHub API token (optional) | |
collect_urls: Whether to collect item URLs | |
Returns: | |
If collect_urls is False: Sorted list of repository names | |
If collect_urls is True: Dictionary mapping repo names to lists of item URLs | |
""" | |
base_url = f"https://api.github.com/search/issues?q=author:{quote(username)}+type:{item_type}" | |
headers = {"Accept": "application/vnd.github.v3+json"} | |
if token: | |
headers["Authorization"] = f"token {token}" | |
if collect_urls: | |
repo_items = defaultdict(list) | |
else: | |
repos = set() | |
page = 1 | |
while True: | |
url = f"{base_url}&page={page}&per_page=100" | |
req = urllib.request.Request(url, headers=headers) | |
try: | |
with urllib.request.urlopen(req) as response: | |
data = json.loads(response.read().decode("utf-8")) | |
items = data.get("items", []) | |
if not items: | |
break | |
for item in items: | |
repo_url = item.get("repository_url", "") | |
if repo_url: | |
repo_name = "/".join(repo_url.split("/")[-2:]) | |
if collect_urls: | |
html_url = item.get("html_url") | |
if html_url: | |
repo_items[repo_name].append(html_url) | |
else: | |
repos.add(repo_name) | |
# Check if we've processed all pages | |
if len(items) < 100: | |
break | |
page += 1 | |
except urllib.error.HTTPError as e: | |
if e.code == 403: | |
rate_limit = e.headers.get("X-RateLimit-Remaining", "0") | |
if rate_limit == "0": | |
reset_timestamp = int(e.headers.get("X-RateLimit-Reset", "0")) | |
reset_time = datetime.fromtimestamp(reset_timestamp).strftime( | |
"%Y-%m-%d %H:%M:%S" | |
) | |
print( | |
f"Error: GitHub API rate limit exceeded. Resets at: {reset_time}" | |
) | |
print("Consider using a GitHub token for higher rate limits.") | |
else: | |
print(f"HTTP Error: {e.code} - {e.reason}") | |
break | |
except urllib.error.URLError as e: | |
print(f"URL Error: {e.reason}") | |
break | |
except Exception as e: | |
print(f"Error: {e}") | |
break | |
if collect_urls: | |
return {k: v for k, v in sorted(repo_items.items())} | |
else: | |
return sorted(list(repos)) | |
def get_repos_with_prs(username, token=None, collect_urls=False): | |
"""Fetch all repositories a user has made pull requests to.""" | |
return get_repos_with_items(username, "pr", token, collect_urls) | |
def get_repos_with_issues(username, token=None, collect_urls=False): | |
"""Fetch all repositories a user has filed issues in.""" | |
return get_repos_with_items(username, "issue", token, collect_urls) | |
def main(): | |
if len(sys.argv) < 2: | |
print("Usage: python github_pr_repos.py <github_username> [github_token]") | |
sys.exit(1) | |
username = sys.argv[1] | |
# Check for token in args or environment | |
token = None | |
if len(sys.argv) > 2: | |
token = sys.argv[2] | |
elif "GITHUB_TOKEN" in os.environ: | |
token = os.environ["GITHUB_TOKEN"] | |
print(f"Fetching repositories where {username} has submitted pull requests...") | |
pr_repos_with_urls = get_repos_with_prs(username, token, collect_urls=True) | |
print(f"Fetching repositories where {username} has filed issues...") | |
issue_repos_with_urls = get_repos_with_issues(username, token, collect_urls=True) | |
if pr_repos_with_urls: | |
print(f"\nFound {len(pr_repos_with_urls)} repositories with pull requests:") | |
for i, (repo, pr_urls) in enumerate(pr_repos_with_urls.items(), 1): | |
print(f"{i}. {repo}") | |
# Display up to 5 PR links per repository | |
for j, url in enumerate(pr_urls[:5], 1): | |
print(f" PR {j}: {url}") | |
if len(pr_urls) > 5: | |
print(f" ... and {len(pr_urls) - 5} more PRs") | |
else: | |
print("No repositories with pull requests found.") | |
if issue_repos_with_urls: | |
print(f"\nFound {len(issue_repos_with_urls)} repositories with issues:") | |
for i, (repo, issue_urls) in enumerate(issue_repos_with_urls.items(), 1): | |
print(f"{i}. {repo}") | |
# Display up to 5 issue links per repository | |
for j, url in enumerate(issue_urls[:5], 1): | |
print(f" Issue {j}: {url}") | |
if len(issue_urls) > 5: | |
print(f" ... and {len(issue_urls) - 5} more issues") | |
else: | |
print("No repositories with issues found.") | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment