--- title: Repo Sync Health --- # Repo Sync Health ```js const repoData = await FileAttachment("data/repos.json").json(); const inventory = await FileAttachment("data/gitea-inventory.json").json(); const repos = Array.isArray(repoData) ? repoData : (repoData.repos ?? []); ``` ```js // Helpers function ageMs(ts) { if (!ts) return Infinity; return Date.now() - new Date(ts).getTime(); } function fmtAge(ts) { if (!ts) return "never"; const ms = ageMs(ts); const m = Math.floor(ms / 60000); if (m < 60) return `${m}m ago`; const h = Math.floor(m / 60); if (h < 24) return `${h}h ago`; return `${Math.floor(h / 24)}d ago`; } function syncColor(ts) { if (!ts) return "var(--theme-red)"; const h = ageMs(ts) / 3600000; if (h < 1) return "var(--theme-green)"; if (h < 24) return "var(--theme-orange)"; return "var(--theme-red)"; } ``` ## Registered Repos — Sync Status ```js const activeRepos = repos.filter(r => r.status === "active"); const staleCount = activeRepos.filter(r => !r.last_state_synced_at || ageMs(r.last_state_synced_at) > 86400000).length; const freshCount = activeRepos.filter(r => r.last_state_synced_at && ageMs(r.last_state_synced_at) < 3600000).length; const sbomCount = activeRepos.filter(r => r.last_sbom_at).length; const noSbomCount = activeRepos.length - sbomCount; ``` ```js display(html`
| Repo | Domain | Last Synced | Last SBOM | Entries | Status |
|---|---|---|---|---|---|
| ${r.slug} | ${r.domain_slug} | ${fmtAge(r.last_state_synced_at)} | ${fmtAge(r.last_sbom_at)} | ${r.sbom_entry_count > 0 ? r.sbom_entry_count.toLocaleString() : "—"} | ${r.status} |
🎉 All Gitea repos are registered!
`); } else { display(html`| Repo | Language | Description | Onboard |
|---|---|---|---|
| ${r.gitea_name} | ${r.language || "—"} | ${r.description || "—"} | make register-project DOMAIN=? PROJECT_PATH=/home/worsch/${r.gitea_name} |
None — all hub repos have a Gitea counterpart.
`); } else { display(html`${r.slug} — domain: ${r.domain}, status: ${r.status}GITEA_TOKEN in state-hub/.env._