126 lines
4.5 KiB
Bash
Executable File
126 lines
4.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# ── Gitea Workflow Feed ────────────────────────────────────────────────
|
|
# Shows open PRs, review pressure, and issue queues across core repos.
|
|
# ───────────────────────────────────────────────────────────────────────
|
|
|
|
set -euo pipefail
|
|
|
|
B='\033[1m'
|
|
D='\033[2m'
|
|
R='\033[0m'
|
|
C='\033[36m'
|
|
G='\033[32m'
|
|
Y='\033[33m'
|
|
|
|
GITEA_URL="${GITEA_URL:-http://143.198.27.163:3000}"
|
|
CORE_REPOS="${CORE_REPOS:-Timmy_Foundation/the-nexus Timmy_Foundation/timmy-home Timmy_Foundation/timmy-config Timmy_Foundation/hermes-agent}"
|
|
|
|
for token_file in "$HOME/.hermes/gitea_token_vps" "$HOME/.config/gitea/token" "$HOME/.config/gitea/codex-token"; do
|
|
if [ -f "$token_file" ]; then
|
|
TOKEN=$(cat "$token_file")
|
|
break
|
|
fi
|
|
done
|
|
|
|
TOKEN="${TOKEN:-}"
|
|
|
|
echo -e "${B}${C} ◈ GITEA WORKFLOW${R} ${D}$(date '+%H:%M:%S')${R}"
|
|
echo -e "${D}────────────────────────────────────────${R}"
|
|
|
|
python3 - "$GITEA_URL" "$TOKEN" "$CORE_REPOS" <<'PY'
|
|
import json
|
|
import sys
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
base = sys.argv[1].rstrip("/")
|
|
token = sys.argv[2]
|
|
repos = sys.argv[3].split()
|
|
headers = {"Authorization": f"token {token}"} if token else {}
|
|
|
|
|
|
def fetch(path):
|
|
req = urllib.request.Request(f"{base}{path}", headers=headers)
|
|
with urllib.request.urlopen(req, timeout=5) as resp:
|
|
return json.loads(resp.read().decode())
|
|
|
|
|
|
def short_repo(repo):
|
|
return repo.split("/", 1)[1]
|
|
|
|
|
|
issues = []
|
|
pulls = []
|
|
errors = []
|
|
|
|
for repo in repos:
|
|
try:
|
|
repo_pulls = fetch(f"/api/v1/repos/{repo}/pulls?state=open&limit=20")
|
|
for pr in repo_pulls:
|
|
pr["_repo"] = repo
|
|
pulls.append(pr)
|
|
repo_issues = fetch(f"/api/v1/repos/{repo}/issues?state=open&limit=50&type=issues")
|
|
for issue in repo_issues:
|
|
issue["_repo"] = repo
|
|
issues.append(issue)
|
|
except urllib.error.URLError as exc:
|
|
errors.append(f"{repo}: {exc.reason}")
|
|
except Exception as exc: # pragma: no cover - defensive panel path
|
|
errors.append(f"{repo}: {exc}")
|
|
|
|
print(" \033[1mOpen PRs\033[0m")
|
|
if not pulls:
|
|
print(" (none)")
|
|
else:
|
|
for pr in pulls[:8]:
|
|
print(
|
|
f" #{pr['number']:3d} {short_repo(pr['_repo']):12s} "
|
|
f"{pr['user']['login'][:12]:12s} {pr['title'][:40]}"
|
|
)
|
|
|
|
print("\033[2m────────────────────────────────────────\033[0m")
|
|
print(" \033[1mNeeds Timmy / Allegro Review\033[0m")
|
|
reviewers = []
|
|
for repo in repos:
|
|
try:
|
|
repo_items = fetch(f"/api/v1/repos/{repo}/issues?state=open&limit=50&type=pulls")
|
|
for item in repo_items:
|
|
assignees = [a.get("login", "") for a in (item.get("assignees") or [])]
|
|
if any(name in assignees for name in ("Timmy", "allegro")):
|
|
item["_repo"] = repo
|
|
reviewers.append(item)
|
|
except Exception:
|
|
continue
|
|
|
|
if not reviewers:
|
|
print(" (clear)")
|
|
else:
|
|
for item in reviewers[:8]:
|
|
names = ",".join(a.get("login", "") for a in (item.get("assignees") or []))
|
|
print(
|
|
f" #{item['number']:3d} {short_repo(item['_repo']):12s} "
|
|
f"{names[:18]:18s} {item['title'][:34]}"
|
|
)
|
|
|
|
print("\033[2m────────────────────────────────────────\033[0m")
|
|
print(" \033[1mIssue Queues\033[0m")
|
|
queue_agents = ["allegro", "codex-agent", "groq", "claude", "ezra", "perplexity", "KimiClaw"]
|
|
for agent in queue_agents:
|
|
assigned = [
|
|
issue
|
|
for issue in issues
|
|
if agent in [a.get("login", "") for a in (issue.get("assignees") or [])]
|
|
]
|
|
print(f" {agent:12s} {len(assigned):2d}")
|
|
|
|
unassigned = [issue for issue in issues if not issue.get("assignees")]
|
|
print("\033[2m────────────────────────────────────────\033[0m")
|
|
print(f" Unassigned issues: \033[33m{len(unassigned)}\033[0m")
|
|
|
|
if errors:
|
|
print("\033[2m────────────────────────────────────────\033[0m")
|
|
print(" \033[1mErrors\033[0m")
|
|
for err in errors[:4]:
|
|
print(f" {err}")
|
|
PY
|