Show recent GitHub commits on competitor cards
Some checks failed
Deploy to Cloudflare / deploy (push) Has been cancelled

Each competitor card now shows their last 3 commits from their repo.
Commits are fetched from GitHub API and cached in KV for 5 minutes.
- Omar: Bored-investments/chuckasickie
- Rhys: Bored-investments/milly-countdown

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
King Omar 2026-06-03 23:25:02 +10:00
parent a8bac830ea
commit f72fbbf8e0

View file

@ -26,13 +26,14 @@ export default {
return new Response('Updated'); return new Response('Updated');
} }
const [revenuesRaw, metricsRaw] = await Promise.all([ const competitors = defaultCompetitors();
const [revenuesRaw, metricsRaw, ...commitArrays] = await Promise.all([
env.MILLY_METRICS.get('revenues'), env.MILLY_METRICS.get('revenues'),
env.MILLY_METRICS.get('metrics'), env.MILLY_METRICS.get('metrics'),
...Object.values(competitors).map(c => fetchCommits(env, c.repo)),
]); ]);
// Static config always comes from code — KV only stores revenue numbers
const competitors = defaultCompetitors();
const revenues = revenuesRaw ? JSON.parse(revenuesRaw) : {}; const revenues = revenuesRaw ? JSON.parse(revenuesRaw) : {};
const metrics = metricsRaw ? JSON.parse(metricsRaw) : {}; const metrics = metricsRaw ? JSON.parse(metricsRaw) : {};
@ -42,6 +43,10 @@ export default {
for (const id of ['rhys', 'pussy', 'patty']) { for (const id of ['rhys', 'pussy', 'patty']) {
if (revenues[id] != null) competitors[id].revenue = revenues[id]; if (revenues[id] != null) competitors[id].revenue = revenues[id];
} }
// Attach commits
Object.keys(competitors).forEach((id, i) => {
competitors[id].commits = commitArrays[i] || [];
});
return new Response(render(competitors), { return new Response(render(competitors), {
headers: { 'Content-Type': 'text/html;charset=UTF-8', 'Cache-Control': 'no-store' } headers: { 'Content-Type': 'text/html;charset=UTF-8', 'Cache-Control': 'no-store' }
@ -51,13 +56,34 @@ export default {
function defaultCompetitors() { function defaultCompetitors() {
return { return {
omar: { name:'King Omar', ai:'Claude Pro', revenue:0, color:'#6366f1', emoji:'👑', github:'kingomarwashere' }, omar: { name:'King Omar', ai:'Claude Pro', revenue:0, color:'#6366f1', emoji:'👑', github:'kingomarwashere', repo:'Bored-investments/chuckasickie' },
rhys: { name:'Basic Rhys', ai:'Codex', revenue:0, color:'#f59e0b', emoji:'🤓', github:'rhy-collab' }, rhys: { name:'Basic Rhys', ai:'Codex', revenue:0, color:'#f59e0b', emoji:'🤓', github:'rhy-collab', repo:'Bored-investments/milly-countdown' },
pussy: { name:'Pussy', ai:'Hermes Plus Ching Chong',revenue:0, color:'#ec4899', emoji:'🐱', github:'QuixThe2nd' }, pussy: { name:'Pussy', ai:'Hermes Plus Ching Chong',revenue:0, color:'#ec4899', emoji:'🐱', github:'QuixThe2nd', repo:null },
patty: { name:'Patty', ai:'Copilot & Clawpilot', revenue:0, color:'#10b981', emoji:'🤠', github:null }, patty: { name:'Patty', ai:'Copilot & Clawpilot', revenue:0, color:'#10b981', emoji:'🤠', github:null, repo:null },
}; };
} }
async function fetchCommits(env, repo) {
if (!repo) return [];
const cacheKey = `commits:${repo}`;
const cached = await env.MILLY_METRICS.get(cacheKey);
if (cached) return JSON.parse(cached);
try {
const res = await fetch(`https://api.github.com/repos/${repo}/commits?per_page=3`, {
headers: { 'User-Agent': 'milly-countdown' }
});
const data = await res.json();
if (!Array.isArray(data)) return [];
const commits = data.map(c => ({
sha: c.sha?.slice(0, 7),
message: c.commit?.message?.split('\n')[0]?.slice(0, 72),
date: c.commit?.author?.date,
}));
await env.MILLY_METRICS.put(cacheKey, JSON.stringify(commits), { expirationTtl: 300 });
return commits;
} catch { return []; }
}
const fmt = n => n >= 1e6 ? `$${(n/1e6).toFixed(3)}M` : n >= 1000 ? `$${(n/1000).toFixed(1)}K` : `$${n.toFixed(0)}`; const fmt = n => n >= 1e6 ? `$${(n/1e6).toFixed(3)}M` : n >= 1000 ? `$${(n/1000).toFixed(1)}K` : `$${n.toFixed(0)}`;
const pct = r => Math.min((r / GOAL) * 100, 100); const pct = r => Math.min((r / GOAL) * 100, 100);
@ -223,6 +249,14 @@ html,body{min-height:100vh;background:#080401;color:#fff;font-family:'Inter',san
.org-btn:hover{background:#1f1f1f;border-color:#6366f1;color:#fff} .org-btn:hover{background:#1f1f1f;border-color:#6366f1;color:#fff}
.org-btn svg{width:16px;height:16px;fill:currentColor} .org-btn svg{width:16px;height:16px;fill:currentColor}
/* === COMMITS === */
.card-commits{margin-top:10px;border-top:1px solid rgba(255,255,255,0.05);padding-top:8px;display:flex;flex-direction:column;gap:4px}
.card-commit{display:flex;align-items:baseline;gap:6px;font-size:11px;line-height:1.4}
.commit-sha{font-family:'Courier New',monospace;color:#4b5563;text-decoration:none;flex-shrink:0;
transition:color 0.2s}
.commit-sha:hover{color:#9ca3af}
.commit-msg{color:#6b7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* === FOOTER === */ /* === FOOTER === */
footer{text-align:center;padding:24px 20px;border-top:1px solid #140900;color:#374151;font-size:12px} footer{text-align:center;padding:24px 20px;border-top:1px solid #140900;color:#374151;font-size:12px}
footer a{color:#4b5563;text-decoration:none} footer a{color:#4b5563;text-decoration:none}
@ -301,6 +335,12 @@ footer a:hover{color:#9ca3af}
<svg viewBox="0 0 16 16"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg> <svg viewBox="0 0 16 16"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
github.com/${c.github} github.com/${c.github}
</a>` : `<span class="card-github" style="opacity:0.3">no github connected</span>`} </a>` : `<span class="card-github" style="opacity:0.3">no github connected</span>`}
${c.commits?.length ? `<div class="card-commits">
${c.commits.map(cm => `<div class="card-commit">
<a href="https://github.com/${c.repo}/commit/${cm.sha}" target="_blank" class="commit-sha">${cm.sha}</a>
<span class="commit-msg">${cm.message}</span>
</div>`).join('')}
</div>` : ''}
</div>`).join('')} </div>`).join('')}
</div> </div>
</div> </div>