Files
profile-stats/scripts/language_stats.py
T

94 lines
2.8 KiB
Python

#!/usr/bin/env python3
"""
Gitea Language Stats Action
Calculates the percentage of every programming language used
in all repositories of a given account and writes / updates
a markdown table inside `.profile/README.md` delimited by the
markers <!--LANG_STATS_START--> … <!--LANG_STATS_END-->.
Required environment variables
--------------------------------
GITEA_BASE_URL e.g. https://gitea.example.com
GITEA_TOKEN personal access-token with `repo` scope
GITEA_USERNAME the account to analyse
"""
import os
import sys
import requests
from collections import defaultdict
BASE_URL = os.getenv("GITEA_BASE_URL", "").rstrip("/")
TOKEN = os.getenv("GITEA_TOKEN")
USERNAME = os.getenv("GITEA_USERNAME")
if not BASE_URL or not TOKEN or not USERNAME:
sys.exit("GITEA_BASE_URL, GITEA_TOKEN and GITEA_USERNAME must be set")
session = requests.Session()
session.headers["Authorization"] = f"token {TOKEN}"
# ---------- collect repositories ----------
page = 1
repos = []
while True:
resp = session.get(
f"{BASE_URL}/api/v1/users/{USERNAME}/repos",
params={"page": page, "limit": 50},
timeout=30,
)
resp.raise_for_status()
batch = resp.json()
if not batch:
break
repos.extend(batch)
page += 1
# ---------- aggregate language bytes ----------
totals: dict[str, int] = defaultdict(int)
for repo in repos:
langs = session.get(
f"{BASE_URL}/api/v1/repos/{USERNAME}/{repo['name']}/languages",
timeout=30,
).json()
for lang, bytes_ in langs.items():
totals[lang] += bytes_
total_bytes = sum(totals.values())
if total_bytes == 0:
sys.exit("No language data returned from API.")
# ---------- build markdown table ----------
pairs = sorted(totals.items(), key=lambda x: x[1], reverse=True)
lines = [
"## 📊 Language Usage\n",
"| Language | Percent |\n",
"|----------|---------|\n",
]
for lang, nbytes in pairs:
pct = nbytes * 100 / total_bytes
bar = "█" * int(pct / 2) # up to 50 chars
lines.append(f"| {lang} | {pct:5.1f}% {bar} |\n")
block = "<!--LANG_STATS_START-->\n" + "".join(lines) + "<!--LANG_STATS_END-->\n"
# ---------- patch .profile/README.md ----------
readme_path = ".profile/README.md"
os.makedirs(".profile", exist_ok=True)
if not os.path.exists(readme_path):
with open(readme_path, "w", encoding="utf-8") as fh:
fh.write(block)
else:
with open(readme_path, "r+", encoding="utf-8") as fh:
content = fh.read()
if "<!--LANG_STATS_START-->" in content and "<!--LANG_STATS_END-->" in content:
pre = content.split("<!--LANG_STATS_START-->")[0]
post = content.split("<!--LANG_STATS_END-->")[-1]
content = pre + block + post
else:
content = content.rstrip() + "\n\n" + block
fh.seek(0)
fh.write(content)
fh.truncate()