From cab29314618a059eee7ca887ec46b659fc31928e Mon Sep 17 00:00:00 2001 From: Christoph Pfoertner Date: Sat, 3 Oct 2026 21:44:17 +0200 Subject: [PATCH] fix(ci): blink Gitea CI indicator only during actively running workflow runs --- index.html | 41 +++++++++++++++++++++++++++++++++++------ server.py | 21 +++++++++++++++++---- 2 files changed, 52 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 3b100cc..5226c95 100644 --- a/index.html +++ b/index.html @@ -642,8 +642,9 @@ } }; - // Live Kanban Counts on Whiteboard + // Live Kanban Counts & CI State let kanbanState = { planned: 3, ready: 1, in_progress: 0, review: 2, done: 12 }; + let giteaCiState = { active_jobs: 0, status: 'PASSING' }; function addLog(text) { const logBox = document.getElementById('eventLog'); @@ -705,6 +706,12 @@ const wipElem = document.getElementById('giteaWip'); if (wipElem) wipElem.innerText = kanbanState.in_progress || 0; } + if (data.gitea.ci_status !== undefined) { + giteaCiState = { + active_jobs: data.gitea.active_ci_jobs || 0, + status: data.gitea.ci_status || 'PASSING' + }; + } // 4. Update Agents if (data.agents) { @@ -1096,12 +1103,34 @@ ctx.beginPath(); ctx.arc(f.x + 15, f.y + 40, 4, 0, Math.PI*2); ctx.fill(); ctx.beginPath(); ctx.arc(f.x + 35, f.y + 46, 4, 0, Math.PI*2); ctx.fill(); - const ciPass = Math.sin(time * 0.01) > 0; - ctx.fillStyle = ciPass ? "#22c55e" : "#16a34a"; - ctx.fillRect(f.x + 48, f.y + 18, 18, 10); + // Gitea CI Indicator + const isCiActive = (giteaCiState.active_jobs > 0 || giteaCiState.status === 'BUILDING'); + let ciColor = "#22c55e"; // Solid Green when idle/passing + + if (isCiActive) { + // Blink/pulsate only while a workflow is actively running + const ciBlink = Math.sin(time * 0.015) > 0; + ciColor = ciBlink ? "#fbbf24" : "#d97706"; + } else if (giteaCiState.status === 'FAILURE') { + ciColor = "#ef4444"; + } + + ctx.fillStyle = ciColor; + ctx.fillRect(f.x + 48, f.y + 18, 20, 11); + ctx.strokeStyle = "rgba(0,0,0,0.4)"; + ctx.lineWidth = 1; + ctx.strokeRect(f.x + 48, f.y + 18, 20, 11); + ctx.font = "8px 'Press Start 2P'"; - ctx.fillStyle = "#fff"; - ctx.fillText("CI", f.x + 51, f.y + 26); + ctx.fillStyle = isCiActive ? "#0f172a" : "#ffffff"; + ctx.fillText("CI", f.x + 52, f.y + 27); + + // Optional mini job counter indicator if building + if (isCiActive && giteaCiState.active_jobs > 0) { + ctx.font = "9px 'VT323'"; + ctx.fillStyle = "#fbbf24"; + ctx.fillText(`⚡${giteaCiState.active_jobs}`, f.x + 48, f.y + 40); + } ctx.font = "11px 'Press Start 2P'"; ctx.fillStyle = "#38bdf8"; diff --git a/server.py b/server.py index 74731a1..4eb9184 100644 --- a/server.py +++ b/server.py @@ -144,10 +144,23 @@ class RealtimeRPGHandler(http.server.SimpleHTTPRequestHandler): # 2. Collect Gitea Live PRs & Issues for Whiteboard Kanban strictly for target_project try: - ci_res = subprocess.run(['docker', 'ps', '--filter', 'name=GITEA-ACTIONS', '--format', '{{.Names}}'], capture_output=True, text=True, timeout=2) - ci_containers = [l for l in ci_res.stdout.strip().split('\n') if l] - status["gitea"]["active_ci_jobs"] = len(ci_containers) - status["gitea"]["ci_status"] = "BUILDING" if len(ci_containers) > 0 else "PASSING" + # Check Gitea Actions API for actively running workflows + active_runs_count = 0 + try: + req_runs = urllib.request.Request( + f"{GITEA_BASE_URL}/repos/openclaw/{target_project}/actions/runs?limit=10", + headers={'Authorization': f'token {GITEA_TOKEN}'} + ) + with urllib.request.urlopen(req_runs, timeout=2) as resp: + runs_data = json.loads(resp.read().decode()) + for r in runs_data.get("workflow_runs", []): + if r.get("status") in ["running", "waiting", "queued"]: + active_runs_count += 1 + except Exception: + pass + + status["gitea"]["active_ci_jobs"] = active_runs_count + status["gitea"]["ci_status"] = "BUILDING" if active_runs_count > 0 else "PASSING" # Fetch Open Pull Requests req_prs = urllib.request.Request(