Skip to content

Commit 78ff2df

Browse files
committed
chore: Update version2query.py to use methods
**Details**: Makes methods great again. **Related Issue(s)**: class implementation: #27 Signed-off-by: Roger Barker <roger.barker@swirldslabs.com>
1 parent 94f715e commit 78ff2df

2 files changed

Lines changed: 186 additions & 95 deletions

File tree

src/v2query.py

Lines changed: 0 additions & 95 deletions
This file was deleted.

src/version2query.py

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
#!/usr/bin/env python3
2+
3+
import os
4+
import json
5+
import shutil
6+
import requests
7+
from pathlib import Path
8+
from ghapi.all import GhApi
9+
from rich import print
10+
11+
TEMP_DIR = Path(f"tmp.dir")
12+
OUTPUT_FILE = f"output.items.json"
13+
14+
def get_github_token():
15+
"""Get GitHub token from environment variable."""
16+
token = os.getenv("GITHUB_TOKEN")
17+
if not token:
18+
raise ValueError("GITHUB_TOKEN environment variable is not set.")
19+
return token
20+
21+
def get_github_orgs():
22+
"""Create a list of github orgs based on the authenticated user."""
23+
24+
# Create the Github API
25+
api:GhApi = GhApi()
26+
27+
# Grab the orgs:
28+
orgs:list[str] = [org["login"] for org in api.orgs.list_for_authenticated_user()]
29+
30+
return orgs
31+
32+
def get_all_projects(orgs:list[str]) -> list[dict]:
33+
"""Get all projects for each org in orgs."""
34+
35+
# Query projects for each org
36+
all_projects:list[dict] = []
37+
for org in orgs:
38+
print(f"[bold blue]Fetching projects for org: {org}[/bold blue]")
39+
40+
# the graphql query needed to pull in all the data from gh api
41+
query:str = """
42+
query($login: String!) {
43+
organization(login: $login) {
44+
projectsV2(first: 100) {
45+
nodes {
46+
id
47+
number
48+
title
49+
}
50+
}
51+
}
52+
}
53+
"""
54+
55+
headers:dict = {"Authorization": f"Bearer {get_github_token()}"}
56+
response = requests.post(
57+
"https://api.github.com/graphql",
58+
json={"query": query, "variables": {"login": org}},
59+
headers=headers
60+
)
61+
62+
result = response.json()
63+
projects:list[dict] = [
64+
{
65+
"org": org,
66+
"number": project["number"],
67+
"title": project["title"]
68+
}
69+
for project in result.get("data", {}).get("organization", {}).get("projectsV2", {}).get("nodes", [])
70+
]
71+
72+
all_projects.extend(projects)
73+
return all_projects
74+
75+
def projects_filtered_by_team(project_list:list[dict], teams:list[str]) -> list[dict]:
76+
"""Filter the project list by team names."""
77+
78+
filtered_projects:list[dict] = []
79+
for team in teams:
80+
matching_projects:list[dict] = [
81+
p for p in project_list if team.lower() in p["title"].lower()
82+
]
83+
84+
filtered_projects.extend(matching_projects)
85+
print(f"[green]Found {len(matching_projects)} matching projects for team '{team}'[/green]")
86+
return filtered_projects
87+
88+
def get_items_for_projects(projects:list[dict]) -> bool:
89+
"""Fetch all items on each project."""
90+
91+
rv:bool = True
92+
TEMP_DIR.mkdir(exist_ok=True)
93+
for project in projects:
94+
org = project["org"]
95+
number = project["number"]
96+
title = project["title"]
97+
print(f"[cyan]Fetching items from: {title} (Org: {org}, Project: {number})[/cyan]")
98+
99+
# Set the output path
100+
out_path:Path = Path(TEMP_DIR,f"{org}-{number}.items.json")
101+
102+
# use os.system instead of subprocess.run or subprocess.popen to avoid creating a new
103+
# process space where we would need to copy environment variables into it (possibly
104+
# exposing things like GH_TOKEN or other environment variables)
105+
os.system( f'gh project item-list --owner "{org}" {number} --format json > "{out_path}"')
106+
107+
if not out_path.exists():
108+
print(f"[red]Failed to fetch items for project: {title} (Org: {org}, Project: {number})[/red]")
109+
rv = False
110+
continue
111+
112+
return rv
113+
114+
def consolidate_items() -> bool:
115+
"""Consolidate all items into a single JSON file."""
116+
117+
rv:bool = True
118+
all_items = []
119+
try:
120+
for file in TEMP_DIR.glob("*.items.json"):
121+
with open(file) as f:
122+
data = json.load(f)
123+
items = data.get("items", [])
124+
all_items.extend(items)
125+
except Exception as e:
126+
print(f"[red]Error consolidating items: {e}[/red]")
127+
rv = False
128+
129+
try:
130+
with open(OUTPUT_FILE, "w") as f:
131+
json.dump(all_items, f, indent=2)
132+
except Exception as e:
133+
print(f"[red]Error writing consolidated items to {OUTPUT_FILE}: {e}[/red]")
134+
rv = False
135+
136+
if OUTPUT_FILE.exists():
137+
print(f"[bold green]Saved all items to {OUTPUT_FILE}[/bold green]")
138+
return rv
139+
140+
def cleanup() -> bool:
141+
"""Cleanup temporary files."""
142+
rv = True
143+
if TEMP_DIR.exists():
144+
try:
145+
shutil.rmtree(TEMP_DIR)
146+
print(f"[bold red]Cleaned up temporary files in {TEMP_DIR}[/bold red]")
147+
except Exception as e:
148+
print(f"[red]Error cleaning up temporary files: {e}[/red]")
149+
rv = False
150+
else:
151+
print(f"[bold green]No temporary files to clean up.[/bold green]")
152+
153+
return rv
154+
155+
def main():
156+
"""The primary method for the version2query.py script."""
157+
teams:list = input("Enter team name(s) to filter projects: ").split(" ")
158+
159+
if not teams:
160+
print("[red]No team names provided. Exiting...[/red]")
161+
return
162+
163+
orgs = get_github_orgs()
164+
if not orgs:
165+
print("[red]No organizations found. Exiting...[/red]")
166+
return
167+
168+
all_projects = get_all_projects(orgs)
169+
if not all_projects:
170+
print("[red]No projects found. Exiting...[/red]")
171+
return
172+
173+
filtered_projects = projects_filtered_by_team(all_projects, teams)
174+
if not filtered_projects:
175+
print("[red]No projects found for the specified teams. Exiting...[/red]")
176+
return
177+
178+
if not get_items_for_projects(filtered_projects):
179+
print("[red]Failed to fetch items for projects. Exiting...[/red]")
180+
return
181+
182+
if not consolidate_items():
183+
print("[red]Failed to consolidate items. Exiting...[/red]")
184+
return
185+
186+
cleanup()

0 commit comments

Comments
 (0)