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
+146
View File
@@ -0,0 +1,146 @@
// Package model holds the data shapes shared by the collector, metrics and web layers.
package model
import (
"errors"
"time"
)
// ErrRateLimited means GitHub limits were hit or are about to be; callers must stop issuing requests.
var ErrRateLimited = errors.New("github rate limit reached")
type Severity int
const (
OK Severity = iota
Warn
Crit
)
func (s Severity) String() string {
switch s {
case Warn:
return "warn"
case Crit:
return "crit"
default:
return "ok"
}
}
type CheckState string
const (
ChecksNone CheckState = "none"
ChecksPending CheckState = "pending"
ChecksPass CheckState = "success"
ChecksFail CheckState = "failure"
ChecksUnknown CheckState = "unknown"
)
type Repo struct {
RefreshedAt time.Time
Release *Release
Description string
FullName string
DefaultBranch string
ReleasesURL string
// Err is set when the last refresh failed entirely; the other fields hold the last good data.
Err string
URL string
Issues []Issue
PRs []PullRequest
Workflows []Workflow
// Warnings lists sections that failed to load while the rest succeeded.
Warnings []string
Health Severity
Archived bool
Up bool
}
type Release struct {
PublishedAt time.Time
Tag string
Name string
URL string
Prerelease bool
// FromTag marks a version taken from tags because the repo has no releases.
FromTag bool
}
type Issue struct {
CreatedAt time.Time
UpdatedAt time.Time
Title string
URL string
Author string
Labels []string
Number int
Comments int
}
type PullRequest struct {
CreatedAt time.Time
UpdatedAt time.Time
Title string
URL string
Author string
HeadSHA string
Labels []string
Checks CheckSummary
Number int
Draft bool
}
type CheckSummary struct {
State CheckState
Failing []string
Total int
Failed int
Pending int
}
type Workflow struct {
// LastDone is the newest completed run; nil when no run has completed yet.
LastDone *Run
Name string
URL string
Latest Run
Background bool
}
type Run struct {
CreatedAt time.Time
UpdatedAt time.Time
URL string
Event string
Branch string
Status string
Conclusion string
ID int64
}
// Running reports whether the run has not completed.
func (r Run) Running() bool { return r.Status != "completed" }
// Failed reports whether a completed run ended in a failing conclusion.
func (r Run) Failed() bool {
switch r.Conclusion {
case "failure", "timed_out", "startup_failure", "action_required":
return true
}
return false
}
type Alert struct {
Since time.Time
Repo string
Kind string
Subject string
Detail string
URL string
Severity Severity
}
// Message renders the alert as one line for logs and the JSON API.
func (a Alert) Message() string { return a.Subject + ": " + a.Detail }