From 35f8fd69ae7e766e9df1ff671429602bb64ee43c Mon Sep 17 00:00:00 2001 From: Darrien Rushing Date: Thu, 17 Jul 2025 22:11:00 -0500 Subject: [PATCH] Getting setup and first displays - repos has a basic table view reporting - database manager for easier sqlite integration - can pull GitHub action workflows --- .gitignore | 3 +++ README.md | 10 ++++++++ database/manager.py | 17 ++++++++++++++ database/util.py | 15 ++++++++++++ github/gh_actions.py | 32 ++++++++++++++++++++++++++ github/models/workflow.py | 41 +++++++++++++++++++++++++++++++++ github/repos.py | 48 ++++++++++++++++++++++++++++++++++++--- main.py | 3 ++- 8 files changed, 165 insertions(+), 4 deletions(-) create mode 100644 README.md create mode 100644 database/manager.py create mode 100644 database/util.py create mode 100644 github/gh_actions.py create mode 100644 github/models/workflow.py diff --git a/.gitignore b/.gitignore index ac9a5ed..34df971 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. +# Ignore sqlite database files +*.db + */bin */include */lib diff --git a/README.md b/README.md new file mode 100644 index 0000000..a94c749 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# GitHub Inventory + +A better way to manage your GitHub. + +## Useful Tools for this repo + +- Draw.io Integration (VSCode extension) +- Draw.io Integration: Mermaid plugin (VSCode extension) +- Draw.io Integration: Markdown plug (VSCode extension) +- SQLite client/browser \ No newline at end of file diff --git a/database/manager.py b/database/manager.py new file mode 100644 index 0000000..dafebb2 --- /dev/null +++ b/database/manager.py @@ -0,0 +1,17 @@ +import sqlite3 + +class DatabaseManager: + def __init__(self, db_path="inventory.db"): + self.db_path = db_path + + def __enter__(self): + self.conn = sqlite3.connect(self.db_path) + self.cursor = self.conn.cursor() + return self.cursor + + def __exit__(self, exc_type, exc_val, exc_tb): + if exc_type is None: + self.conn.commit() + else: + self.conn.rollback() + self.conn.close() diff --git a/database/util.py b/database/util.py new file mode 100644 index 0000000..7c0427d --- /dev/null +++ b/database/util.py @@ -0,0 +1,15 @@ +import sqlite3 + +conn = sqlite3.connect('inventory.db') +cursor = conn.cursor() + +cursor.execute(''' + CREATE TABLE IF NOT EXISTS repositories ( + id TEXT PRIMARY KEY, + name TEXT, + url TEXT + ) +''') + +conn.commit() +conn.close() \ No newline at end of file diff --git a/github/gh_actions.py b/github/gh_actions.py new file mode 100644 index 0000000..9a88aa2 --- /dev/null +++ b/github/gh_actions.py @@ -0,0 +1,32 @@ +import os +from typing import Dict, List, Any +import requests + +def __request_repo_workflows(owner: str, repo: str) -> List[Any]: + + token = os.environ.get('GITHUB_TOKEN') + headers = { + 'Authorization': f'token {token}', + 'X-GitHub-Api-Version': '2022-11-28', + 'Accept': 'application/vnd.github+json' + } + timeout = 10 + + params: Dict[str, int] = { 'per_page': 100, 'page': 1 } + data = [] + + try: + continue_paging = True + while continue_paging: + r = requests.get(f'https://api.github.com/repos/{owner}/{repo}/actions/workflows', + headers = headers, + params = params, + timeout = timeout + ) + result = r.json() + data = result['workflows'] + except Exception as e: + # logging.error(f"--- ERROR --- {e}") + print(f"--- ERROR --- {e}") + + return data diff --git a/github/models/workflow.py b/github/models/workflow.py new file mode 100644 index 0000000..edf73af --- /dev/null +++ b/github/models/workflow.py @@ -0,0 +1,41 @@ + + +class GitHubWorkflow: + def __init__( + self, + id: int, + node_id: str, + name: str, + path: str, + state: str, + created_at: str, + updated_at: str, + url: str, + html_url: str, + badge_url: str + ): + self.id: int = id + self.node_id: str = node_id + self.name: str = name + self.path: str = path + self.state: str = state + self.created_at: str = created_at + self.updated_at: str = updated_at + self.url: str = url + self.html_url: str = html_url + self.badge_url: str = badge_url + + @classmethod + def from_dict(cls, data): + return cls( + data['id'], + data['node_id'], + data['name'], + data['path'], + data['state'], + data['created_at'], + data['updated_at'], + data['url'], + data['html_url'], + data['badge_url'] + ) diff --git a/github/repos.py b/github/repos.py index 1f616d5..3430ca4 100644 --- a/github/repos.py +++ b/github/repos.py @@ -1,7 +1,16 @@ +"""Handle repository actions with GitHub +""" + import os import logging +import uuid from typing import Dict, List, Any import requests +from rich.console import Console +from rich.table import Table +from github.gh_actions import __request_repo_workflows +from github.models.workflow import GitHubWorkflow +from database.manager import DatabaseManager def __request_repos_for_user(username: str) -> List[Any]: """Get repos for a GitHub user @@ -40,15 +49,48 @@ def __request_repos_for_user(username: str) -> List[Any]: return repos +def insert_repo(repo): + repo_id = str(uuid.uuid4()) + with DatabaseManager() as db: + db.execute(''' + INSERT INTO repositories (id, name, url) + VALUES (?, ?, ?) + ''', (repo_id, repo['name'], repo['html_url']) + ) + def user_repos_report(username: str): """Handle reporting GitHub repos for user """ repos = __request_repos_for_user(username) - print(repos) + + console = Console() + table = Table(title="GitHub Repositories") + + table.add_column("Name", style="cyan", no_wrap=True) + table.add_column("Private", justify="center", style="magenta") + table.add_column("Language", style="green") + table.add_column("Stars", justify="right", style="yellow") + + for repo in repos: + # insert_repo(repo) + table.add_row( + repo["name"], + "🔒" if repo["private"] else "🌐", + repo["language"] or "—", + str(repo["stargazers_count"]) + ) + + # Render table + console.print(table) def handle_args(args): - print(f"IN github/repos.py args: {args}") if args.report == 'list': - user_repos_report(args.user) \ No newline at end of file + user_repos_report(args.user) + + if args.report == 'workflows': + wfs = __request_repo_workflows(owner = 'meddlin', repo = 'github-inventory') + for w in wfs: + workflow = GitHubWorkflow.from_dict(w) + print(workflow.name) \ No newline at end of file diff --git a/main.py b/main.py index 35675b9..8eadff0 100644 --- a/main.py +++ b/main.py @@ -1,8 +1,8 @@ import argparse import textwrap -import github.repos as gh_repos from dotenv import load_dotenv +import github.repos as gh_repos def start_repl(): print("Welcome to the REPL") @@ -38,6 +38,7 @@ def main(): repo_parser = gh_subparser.add_parser('repo', help = 'GitHub repo commands') repo_parser.add_argument('--name', type = str, help = 'Name of repository') + repo_parser.add_argument('--owner', type = str, help = 'Owner of repository') repo_parser.add_argument('--report', type = str, help = 'Type of report to execute') repo_parser.add_argument('--user', type = str, help = 'GitHub username') repo_parser.add_argument('--csv', action = argparse.BooleanOptionalAction)