apiVersion: v1 kind: ConfigMap metadata: name: forgejo-metrics-script namespace: forgejo-runners data: server.py: | #!/usr/bin/env python3 """ Polls the Forgejo API for waiting action tasks and exposes the count as JSON. KEDA metrics-api scaler reads: { "value": } """ import http.server import json import os import threading import time import urllib.request FORGEJO_URL = os.environ["FORGEJO_URL"].rstrip("/") TOKEN = "69eccfc51f720c21c615cfd5caa422fb02f0ab43" REFRESH = int(os.environ.get("REFRESH_INTERVAL", "20")) _state = {"value": 0} _lock = threading.Lock() def fetch(path): req = urllib.request.Request( f"{FORGEJO_URL}/api/v1{path}", headers={"Authorization": f"token {TOKEN}", "Accept": "application/json"}, ) with urllib.request.urlopen(req, timeout=10) as r: return json.loads(r.read()) def count_pending_jobs(): total = 0 page = 1 while True: repos = fetch(f"/repos/search?limit=50&page={page}") items = repos.get("data", []) for repo in items: owner = repo["owner"]["login"] name = repo["name"] try: result = fetch( f"/repos/{owner}/{name}/actions/tasks?status=waiting&limit=1" ) total += result.get("total_count", 0) except Exception: pass if len(items) < 50: break page += 1 return total def refresh_loop(): while True: try: count = count_pending_jobs() with _lock: _state["value"] = count print(f"pending jobs: {count}", flush=True) except Exception as e: print(f"refresh error: {e}", flush=True) time.sleep(REFRESH) threading.Thread(target=refresh_loop, daemon=True).start() class Handler(http.server.BaseHTTPRequestHandler): def do_GET(self): with _lock: val = _state["value"] body = json.dumps({"value": val}).encode() self.send_response(200) self.send_header("Content-Type", "application/json") self.end_headers() self.wfile.write(body) def log_message(self, *_): pass http.server.HTTPServer(("", 8080), Handler).serve_forever() --- apiVersion: apps/v1 kind: Deployment metadata: name: forgejo-metrics-exporter namespace: forgejo-runners spec: replicas: 1 selector: matchLabels: app: forgejo-metrics-exporter template: metadata: labels: app: forgejo-metrics-exporter spec: containers: - name: exporter image: python:3.12-alpine command: ["python", "/scripts/server.py"] env: - name: FORGEJO_URL value: https://git.juntekim.com - name: FORGEJO_TOKEN valueFrom: secretKeyRef: name: forgejo-api-secret key: token - name: REFRESH_INTERVAL value: "20" ports: - containerPort: 8080 resources: requests: cpu: 10m memory: 32Mi limits: cpu: 100m memory: 64Mi volumeMounts: - name: scripts mountPath: /scripts volumes: - name: scripts configMap: name: forgejo-metrics-script --- apiVersion: v1 kind: Service metadata: name: forgejo-metrics-exporter namespace: forgejo-runners spec: selector: app: forgejo-metrics-exporter ports: - port: 8080 targetPort: 8080