Skip to content

Instantly share code, notes, and snippets.

@iccir
Created May 23, 2026 18:39
Show Gist options
  • Select an option

  • Save iccir/e0015bcc906286ca6e34ed009bcb9231 to your computer and use it in GitHub Desktop.

Select an option

Save iccir/e0015bcc906286ca6e34ed009bcb9231 to your computer and use it in GitHub Desktop.
Check settings of all GitHub repositories
#!/usr/bin/env python3
"""
check_repos.py
This is a tool to check the GitHub settings of all of my repositories.
- "Wiki", "Projects", "Discussions", and "Pull Requests" should be disabled.
- "Issues" should be enabled for select repositories.
- Watch should be set to "All Activity" when Issues are enabled.
Usage:
python3 check_repos.py --token ghp_token_here
"""
import argparse
import json
import os
import sys
import urllib.error
import urllib.parse
import urllib.request
import textwrap
def gh_get(token: str, path: str, params: dict, allow_404 = False):
headers = {
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
}
url = f"{path}?{urllib.parse.urlencode(params)}"
req = urllib.request.Request(url, headers=headers)
try:
with urllib.request.urlopen(req) as resp:
return json.loads(resp.read().decode())
except urllib.error.HTTPError as e:
body = e.read().decode()
if (e.code == 404 and allow_404):
return None
else:
print(f"GitHub API error: {e.code} - {body}")
sys.exit(1)
def fetch_all_repos(token: str) -> list:
repos = []
page = 1
while True:
batch = gh_get(token, "https://api.github.com/user/repos", {
"affiliation": "owner,organization_member",
"per_page": 100,
"page": page,
"sort": "full_name",
})
if not batch:
break
repos.extend(batch)
page += 1
return repos
def get_subscription_status(token: str, repo_name: str) -> list:
result = gh_get(token, f"https://api.github.com/repos/{repo_name}/subscription", { }, allow_404 = True)
if result:
return result.get("subscribed")
else:
return False
def print_repo_list(header: str, repos: list) -> None:
if (len(repos) == 0): return
print("")
print(f"{header}:")
sorted_repos = sorted(repos, key=lambda d: d["full_name"])
sorted_names = ", ".join(d["full_name"] for d in sorted_repos)
print(textwrap.fill(sorted_names, width=80, break_on_hyphens=False))
def make_subscribed_table(repos: list) -> list:
def yes_or_no(value):
return "YES" if value else "no"
rows = []
for repo in repos:
rows.append([
[ "name", repo["full_name"] ],
[ "issues?", yes_or_no(repo.get("has_issues")) ],
[ "pull?", yes_or_no(repo.get("has_pull_requests")) ],
[ "subscribed?", yes_or_no(repo.get("is_subscribed")) ]
])
return rows
def make_table(repos: list) -> list:
def yes_or_no(value):
return "YES" if value else "no"
rows = []
for repo in repos:
rows.append([
[ "name", repo["full_name"] ],
[ "issues?", yes_or_no(repo.get("has_issues")) ],
[ "wiki?", yes_or_no(repo.get("has_wiki")) ],
[ "projects?", yes_or_no(repo.get("has_projects")) ],
[ "pages?", yes_or_no(repo.get("has_pages")) ],
[ "dis?", yes_or_no(repo.get("has_discussions")) ],
[ "pull?", yes_or_no(repo.get("has_pull_requests")) ]
])
return rows
def print_table(rows: list) -> None:
def make_row(values):
return "| " + " | ".join(v.ljust(w) for v, w in zip(values, col_widths)) + " |"
def make_separator():
return "+-" + "-+-".join("-" * w for w in col_widths) + "-+"
headers = [ cell[0] for cell in rows[0] ]
col_widths = []
for i, header in enumerate(headers):
max_val_len = max(len(row[i][1]) for row in rows)
col_widths.append(max(len(header), max_val_len))
sep = make_separator()
print(sep)
print(make_row(headers))
print(sep)
for row in rows:
print(make_row([cell[1] for cell in row]))
print(sep)
def print_repo_table(header: str, repos: list) -> None:
print("")
print(f"{header}:")
rows = make_table(repos)
if len(rows) > 0:
print_table(rows)
else:
print("No repositories")
print
def main():
parser = argparse.ArgumentParser(description="Check settings across all GitHub repositories.")
parser.add_argument("--token", help="GitHub personal access token")
parser.add_argument("--subscribed", help="Check subscribed status", action="store_true")
parser.add_argument("--archived", help="List archived repositories", action="store_true")
args = parser.parse_args()
token = args.token
if not token:
print("Error: provide a GitHub token via --token")
sys.exit(1)
print("Fetching repositories...", flush=True)
repos = fetch_all_repos(token)
def extract(list, condition):
return (
[x for x in list if not condition(x)],
[x for x in list if condition(x) ]
)
repos, archived_repos = extract(repos, lambda x: x.get("archived"))
if args.subscribed:
_, repos = extract(repos, lambda x: x.get("has_pull_requests") or x.get("has_issues"))
for repo in repos:
repo["is_subscribed"] = get_subscription_status(token, repo["full_name"])
rows = make_subscribed_table(repos)
print("")
print_table(rows)
else:
repos, pr_repos = extract(repos, lambda x: x.get("has_pull_requests"))
repos, issue_repos = extract(repos, lambda x: x.get("has_issues"))
repos, forked_repos = extract(repos, lambda x: x.get("fork"))
print_repo_table("Pull Requests Enabled", pr_repos)
print_repo_table("Issues Enabled", issue_repos)
print_repo_table("Standard", repos)
print_repo_table("Forked", forked_repos)
if args.archived:
print_repo_list("Archived", archived_repos)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment