Skip to content

Commit a59a755

Browse files
committed
Fix gitignore swallowing stats_template.html; add rate-limit UI
- .gitignore: re-allow stats_template.html (the /stats_*.html ignore rule was excluding it, which broke CI mining since render_html() couldn't find the template file). - Worker /api/status: also returns current GitHub search + core rate limit (remaining/limit/reset). Loading page renders a small rate-limit pill below the progress bar; turns orange and shows 'resets in Xs' when remaining is low (i.e. when the CI is in a cooldown sleep).
1 parent 1e3831c commit a59a755

3 files changed

Lines changed: 1552 additions & 2 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ __pycache__/
1010
# (pirate's enhanced version, regenerated locally via `python3 generate_stats.py`).
1111
/stats.html
1212
/stats_*.html
13+
!/stats_template.html
1314
/cloudflare/public/*.html
1415
!/cloudflare/public/pirate.html
1516
!/cloudflare/public/404.html

cloudflare/worker/index.ts

Lines changed: 85 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,17 +184,49 @@ async function handleStatus(
184184
status: s.status,
185185
conclusion: s.conclusion,
186186
}));
187+
188+
// Surface current GitHub API rate-limit state so the loading page can
189+
// explain delays. Uses the same PAT the CI runs with, so the search /
190+
// core remaining numbers are very close to what the CI job sees.
191+
let rateLimit: any = null;
192+
try {
193+
const rl = await fetch("https://api.github.com/rate_limit", {
194+
headers: {
195+
Authorization: `Bearer ${env.GH_DISPATCH_TOKEN}`,
196+
"User-Agent": "githubusers-archivebox-io",
197+
Accept: "application/vnd.github+json",
198+
},
199+
});
200+
if (rl.ok) {
201+
const rd = await rl.json() as any;
202+
const r = rd?.resources ?? {};
203+
rateLimit = {
204+
search: r.search ? {
205+
remaining: r.search.remaining,
206+
limit: r.search.limit,
207+
reset: r.search.reset, // epoch seconds
208+
} : null,
209+
core: r.core ? {
210+
remaining: r.core.remaining,
211+
limit: r.core.limit,
212+
reset: r.core.reset,
213+
} : null,
214+
};
215+
}
216+
} catch {}
217+
187218
return json({
188219
ok: true,
189220
run_id: run.id,
190-
run_status: run.status, // queued | in_progress | completed
191-
run_conclusion: run.conclusion, // success | failure | cancelled | null
221+
run_status: run.status,
222+
run_conclusion: run.conclusion,
192223
run_started_at: run.run_started_at,
193224
run_url: run.html_url,
194225
job_status: job?.status,
195226
current_step: steps.find((s: any) => s.status === "in_progress")?.name
196227
?? steps.at(-1)?.name ?? null,
197228
steps,
229+
rate_limit: rateLimit,
198230
});
199231
}
200232

@@ -272,6 +304,25 @@ function loadingPage(user: string): string {
272304
border: 1px solid #6e2120; border-radius: 6px; font-size: 13px;
273305
margin: 10px 0;
274306
}
307+
.ratelimit {
308+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
309+
font-size: 11px; padding: 8px 12px; margin: 0 0 14px;
310+
background: #0d1117; border: 1px solid #21262d;
311+
border-radius: 6px; color: #8b949e;
312+
display: flex; justify-content: space-between; align-items: center;
313+
gap: 12px; flex-wrap: wrap;
314+
}
315+
.ratelimit.cooldown {
316+
background: #2a1a08; border-color: #6e4c18; color: #ffa657;
317+
}
318+
.ratelimit .gauge {
319+
flex: 1; height: 4px; background: #21262d;
320+
border-radius: 2px; overflow: hidden; min-width: 80px;
321+
}
322+
.ratelimit .gauge > span {
323+
display: block; height: 100%; background: #3fb950;
324+
}
325+
.ratelimit.cooldown .gauge > span { background: #d97706; }
275326
a { color: #58a6ff; }
276327
code { background: #21262d; padding: 1px 5px; border-radius: 3px;
277328
font-size: 90%; font-family: inherit; }
@@ -297,6 +348,8 @@ function loadingPage(user: string): string {
297348
298349
<div class="progress-track"><div class="progress-fill" id="progress"></div></div>
299350
351+
<div id="ratelimit" class="ratelimit" style="display:none"></div>
352+
300353
<ol class="steps" id="steps"></ol>
301354
302355
<div id="error" class="err" style="display:none"></div>
@@ -320,6 +373,7 @@ const $steps = document.getElementById("steps");
320373
const $err = document.getElementById("error");
321374
const $runLink = document.getElementById("run-link");
322375
const $spinner = document.getElementById("hdr-spinner");
376+
const $rl = document.getElementById("ratelimit");
323377
324378
const startedAt = Date.now();
325379
function fmtElapsed(sec) {
@@ -386,6 +440,34 @@ async function checkDeployed() {
386440
}
387441
}
388442
443+
function renderRateLimit(rl) {
444+
if (!rl || (!rl.search && !rl.core)) {
445+
$rl.style.display = "none"; return;
446+
}
447+
const now = Math.floor(Date.now() / 1000);
448+
const items = [];
449+
for (const kind of ["search", "core"]) {
450+
const r = rl[kind];
451+
if (!r) continue;
452+
const pct = Math.max(0, Math.min(100, (r.remaining / r.limit) * 100));
453+
const cooldown = r.remaining < (kind === "search" ? 5 : 100);
454+
const secs = Math.max(0, (r.reset || 0) - now);
455+
const label = kind === "search" ? "GitHub search" : "GitHub core";
456+
items.push(
457+
'<div style="display:flex;align-items:center;gap:8px;flex:1;min-width:160px">' +
458+
'<span>' + label + ' ' + r.remaining + '/' + r.limit + '</span>' +
459+
'<div class="gauge"><span style="width:' + pct + '%"></span></div>' +
460+
(cooldown ? '<span style="color:#ffa657">resets in ' + secs + 's</span>' : '') +
461+
'</div>'
462+
);
463+
}
464+
$rl.innerHTML = items.join("");
465+
const lowSearch = rl.search && rl.search.remaining < 5;
466+
const lowCore = rl.core && rl.core.remaining < 100;
467+
$rl.className = "ratelimit" + (lowSearch || lowCore ? " cooldown" : "");
468+
$rl.style.display = "flex";
469+
}
470+
389471
function renderSteps(status) {
390472
if (!status || !status.steps) return;
391473
if (status.run_url) {
@@ -439,6 +521,7 @@ function renderSteps(status) {
439521
checkDeployed(),
440522
]);
441523
renderSteps(status);
524+
if (status) renderRateLimit(status.rate_limit);
442525
if (deployed) {
443526
clearInterval(interval);
444527
$now.textContent = "Dashboard ready — reloading…";

0 commit comments

Comments
 (0)