Skip to content

Commit 3371c5b

Browse files
committed
improved app
1 parent d6ce35a commit 3371c5b

1 file changed

Lines changed: 193 additions & 39 deletions

File tree

GitHub-Inventory.py

Lines changed: 193 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
99
GITHUB_USERNAME = os.getenv("GITHUB_USERNAME")
1010

11-
# GitHub API URL
12-
GITHUB_API_URL = (
13-
f"https://api.github.com/users/{GITHUB_USERNAME}/repos?per_page=100&type=all&page="
11+
# GitHub API URLs
12+
GITHUB_API_BASE = "https://api.github.com"
13+
USER_REPOS_URL = (
14+
f"{GITHUB_API_BASE}/users/{GITHUB_USERNAME}/repos?per_page=100&type=all&page="
1415
)
16+
USER_ORGS_URL = f"{GITHUB_API_BASE}/users/{GITHUB_USERNAME}/orgs"
17+
ORG_REPOS_URL = f"{GITHUB_API_BASE}/orgs/{{org}}/repos?per_page=100&page="
1518

1619
# Output directories
1720
TEXT_FOLDER = "output/text"
@@ -30,83 +33,215 @@ def check_folders(log_file):
3033
"""Check if output directories exist, create if not, and log status."""
3134
log_message("[INFO] Checking output directories...", log_file)
3235

33-
if not os.path.exists(TEXT_FOLDER):
34-
os.makedirs(TEXT_FOLDER)
36+
for folder in [TEXT_FOLDER, MARKDOWN_FOLDER]:
37+
if not os.path.exists(folder):
38+
os.makedirs(folder)
39+
log_message(
40+
f"[CREATED] Folder '{folder}' was missing and has been created.",
41+
log_file,
42+
)
43+
else:
44+
log_message(f"[FOUND] Folder '{folder}' exists.", log_file)
45+
46+
log_message("[SUCCESS] All necessary folders are ready.\n", log_file)
47+
48+
49+
def make_api_request(url, headers, log_file):
50+
"""Make an API request and handle errors."""
51+
global response
52+
try:
53+
response = requests.get(url, headers=headers)
54+
response.raise_for_status()
55+
return response.json()
56+
except requests.exceptions.RequestException as e:
3557
log_message(
36-
f"[CREATED] Folder '{TEXT_FOLDER}' was missing and has been created.",
58+
f"[ERROR] API request failed: {url} - {str(e)}",
3759
log_file,
3860
)
39-
else:
40-
log_message(f"[FOUND] Folder '{TEXT_FOLDER}' exists.", log_file)
61+
try:
62+
log_message(str(response.json()), log_file) # Print error details
63+
except:
64+
pass
65+
return []
66+
67+
68+
def fetch_user_repos(headers, log_file):
69+
"""Fetch user's personal GitHub repositories."""
70+
log_message("[INFO] Fetching personal repositories from GitHub...", log_file)
71+
repos = []
72+
page = 1
4173

42-
if not os.path.exists(MARKDOWN_FOLDER):
43-
os.makedirs(MARKDOWN_FOLDER)
74+
while True:
75+
page_repos = make_api_request(USER_REPOS_URL + str(page), headers, log_file)
76+
if not page_repos:
77+
break
78+
79+
repos.extend(page_repos)
80+
page += 1
81+
82+
log_message(f"[SUCCESS] Retrieved {len(repos)} personal repositories.", log_file)
83+
return repos
84+
85+
86+
def fetch_organizations(headers, log_file):
87+
"""Fetch organizations that the user is a member of."""
88+
log_message("[INFO] Fetching user's organizations...", log_file)
89+
orgs = make_api_request(USER_ORGS_URL, headers, log_file)
90+
91+
if orgs:
92+
org_names = [org["login"] for org in orgs]
4493
log_message(
45-
f"[CREATED] Folder '{MARKDOWN_FOLDER}' was missing and has been created.",
94+
f"[SUCCESS] Found {len(orgs)} organizations: {', '.join(org_names)}",
4695
log_file,
4796
)
4897
else:
49-
log_message(f"[FOUND] Folder '{MARKDOWN_FOLDER}' exists.", log_file)
98+
log_message(
99+
"[INFO] No organizations found or unable to fetch organizations.", log_file
100+
)
50101

51-
log_message("[SUCCESS] All necessary folders are ready.\n", log_file)
102+
return orgs
52103

53104

54-
def fetch_repos(log_file):
55-
"""Fetch GitHub repositories and return a list of repo details."""
56-
log_message("[INFO] Fetching repositories from GitHub...", log_file)
57-
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
105+
def fetch_org_repos(org_name, headers, log_file):
106+
"""Fetch repositories from a specific organization."""
107+
log_message(
108+
f"[INFO] Fetching repositories from organization: {org_name}...", log_file
109+
)
58110
repos = []
59111
page = 1
60112

61113
while True:
62-
response = requests.get(GITHUB_API_URL + str(page), headers=headers)
63-
64-
if response.status_code != 200:
65-
log_message(
66-
f"[ERROR] Failed to fetch repositories. Status Code: {response.status_code}",
67-
log_file,
68-
)
69-
log_message(str(response.json()), log_file) # Print error details
70-
return []
71-
72-
page_repos = response.json()
114+
url = ORG_REPOS_URL.format(org=org_name) + str(page)
115+
page_repos = make_api_request(url, headers, log_file)
73116
if not page_repos:
74117
break
75118

76119
repos.extend(page_repos)
77120
page += 1
78121

79-
log_message(f"[SUCCESS] Retrieved {len(repos)} repositories.\n", log_file)
122+
log_message(
123+
f"[SUCCESS] Retrieved {len(repos)} repositories from {org_name}.", log_file
124+
)
80125
return repos
81126

82127

128+
def fetch_all_repos(log_file):
129+
"""Fetch all repositories: personal and from organizations."""
130+
log_message("[INFO] Beginning repository collection process...", log_file)
131+
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
132+
133+
# Get personal repos
134+
user_repos = fetch_user_repos(headers, log_file)
135+
136+
# Get organization repos
137+
orgs = fetch_organizations(headers, log_file)
138+
org_repos = []
139+
140+
for org in orgs:
141+
org_name = org["login"]
142+
org_repos_list = fetch_org_repos(org_name, headers, log_file)
143+
144+
# Add organization info to each repo
145+
for repo in org_repos_list:
146+
repo["organization"] = org_name
147+
148+
org_repos.extend(org_repos_list)
149+
150+
# Add source info to personal repos
151+
for repo in user_repos:
152+
repo["organization"] = "personal"
153+
154+
# Combine all repos
155+
all_repos = user_repos + org_repos
156+
157+
log_message(f"[SUCCESS] Total repositories collected: {len(all_repos)}\n", log_file)
158+
return all_repos
159+
160+
83161
def format_repo_list(repos, log_file):
84162
"""Format repo data and generate both text and markdown outputs."""
85163
total_repos = len(repos)
86164
public_repos = sum(1 for repo in repos if not repo["private"])
87165
private_repos = total_repos - public_repos
88166

167+
# Count repos by source (personal vs organizations)
168+
personal_repos = sum(1 for repo in repos if repo["organization"] == "personal")
169+
org_repos = total_repos - personal_repos
170+
171+
# Get unique organizations
172+
orgs = {
173+
repo["organization"] for repo in repos if repo["organization"] != "personal"
174+
}
175+
89176
timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
90-
text_filename = f"{TEXT_FOLDER}/text_output_{timestamp}.txt"
91-
markdown_filename = f"{MARKDOWN_FOLDER}/markdown_output_{timestamp}.txt"
177+
text_filename = f"{TEXT_FOLDER}/github_inventory_{timestamp}.txt"
178+
markdown_filename = f"{MARKDOWN_FOLDER}/github_inventory_{timestamp}.md"
92179

93-
log_message(f"[INFO] Formatting repository data...\n", log_file)
180+
log_message(f"[INFO] Formatting repository data...", log_file)
94181

95182
# Stats Header
96-
header_text = f"GitHub Inventory Report\nTotal Repositories: {total_repos}\nPublic Repos: {public_repos}\nPrivate Repos: {private_repos}\n\n"
97-
header_markdown = f"# GitHub Inventory Report\n**Total Repositories:** {total_repos}\n**Public Repos:** {public_repos}\n**Private Repos:** {private_repos}\n\n"
183+
header_text = (
184+
f"GitHub Inventory Report\n"
185+
f"Generated on: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
186+
f"User: {GITHUB_USERNAME}\n\n"
187+
f"Repository Statistics:\n"
188+
f"- Total Repositories: {total_repos}\n"
189+
f"- Personal Repositories: {personal_repos}\n"
190+
f"- Organization Repositories: {org_repos}\n"
191+
f"- Public Repositories: {public_repos}\n"
192+
f"- Private Repositories: {private_repos}\n"
193+
f"- Organizations: {len(orgs)}\n\n"
194+
)
195+
196+
header_markdown = (
197+
f"# GitHub Inventory Report\n"
198+
f"**Generated on:** {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
199+
f"**User:** {GITHUB_USERNAME}\n\n"
200+
f"## Repository Statistics\n"
201+
f"- **Total Repositories:** {total_repos}\n"
202+
f"- **Personal Repositories:** {personal_repos}\n"
203+
f"- **Organization Repositories:** {org_repos}\n"
204+
f"- **Public Repositories:** {public_repos}\n"
205+
f"- **Private Repositories:** {private_repos}\n"
206+
f"- **Organizations:** {len(orgs)}\n\n"
207+
)
98208

99209
# Repo List
100210
text_output = header_text
101211
markdown_output = header_markdown
102212

103-
for index, repo in enumerate(repos, 1): # Adding numbering here
213+
# Sort repos by source (personal first, then by org name) and then by repo name
214+
sorted_repos = sorted(
215+
repos,
216+
key=lambda r: (r["organization"] != "personal", r["organization"], r["name"]),
217+
)
218+
219+
# Group by source (personal/organization)
220+
current_source = None
221+
222+
for index, repo in enumerate(sorted_repos, 1):
104223
name = repo["name"]
105224
desc = repo["description"] or "No description provided."
106225
visibility = "Public" if not repo["private"] else "Private"
226+
source = repo["organization"]
227+
228+
# Add section headers when source changes
229+
if source != current_source:
230+
current_source = source
231+
232+
if source == "personal":
233+
text_output += f"PERSONAL REPOSITORIES\n{'=' * 20}\n\n"
234+
markdown_output += f"## Personal Repositories\n\n"
235+
else:
236+
text_output += f"\nORGANIZATION: {source}\n{'=' * 20}\n\n"
237+
markdown_output += f"## Organization: {source}\n\n"
107238

108-
text_output += f"{index}. {name} ({visibility})\n {desc}\n\n"
239+
# Add repository information
240+
text_output += f"{index}. {name} ({visibility})\n {desc}\n\n"
109241
markdown_output += f"### {index}. {name} ({visibility})\n{desc}\n\n"
242+
# Add URL to markdown version
243+
if "html_url" in repo:
244+
markdown_output += f"[View on GitHub]({repo['html_url']})\n\n"
110245

111246
# Writing to files
112247
with open(text_filename, "w", encoding="utf-8") as text_file:
@@ -126,11 +261,30 @@ def main():
126261
os.makedirs(LOGS_FOLDER)
127262
print(f"[INFO] Logs folder created at '{LOGS_FOLDER}'")
128263

129-
log_filename = f"{LOGS_FOLDER}/log.txt"
264+
log_filename = (
265+
f"{LOGS_FOLDER}/log_{datetime.datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.txt"
266+
)
130267
with open(log_filename, "a", encoding="utf-8") as log_file:
131-
log_message("🔹 Starting GitHub Inventory Tool 🔹\n", log_file)
268+
log_message(
269+
"🔹 Starting GitHub Inventory Tool (with Organizations) 🔹\n", log_file
270+
)
132271
check_folders(log_file)
133-
repos = fetch_repos(log_file)
272+
273+
if not GITHUB_TOKEN:
274+
log_message(
275+
"[ERROR] GitHub token not found. Please set GITHUB_TOKEN in your .env file.",
276+
log_file,
277+
)
278+
return
279+
280+
if not GITHUB_USERNAME:
281+
log_message(
282+
"[ERROR] GitHub username not found. Please set GITHUB_USERNAME in your .env file.",
283+
log_file,
284+
)
285+
return
286+
287+
repos = fetch_all_repos(log_file)
134288
if repos:
135289
format_repo_list(repos, log_file)
136290
log_message("✅ Done. Exiting program.\n", log_file)

0 commit comments

Comments
 (0)