feat: add Gitea action to update language usage stats in profile README.md
Co-authored-by: aider (o3) <aider@aider.chat>
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
name: Update Language Stats
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 6 * * *" # run every day at 06:00 UTC
|
||||
workflow_dispatch: # allow manual runs
|
||||
push:
|
||||
paths:
|
||||
- "scripts/language_stats.py"
|
||||
|
||||
jobs:
|
||||
lang-stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Python deps
|
||||
run: sudo apt-get update && sudo apt-get install -y python3 python3-pip
|
||||
|
||||
- name: Run language stats script
|
||||
env:
|
||||
GITEA_BASE_URL: ${{ secrets.GITEA_BASE_URL }}
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
GITEA_USERNAME: ${{ secrets.GITEA_USERNAME }}
|
||||
run: python3 scripts/language_stats.py
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
git config --global user.name "lang-stats-bot"
|
||||
git config --global user.email "bot@example.com"
|
||||
git add .profile/README.md
|
||||
if git diff --cached --quiet; then
|
||||
echo "No changes to commit"
|
||||
else
|
||||
git commit -m "chore(profile): update language stats"
|
||||
git push
|
||||
fi
|
||||
@@ -0,0 +1,93 @@
|
||||
#!/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()
|
||||
Reference in New Issue
Block a user