feat: add GitHub repository dashboard with Prometheus metrics

ghtracker authenticates as a GitHub App and scans a configured list of repositories on a schedule. It serves a dashboard, a JSON state endpoint, a health probe and Prometheus metrics.

The dashboard lists only items that need attention: failing workflows, pull requests that wait too long or fail their checks, stale issues and projects that failed to scan. It has search, dynamic filters and a button that rescans on demand. The Vue app is embedded in the Go binary.

The client protects the GitHub rate limit with conditional requests, a request reserve and Retry-After handling.
This commit is contained in:
2026-10-02 04:28:52 +01:00
commit 5313b56774
110 changed files with 10967 additions and 0 deletions
+71
View File
@@ -0,0 +1,71 @@
export type Severity = 'ok' | 'warn' | 'crit'
export interface Release {
tag: string
url: string
publishedAt: string
prerelease: boolean
fromTag: boolean
}
export interface Repo {
fullName: string
url: string
releasesUrl: string
description: string
health: Severity
error: string
workflows: { total: number; failing: number; running: number }
prs: { open: number; waiting: number; failingChecks: number; oldestSeconds: number }
issues: { open: number; stale: number }
archived: boolean
up: boolean
refreshedAt: string
release: Release | null
}
export interface Alert {
repo: string
kind: string
severity: Severity
subject: string
detail: string
url: string
since: string
}
export interface Thresholds {
prWarnSeconds: number
prCritSeconds: number
issueStaleSeconds: number
workflowStuckSeconds: number
workflowDormantSeconds: number
}
export interface TrackerState {
now: string
lastRefresh: string
intervalSeconds: number
loaded: boolean
refreshing: boolean
manualRefresh: boolean
counts: { crit: number; warn: number }
thresholds: Thresholds
repos: Repo[]
alerts: Alert[]
}
/** One line in a repo block: every alert that targets the same URL, merged. */
export interface Row {
kind: string
subject: string
detail: string
url: string
since: string
severity: Exclude<Severity, 'ok'>
}
export interface Block {
repo: Repo
rows: Row[]
}