Files
tiennm99andGitHub 0d5e4b3266 feat: momentum metrics, agents.yml validation, drift detection, hardening (#9)
Review fixes:
- applyMigrations resolves chained renames deterministically (cycle guard)
- GraphQL errors name the failing owner/repo, not the alias
- sanitizeCell escapes backslash + angle brackets (README md/HTML injection)
- timeNow/graphqlURL seams; new httptest fetch tests, site + delta tests
  (coverage 30% -> 65.6%)

Features:
- delta30d computed from history; dashboard gains sortable D30d column
- README shows Top 7-day mover line
- 'go run . -check' validates agents.yml offline (dupes, categories,
  name patterns); wired into CI for contributor PRs
- isArchived + rename drift warnings in daily run; archived badge on
  dashboard (E2E found 3 archived repos: gpt-engineer, void, Roo-Code)
2026-08-09 03:26:01 +07:00

68 lines
2.0 KiB
Go

package main
import (
"encoding/json"
"os"
)
// siteRow is one ranked repo in site/data.json, consumed by site/index.html.
type siteRow struct {
Key string `json:"key"` // canonical owner/repo, matches history keys
NameWithOwner string `json:"nameWithOwner"`
URL string `json:"url"`
Stars int `json:"stars"`
Delta7d int `json:"delta7d"`
HasDelta bool `json:"hasDelta"`
Delta30d int `json:"delta30d"`
HasDelta30 bool `json:"hasDelta30"`
Language string `json:"language"`
PushedAt string `json:"pushedAt"`
Description string `json:"description"`
Category string `json:"category"`
Notes string `json:"notes,omitempty"`
Archived bool `json:"archived"`
}
type siteData struct {
UpdatedAt string `json:"updatedAt"`
Rows []siteRow `json:"rows"`
History []Snapshot `json:"history"`
}
// writeSiteData emits the JSON payload for the GitHub Pages dashboard.
// The file is generated fresh on every updater run and is not committed;
// the Pages deploy step in the workflow picks it up from the working tree.
func writeSiteData(path string, stats []Stat, deltas7, deltas30 map[string]int, history []Snapshot) error {
rows := make([]siteRow, len(stats))
for i, s := range stats {
delta7, has7 := deltas7[s.CanonicalKey]
delta30, has30 := deltas30[s.CanonicalKey]
rows[i] = siteRow{
Key: s.CanonicalKey,
NameWithOwner: s.NameWithOwner,
URL: s.URL,
Stars: s.Stars,
Delta7d: delta7,
HasDelta: has7,
Delta30d: delta30,
HasDelta30: has30,
Language: s.Language,
PushedAt: s.PushedAt.Format("2006-01-02"),
Description: s.Description,
Category: s.Category,
Notes: s.Notes,
Archived: s.IsArchived,
}
}
out, err := json.Marshal(siteData{
UpdatedAt: timeNow().UTC().Format("2006-01-02 15:04 UTC"),
Rows: rows,
History: history,
})
if err != nil {
return err
}
return os.WriteFile(path, out, 0o644)
}