From 44ef4325b7a138828f557e3b553ccc765bb1c46e Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Fri, 26 Jun 2026 19:33:22 -0400 Subject: [PATCH 01/12] add argparse for GH exporter --- gh_repo_exporter.py | 49 +++++++++++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/gh_repo_exporter.py b/gh_repo_exporter.py index 7821f18..9f9412e 100644 --- a/gh_repo_exporter.py +++ b/gh_repo_exporter.py @@ -9,6 +9,7 @@ import time import os import re +import argparse from dotenv import load_dotenv load_dotenv() @@ -17,6 +18,7 @@ GH_ORG_NAME = os.getenv("GH_ORG_NAME") SPREADSHEET_ID = os.getenv("SPREADSHEET_ID") GH_SHEET_NAME = os.getenv("GH_SHEET_NAME","GH-Repos") + # Package requirement files to check PACKAGE_REQUIREMENT_FILES = [ @@ -384,10 +386,8 @@ def extract_display_name(val: str) -> str: match = re.search(r'"([^"]+)"\)$', val) # regex to extract the repo-name from "=HYPERLINK(..., "repo-name")" return match.group(1) if match else val -def update_google_sheet(df: pd.DataFrame) -> None: +def update_google_sheet(df: pd.DataFrame, spreadsheet_id: str, sheet_name: str, creds_path: str) -> None: # Authenticate Google API - creds_path = os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") - creds = Credentials.from_service_account_file( creds_path, scopes=[ @@ -397,7 +397,7 @@ def update_google_sheet(df: pd.DataFrame) -> None: ) client = gspread.authorize(creds) - sheet = client.open_by_key(SPREADSHEET_ID).worksheet(GH_SHEET_NAME) + sheet = client.open_by_key(spreadsheet_id).worksheet(sheet_name) # Pull current header HEADER_ROW_INDEX = 2 @@ -516,11 +516,25 @@ def get_column_index(col_name: str): # -------- def main(): - TOKEN = (os.getenv("GH_TOKEN") or input("Enter your GitHub token: ")).strip() - + parser = argparse.ArgumentParser(description="Export GitHub org repo metadata to Google Sheets.") + parser.add_argument("--token", default=None, help="GitHub personal access token (overrides GH_TOKEN in .env)") + parser.add_argument("--org", default=None, help="GitHub org name (overrides GH_ORG_NAME in .env)") + parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") + parser.add_argument("--sheet-name", default="GH-Repos", help="Sheet tab name (default: GH-Repos)") + parser.add_argument("--credentials-path", default=None, help="Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env)") + parser.add_argument("--repo-type", default=None, help="Repo type filter: all, public, private, forks, sources, member (overrides REPO_TYPE in .env)") + args = parser.parse_args() + + TOKEN = args.token or os.getenv("GH_TOKEN") or input("Enter your GitHub token: ").strip() + org_name = args.org or GH_ORG_NAME + spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID + sheet_name = args.sheet_name or GH_SHEET_NAME + creds_path = args.credentials_path or os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") + repo_type = args.repo_type or os.getenv("REPO_TYPE") + required_vars = { - "GH_ORG_NAME": GH_ORG_NAME, - "SPREADSHEET_ID": SPREADSHEET_ID, + "GH_ORG_NAME": org_name, + "SPREADSHEET_ID": spreadsheet_id, } missing = [name for name, value in required_vars.items() if not value] @@ -536,18 +550,17 @@ def main(): gh = Github(auth=Auth.Token(TOKEN)) if TOKEN else Github() try: - org = gh.get_organization(GH_ORG_NAME) + org = gh.get_organization(org_name) except Exception as e: - print(f"ERROR: Could not access org: \"{GH_ORG_NAME}\"") + print(f"ERROR: Could not access org: \"{org_name}\"") return print("") - print(f"Fetching repositories from organization: {GH_ORG_NAME}") + print(f"Fetching repositories from organization: {org_name}") print("") print("----------------") - REPO_TYPE = os.getenv("REPO_TYPE") - repos = list(org.get_repos(type=REPO_TYPE)) + repos = list(org.get_repos(type=repo_type)) print(f"Total repos fetched: {len(repos)}") data = [] @@ -557,8 +570,6 @@ def main(): existing_df = pd.DataFrame() try: - creds_path = os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") - creds = Credentials.from_service_account_file( creds_path, scopes=[ @@ -568,7 +579,7 @@ def main(): ) client = gspread.authorize(creds) - sheet = client.open_by_key(SPREADSHEET_ID).worksheet(GH_SHEET_NAME) + sheet = client.open_by_key(spreadsheet_id).worksheet(sheet_name) all_values = sheet.get_all_values() @@ -593,7 +604,7 @@ def main(): print(f"Existing sheet data shape: {existing_df.shape}") - for repo in tqdm(repos, desc=f"Fetching repositories from {GH_ORG_NAME}...", unit="repo", colour="green", ncols=100, **tqdm_kwargs): + for repo in tqdm(repos, desc=f"Fetching repositories from {org_name}...", unit="repo", colour="green", ncols=100, **tqdm_kwargs): try: info = get_repo_info(repo, existing_df) data.append(info) @@ -611,8 +622,8 @@ def main(): df = pd.DataFrame(data) df.sort_values(by="Repository Name", inplace=True) - update_google_sheet(df) - print(f"Finished fetching info for {len(df)} repositories from {GH_ORG_NAME} organization") + update_google_sheet(df, spreadsheet_id, sheet_name, creds_path) + print(f"Finished fetching info for {len(df)} repositories from {org_name} organization") elapsed = time.time() - start_time minutes, seconds = divmod(int(elapsed), 60) From a198d381f41253e0ce1965b18dd82c6a3729a77b Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Fri, 26 Jun 2026 19:56:46 -0400 Subject: [PATCH 02/12] add argparse for HF exporter --- hf_repo_exporter.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index 3d66da2..c4f81b5 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -9,6 +9,7 @@ import os import re from collections import Counter +import argparse from dotenv import load_dotenv load_dotenv() @@ -333,9 +334,8 @@ def extract_display_name(val: str) -> str: match = re.search(r'"([^"]+)"\)$', val) # regex to extract the repo-name from "=HYPERLINK(..., "repo-name")" return match.group(1) if match else val -def update_google_sheet(df: pd.DataFrame) -> None: +def update_google_sheet(df: pd.DataFrame, spreadsheet_id: str, sheet_name: str, creds_path: str) -> None: # Authenticate Google API - creds_path = os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") creds = Credentials.from_service_account_file( creds_path, @@ -346,7 +346,7 @@ def update_google_sheet(df: pd.DataFrame) -> None: ) client = gspread.authorize(creds) - sheet = client.open_by_key(SPREADSHEET_ID).worksheet(HF_SHEET_NAME) + sheet = client.open_by_key(spreadsheet_id).worksheet(sheet_name) # Pull current header HEADER_ROW_INDEX = 2 @@ -463,11 +463,23 @@ def get_column_index(col_name: str): def main(): - TOKEN = (os.getenv("HF_TOKEN") or input("Enter your Hugging Face token: ")).strip() or None + parser = argparse.ArgumentParser(description="Export Hugging Face org repo metadata to Google Sheets.") + parser.add_argument("--token", default=None, help="Hugging Face token (overrides HF_TOKEN in .env)") + parser.add_argument("--org", default=None, help="Hugging Face org name (overrides HF_ORG_NAME in .env)") + parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") + parser.add_argument("--sheet-name", default="HF-Repos", help="Sheet tab name (default: HF-Repos)") + parser.add_argument("--credentials-path", default=None, help="Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env)") + args = parser.parse_args() + + TOKEN = args.token or os.getenv("HF_TOKEN") or input("Enter your Hugging Face token: ").strip() or None + org_name = args.org or HF_ORG_NAME + spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID + sheet_name = args.sheet_name or HF_SHEET_NAME + creds_path = args.credentials_path or os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") required_vars = { - "HF_ORG_NAME": HF_ORG_NAME, - "SPREADSHEET_ID": SPREADSHEET_ID, + "HF_ORG_NAME": org_name, + "SPREADSHEET_ID": spreadsheet_id, } missing = [name for name, value in required_vars.items() if not value] @@ -485,22 +497,22 @@ def main(): try: repos = [] - for m in api.list_models(author=HF_ORG_NAME, full=True): + for m in api.list_models(author=org_name, full=True): repos.append((api.model_info(m.id), "model")) - for d in api.list_datasets(author=HF_ORG_NAME, full=True): + for d in api.list_datasets(author=org_name, full=True): repos.append((api.dataset_info(d.id), "dataset")) - for s in api.list_spaces(author=HF_ORG_NAME, full=True): + for s in api.list_spaces(author=org_name, full=True): repos.append((api.space_info(s.id), "space")) except Exception as e: - print(f'ERROR: Could not fetch models for "{HF_ORG_NAME}"') + print(f'ERROR: Could not fetch models for "{org_name}"') print(e) return print("") - print(f"Fetching Hugging Face repositories for: {HF_ORG_NAME}") + print(f"Fetching Hugging Face repositories for: {org_name}") print("") print("----------------") @@ -510,7 +522,7 @@ def main(): if os.environ.get("CI") == "true": tqdm_kwargs = {"mininterval": 1, "dynamic_ncols": False, "leave": False} - for repo, repo_type in tqdm(repos, desc=f"Fetching HF repos from {HF_ORG_NAME}...", unit="repo", colour="green", ncols=100, **tqdm_kwargs): + for repo, repo_type in tqdm(repos, desc=f"Fetching HF repos from {org_name}...", unit="repo", colour="green", ncols=100, **tqdm_kwargs): try: info = get_repo_info(api, repo, repo_type, token=TOKEN) data.append(info) @@ -528,8 +540,8 @@ def main(): df = pd.DataFrame(data) df.sort_values(by="Repository Name", inplace=True) - update_google_sheet(df) - print(f"Finished fetching info for {len(df)} repositories from {HF_ORG_NAME} organization") + update_google_sheet(df, spreadsheet_id, sheet_name, creds_path) + print(f"Finished fetching info for {len(df)} repositories from {org_name} organization") elapsed = time.time() - start_time minutes, seconds = divmod(int(elapsed), 60) From d3b6e0837a534635a167b6f60c848b7f554ec646 Mon Sep 17 00:00:00 2001 From: Amanda Lan <156035416+mandylan75@users.noreply.github.com> Date: Tue, 30 Jun 2026 13:15:43 -0400 Subject: [PATCH 03/12] Apply suggestions from code review Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- gh_repo_exporter.py | 4 ++-- hf_repo_exporter.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/gh_repo_exporter.py b/gh_repo_exporter.py index 9f9412e..0f97c82 100644 --- a/gh_repo_exporter.py +++ b/gh_repo_exporter.py @@ -520,12 +520,12 @@ def main(): parser.add_argument("--token", default=None, help="GitHub personal access token (overrides GH_TOKEN in .env)") parser.add_argument("--org", default=None, help="GitHub org name (overrides GH_ORG_NAME in .env)") parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") - parser.add_argument("--sheet-name", default="GH-Repos", help="Sheet tab name (default: GH-Repos)") + parser.add_argument("--sheet-name", default=None, help="Sheet tab name (overrides GH_SHEET_NAME in .env; default: GH-Repos)") parser.add_argument("--credentials-path", default=None, help="Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env)") parser.add_argument("--repo-type", default=None, help="Repo type filter: all, public, private, forks, sources, member (overrides REPO_TYPE in .env)") args = parser.parse_args() - TOKEN = args.token or os.getenv("GH_TOKEN") or input("Enter your GitHub token: ").strip() + TOKEN = (args.token or os.getenv("GH_TOKEN") or input("Enter your GitHub token: ")).strip() org_name = args.org or GH_ORG_NAME spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or GH_SHEET_NAME diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index c4f81b5..466b748 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -467,11 +467,11 @@ def main(): parser.add_argument("--token", default=None, help="Hugging Face token (overrides HF_TOKEN in .env)") parser.add_argument("--org", default=None, help="Hugging Face org name (overrides HF_ORG_NAME in .env)") parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") - parser.add_argument("--sheet-name", default="HF-Repos", help="Sheet tab name (default: HF-Repos)") + parser.add_argument("--sheet-name", default=None, help="Sheet tab name (overrides HF_SHEET_NAME in .env; default: HF-Repos)") parser.add_argument("--credentials-path", default=None, help="Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env)") args = parser.parse_args() - TOKEN = args.token or os.getenv("HF_TOKEN") or input("Enter your Hugging Face token: ").strip() or None + TOKEN = (args.token or os.getenv("HF_TOKEN") or input("Enter your Hugging Face token: ")).strip() or None org_name = args.org or HF_ORG_NAME spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or HF_SHEET_NAME From b7c55f0719c2fdd8afa418e7138369fd4e300a6f Mon Sep 17 00:00:00 2001 From: Amanda Lan <156035416+mandylan75@users.noreply.github.com> Date: Thu, 2 Jul 2026 12:26:33 -0400 Subject: [PATCH 04/12] Apply suggestions from code review Co-authored-by: Elizabeth Campolongo <38985481+egrace479@users.noreply.github.com> --- gh_repo_exporter.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/gh_repo_exporter.py b/gh_repo_exporter.py index 0f97c82..0c86a6f 100644 --- a/gh_repo_exporter.py +++ b/gh_repo_exporter.py @@ -18,6 +18,7 @@ GH_ORG_NAME = os.getenv("GH_ORG_NAME") SPREADSHEET_ID = os.getenv("SPREADSHEET_ID") GH_SHEET_NAME = os.getenv("GH_SHEET_NAME","GH-Repos") +GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") # Package requirement files to check @@ -517,20 +518,20 @@ def get_column_index(col_name: str): def main(): parser = argparse.ArgumentParser(description="Export GitHub org repo metadata to Google Sheets.") - parser.add_argument("--token", default=None, help="GitHub personal access token (overrides GH_TOKEN in .env)") parser.add_argument("--org", default=None, help="GitHub org name (overrides GH_ORG_NAME in .env)") + parser.add_argument("--token", default=None, help="GitHub personal access token (overrides GH_TOKEN in .env)") + parser.add_argument("--repo-type", default="all", help="Repo type filter: all, public, private, forks, sources, member; default: all") parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") - parser.add_argument("--sheet-name", default=None, help="Sheet tab name (overrides GH_SHEET_NAME in .env; default: GH-Repos)") - parser.add_argument("--credentials-path", default=None, help="Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env)") - parser.add_argument("--repo-type", default=None, help="Repo type filter: all, public, private, forks, sources, member (overrides REPO_TYPE in .env)") + parser.add_argument("--sheet-name", default=None, help=f"Sheet tab name (overrides GH_SHEET_NAME in .env; default: {GH_SHEET_NAME})") + parser.add_argument("--credentials-path", default=None, help=f"Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env; default: {GOOGLE_CREDENTIALS_PATH})") args = parser.parse_args() - TOKEN = (args.token or os.getenv("GH_TOKEN") or input("Enter your GitHub token: ")).strip() org_name = args.org or GH_ORG_NAME + TOKEN = args.token or GH_TOKEN + repo_type = args.repo_type spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or GH_SHEET_NAME - creds_path = args.credentials_path or os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") - repo_type = args.repo_type or os.getenv("REPO_TYPE") + creds_path = args.credentials_path or GOOGLE_CREDENTIALS_PATH required_vars = { "GH_ORG_NAME": org_name, From 02db0fd7fcc741ecdc52a504bebc1c41d1739f38 Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Thu, 2 Jul 2026 14:06:32 -0400 Subject: [PATCH 05/12] update env reads for hf_exporter --- gh_repo_exporter.py | 1 + hf_repo_exporter.py | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/gh_repo_exporter.py b/gh_repo_exporter.py index 0c86a6f..71ca727 100644 --- a/gh_repo_exporter.py +++ b/gh_repo_exporter.py @@ -18,6 +18,7 @@ GH_ORG_NAME = os.getenv("GH_ORG_NAME") SPREADSHEET_ID = os.getenv("SPREADSHEET_ID") GH_SHEET_NAME = os.getenv("GH_SHEET_NAME","GH-Repos") +GH_TOKEN = os.getenv("GH_TOKEN") GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") # Package requirement files to check diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index 466b748..85e01a2 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -18,6 +18,8 @@ HF_ORG_NAME = os.getenv("HF_ORG_NAME") SPREADSHEET_ID = os.getenv("SPREADSHEET_ID") HF_SHEET_NAME = os.getenv("HF_SHEET_NAME","HF-Repos") +HF_TOKEN = os.getenv("HF_TOKEN") +GOOGLE_CREDENTIALS_PATH = os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") # Helper Functions def get_repo_url(repo, repo_type: str) -> str: @@ -464,18 +466,20 @@ def get_column_index(col_name: str): def main(): parser = argparse.ArgumentParser(description="Export Hugging Face org repo metadata to Google Sheets.") - parser.add_argument("--token", default=None, help="Hugging Face token (overrides HF_TOKEN in .env)") parser.add_argument("--org", default=None, help="Hugging Face org name (overrides HF_ORG_NAME in .env)") + parser.add_argument("--token", default=None, help="Hugging Face token (overrides HF_TOKEN in .env)") + parser.add_argument("--repo-type", default="all", help="Repo type filter: all, public, private, forks, sources, member; default: all") parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") - parser.add_argument("--sheet-name", default=None, help="Sheet tab name (overrides HF_SHEET_NAME in .env; default: HF-Repos)") - parser.add_argument("--credentials-path", default=None, help="Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env)") + parser.add_argument("--sheet-name", default=None, help="fSheet tab name (overrides HF_SHEET_NAME in .env; default: {HF_SHEET_NAME})") + parser.add_argument("--credentials-path", default=None, help="fPath to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env; default: {GOOGLE_CREDENTIALS_PATH})") args = parser.parse_args() - TOKEN = (args.token or os.getenv("HF_TOKEN") or input("Enter your Hugging Face token: ")).strip() or None org_name = args.org or HF_ORG_NAME + TOKEN = args.token or HF_TOKEN + repo_type = args.repo_type spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or HF_SHEET_NAME - creds_path = args.credentials_path or os.getenv("GOOGLE_CREDENTIALS_PATH", "service_account.json") + creds_path = args.credentials_path or GOOGLE_CREDENTIALS_PATH required_vars = { "HF_ORG_NAME": org_name, From 8b153d38cca1b4493d75067ed8d077cfa37b4e78 Mon Sep 17 00:00:00 2001 From: Amanda Lan <156035416+mandylan75@users.noreply.github.com> Date: Thu, 2 Jul 2026 15:00:13 -0400 Subject: [PATCH 06/12] Update hf_repo_exporter.py Co-authored-by: Elizabeth Campolongo <38985481+egrace479@users.noreply.github.com> --- hf_repo_exporter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index 85e01a2..a09c2d2 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -470,8 +470,8 @@ def main(): parser.add_argument("--token", default=None, help="Hugging Face token (overrides HF_TOKEN in .env)") parser.add_argument("--repo-type", default="all", help="Repo type filter: all, public, private, forks, sources, member; default: all") parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") - parser.add_argument("--sheet-name", default=None, help="fSheet tab name (overrides HF_SHEET_NAME in .env; default: {HF_SHEET_NAME})") - parser.add_argument("--credentials-path", default=None, help="fPath to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env; default: {GOOGLE_CREDENTIALS_PATH})") + parser.add_argument("--sheet-name", default=None, help=f"Sheet tab name (overrides HF_SHEET_NAME in .env; default: {HF_SHEET_NAME})") + parser.add_argument("--credentials-path", default=None, help=f"Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env; default: {GOOGLE_CREDENTIALS_PATH})") args = parser.parse_args() org_name = args.org or HF_ORG_NAME From 10584939d8779d3d9eadc3659d62387ca9dbc784 Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Thu, 2 Jul 2026 15:27:15 -0400 Subject: [PATCH 07/12] delete repo type in hf-exporter --- hf_repo_exporter.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index a09c2d2..316a431 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -468,7 +468,6 @@ def main(): parser = argparse.ArgumentParser(description="Export Hugging Face org repo metadata to Google Sheets.") parser.add_argument("--org", default=None, help="Hugging Face org name (overrides HF_ORG_NAME in .env)") parser.add_argument("--token", default=None, help="Hugging Face token (overrides HF_TOKEN in .env)") - parser.add_argument("--repo-type", default="all", help="Repo type filter: all, public, private, forks, sources, member; default: all") parser.add_argument("--spreadsheet-id", default=None, help="Google Sheets spreadsheet ID (overrides SPREADSHEET_ID in .env)") parser.add_argument("--sheet-name", default=None, help=f"Sheet tab name (overrides HF_SHEET_NAME in .env; default: {HF_SHEET_NAME})") parser.add_argument("--credentials-path", default=None, help=f"Path to service_account.json (overrides GOOGLE_CREDENTIALS_PATH in .env; default: {GOOGLE_CREDENTIALS_PATH})") @@ -476,7 +475,6 @@ def main(): org_name = args.org or HF_ORG_NAME TOKEN = args.token or HF_TOKEN - repo_type = args.repo_type spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or HF_SHEET_NAME creds_path = args.credentials_path or GOOGLE_CREDENTIALS_PATH From 57f50c0a962e720f31a598e66478cf746f6f938a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Jul 2026 20:33:28 +0000 Subject: [PATCH 08/12] Update workflow to pass --repo-type as CLI arg instead of REPO_TYPE env var --- .github/workflows/gh-repo-exporter.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/gh-repo-exporter.yml b/.github/workflows/gh-repo-exporter.yml index 55eed78..7ca5a54 100644 --- a/.github/workflows/gh-repo-exporter.yml +++ b/.github/workflows/gh-repo-exporter.yml @@ -45,7 +45,6 @@ jobs: env: GH_TOKEN: ${{ secrets.GH_TOKEN }} GOOGLE_CREDENTIALS_PATH: service_account.json - REPO_TYPE: ${{ github.event.inputs.repo_type || 'all' }} GH_ORG_NAME: ${{ vars.GH_ORG_NAME }} SPREADSHEET_ID: ${{ vars.SPREADSHEET_ID }} - run: python gh_repo_exporter.py + run: python gh_repo_exporter.py --repo-type "${{ github.event.inputs.repo_type || 'all' }}" From b200141bbb4ed0c57c474be7f125bdb149bdd0ac Mon Sep 17 00:00:00 2001 From: Amanda Lan <156035416+mandylan75@users.noreply.github.com> Date: Thu, 2 Jul 2026 17:28:10 -0400 Subject: [PATCH 09/12] Apply suggestions from code review Co-authored-by: Elizabeth Campolongo <38985481+egrace479@users.noreply.github.com> --- gh_repo_exporter.py | 2 +- hf_repo_exporter.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/gh_repo_exporter.py b/gh_repo_exporter.py index 71ca727..966009b 100644 --- a/gh_repo_exporter.py +++ b/gh_repo_exporter.py @@ -528,7 +528,7 @@ def main(): args = parser.parse_args() org_name = args.org or GH_ORG_NAME - TOKEN = args.token or GH_TOKEN + TOKEN = args.token.strip() or GH_TOKEN repo_type = args.repo_type spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or GH_SHEET_NAME diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index 316a431..a4a8511 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -474,7 +474,7 @@ def main(): args = parser.parse_args() org_name = args.org or HF_ORG_NAME - TOKEN = args.token or HF_TOKEN + TOKEN = args.token.strip() or HF_TOKEN spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or HF_SHEET_NAME creds_path = args.credentials_path or GOOGLE_CREDENTIALS_PATH From f1789f795e7de6084d27977c47d93825c6a4dece Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Thu, 2 Jul 2026 18:20:12 -0400 Subject: [PATCH 10/12] implement copilots org_name fix --- hf_repo_exporter.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index a4a8511..a8f3986 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -30,12 +30,12 @@ def get_repo_url(repo, repo_type: str) -> str: else: # model return f"https://huggingface.co/{repo.id}" -def get_author(api, repo_id, repo_type) -> str: +def get_author(api, repo_id, repo_type, org_name: str | None = None) -> str: try: # Fetch all commits commits = api.list_repo_commits(repo_id=repo_id, repo_type=repo_type) if not commits: - return HF_ORG_NAME or "N/A" + return org_name or "N/A" # The last item in the list is the earliest commit (the creation) first_commit = commits[-1] @@ -48,13 +48,13 @@ def get_author(api, repo_id, repo_type) -> str: return first_author # If it's an object, check for user handle then display name - return getattr(first_author, 'user', getattr(first_author, 'name', HF_ORG_NAME or "N/A")) + return getattr(first_author, 'user', getattr(first_author, 'name', org_name or "N/A")) - return HF_ORG_NAME or "N/A" + return org_name or "N/A" except Exception: - return HF_ORG_NAME or "N/A" + return org_name or "N/A" -def get_top_contributors(api, repo_id, repo_type) -> str: +def get_top_contributors(api, repo_id, repo_type, org_name: str | None = None) -> str: try: commits = api.list_repo_commits(repo_id=repo_id, repo_type=repo_type) @@ -72,11 +72,11 @@ def get_top_contributors(api, repo_id, repo_type) -> str: all_handles.append(str(handle)) # Filter out the Org name and the web-flow bot - bots_and_orgs = {(HF_ORG_NAME or "").lower(), "web-flow"} + bots_and_orgs = {(org_name or "").lower(), "web-flow"} filtered = [n for n in all_handles if str(n).lower() not in bots_and_orgs] if not filtered: - return HF_ORG_NAME or "N/A" + return org_name or "N/A" counts = Counter(filtered) # Get top 4 most common contributors @@ -84,7 +84,7 @@ def get_top_contributors(api, repo_id, repo_type) -> str: return ", ".join(top_4) except Exception as e: # Optional: tqdm.write(f"Error for {repo_id}: {e}") - return HF_ORG_NAME or "N/A" + return org_name or "N/A" def get_open_pr_count(api, repo_id, repo_type) -> int: try: @@ -275,8 +275,7 @@ def extract_link_from_text(text, label): return content return "No" -def get_repo_info(api, repo, repo_type: str, token: str | None = None) -> dict[str, str | int]: - +def get_repo_info(api, repo, repo_type: str, token: str | None = None, org_name: str | None = None) -> dict[str, str | int]: # 1. Download README once readme_text = "" try: @@ -305,8 +304,8 @@ def get_repo_info(api, repo, repo_type: str, token: str | None = None) -> dict[s "Description": get_card_field(repo, ["model_description", "description"]) or "N/A", "Date Created": repo.created_at.strftime("%Y-%m-%d") if getattr(repo, "created_at", False) else "N/A", "Last Updated": repo.lastModified.strftime("%Y-%m-%d") if getattr(repo, "lastModified", False) else "N/A", - "Created By": get_author(api, repo.id, repo_type), - "Top 4 Contributors/Curators": get_top_contributors(api, repo.id, repo_type), + "Created By": get_author(api, repo.id, repo_type, org_name), + "Top 4 Contributors/Curators": get_top_contributors(api, repo.id, repo_type, org_name), "Likes": getattr(repo, "likes", "N/A"), "# of Open PRs": get_open_pr_count(api, repo.id, repo_type), "README": "Yes" if getattr(repo, "cardData", False) else "No", @@ -474,7 +473,7 @@ def main(): args = parser.parse_args() org_name = args.org or HF_ORG_NAME - TOKEN = args.token.strip() or HF_TOKEN + TOKEN = (args.token or HF_TOKEN or "").strip() or None spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or HF_SHEET_NAME creds_path = args.credentials_path or GOOGLE_CREDENTIALS_PATH @@ -526,7 +525,7 @@ def main(): for repo, repo_type in tqdm(repos, desc=f"Fetching HF repos from {org_name}...", unit="repo", colour="green", ncols=100, **tqdm_kwargs): try: - info = get_repo_info(api, repo, repo_type, token=TOKEN) + info = get_repo_info(api, repo, repo_type, token=TOKEN, org_name=org_name) data.append(info) tqdm.write(f"Fetched info for /{repo.id}") except Exception as e: From 0dae716135812c467b4f813e3629050be5031bfa Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Thu, 2 Jul 2026 18:26:03 -0400 Subject: [PATCH 11/12] update integration test to pass org_name explicitly --- tests/test_get_hf_repo_info_integration.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_get_hf_repo_info_integration.py b/tests/test_get_hf_repo_info_integration.py index a621f2d..7a32ebf 100644 --- a/tests/test_get_hf_repo_info_integration.py +++ b/tests/test_get_hf_repo_info_integration.py @@ -178,7 +178,7 @@ def test_get_repo_info_minimal_repo_defaults_to_no_or_na(): with patch("hf_repo_exporter.hf_hub_download", return_value="/fake/README.md"), \ patch("builtins.open", mock_open(read_data="Just a plain readme with nothing special.")), \ patch("hf_repo_exporter.HF_ORG_NAME", "imageomics"): - result = exporter.get_repo_info(api, repo, "model") + result = exporter.get_repo_info(api, repo, "model", org_name="imageomics") assert result["Repository Name"] == '=HYPERLINK("https://huggingface.co/imageomics/bare-repo", "imageomics/bare-repo")' assert result["Repository Type"] == "model" From 1674da0da8a2c3810760626b20b30fe10641639e Mon Sep 17 00:00:00 2001 From: mandylan75 Date: Thu, 2 Jul 2026 18:28:50 -0400 Subject: [PATCH 12/12] revert to old token line --- hf_repo_exporter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hf_repo_exporter.py b/hf_repo_exporter.py index a8f3986..1a54976 100644 --- a/hf_repo_exporter.py +++ b/hf_repo_exporter.py @@ -473,7 +473,7 @@ def main(): args = parser.parse_args() org_name = args.org or HF_ORG_NAME - TOKEN = (args.token or HF_TOKEN or "").strip() or None + TOKEN = args.token.strip() or HF_TOKEN spreadsheet_id = args.spreadsheet_id or SPREADSHEET_ID sheet_name = args.sheet_name or HF_SHEET_NAME creds_path = args.credentials_path or GOOGLE_CREDENTIALS_PATH