diff --git a/TODO.md b/TODO.md index df4c7b0..f3f9a37 100644 --- a/TODO.md +++ b/TODO.md @@ -21,14 +21,14 @@ ## Code quality -- [ ] Trim the `summary` struct to the fields actually used; `interface{}` should be `any` -- [ ] Print the overall status (`Status.Description`) -- [ ] Replace the if/else chain with a `switch` (staticcheck QF1003) covering +- [x] Trim the `summary` struct to the fields actually used; `interface{}` should be `any` +- [x] Print the overall status (`Status.Description`) +- [x] Replace the if/else chain with a `switch` (staticcheck QF1003) covering `operational`, `degraded_performance`, `partial_outage`, `major_outage`, `under_maintenance` -- [ ] Filter the "Visit www.githubstatus.com for more information" filler row +- [x] Filter the "Visit www.githubstatus.com for more information" filler row - [x] Use `json.NewDecoder(resp.Body).Decode` instead of `io.ReadAll` + `Unmarshal` -- [ ] Use the readable URL `https://www.githubstatus.com/api/v2/summary.json` +- [x] Use the readable URL `https://www.githubstatus.com/api/v2/summary.json` - [x] Add a package comment (revive) - [ ] Exit non-zero when any component isn't operational diff --git a/github_status.go b/github_status.go index 1a67068..7ec3a2e 100644 --- a/github_status.go +++ b/github_status.go @@ -17,35 +17,16 @@ import ( type summary struct { Components []struct { - CreatedAt string `json:"created_at"` - Description string `json:"description"` - Group bool `json:"group"` - GroupID interface{} `json:"group_id"` - ID string `json:"id"` - Name string `json:"name"` - OnlyShowIfDegraded bool `json:"only_show_if_degraded"` - PageID string `json:"page_id"` - Position int64 `json:"position"` - Showcase bool `json:"showcase"` - Status string `json:"status"` - UpdatedAt string `json:"updated_at"` + Name string `json:"name"` + Status string `json:"status"` + Showcase bool `json:"showcase"` } `json:"components"` - Incidents []interface{} `json:"incidents"` - Page struct { - ID string `json:"id"` - Name string `json:"name"` - TimeZone string `json:"time_zone"` - UpdatedAt string `json:"updated_at"` - URL string `json:"url"` - } `json:"page"` - ScheduledMaintenances []interface{} `json:"scheduled_maintenances"` - Status struct { + Status struct { Description string `json:"description"` - Indicator string `json:"indicator"` } `json:"status"` } -const summaryURL = "https://kctbh9vrtdwd.statuspage.io/api/v2/summary.json" +const summaryURL = "https://www.githubstatus.com/api/v2/summary.json" func main() { s, err := fetch(summaryURL) @@ -74,25 +55,43 @@ func fetch(url string) (summary, error) { return s, nil } -// render writes one line per component to w, with the status colorized. +// render writes the overall status followed by one line per component to w, +// with each component status colorized. Components not shown on the status +// page (showcase false) are skipped. func render(w io.Writer, s summary) error { + if _, err := fmt.Fprintf(w, "%s\n\n", s.Status.Description); err != nil { + return err + } for _, component := range s.Components { - c := color.New(color.FgYellow) - if component.Status == "operational" { - c = color.New(color.FgGreen) - } else if component.Status == "major_outage" { - c = color.New(color.FgRed) + if !component.Showcase { + continue } if _, err := fmt.Fprintf(w, "%-60s", component.Name); err != nil { return err } - if _, err := c.Fprintln(w, component.Status); err != nil { + if _, err := statusColor(component.Status).Fprintln(w, component.Status); err != nil { return err } } return nil } +// statusColor returns the color used to display a component status. +func statusColor(status string) *color.Color { + switch status { + case "operational": + return color.New(color.FgGreen) + case "degraded_performance", "partial_outage": + return color.New(color.FgYellow) + case "major_outage": + return color.New(color.FgRed) + case "under_maintenance": + return color.New(color.FgBlue) + default: + return color.New(color.FgYellow) + } +} + func checkErr(err error) { if err != nil { fmt.Fprintln(os.Stderr, "error:", err) diff --git a/github_status_test.go b/github_status_test.go index d56e356..39c3c77 100644 --- a/github_status_test.go +++ b/github_status_test.go @@ -37,8 +37,8 @@ func TestFetch(t *testing.T) { if err != nil { t.Fatalf("fetch: %v", err) } - if got := len(s.Components); got != 3 { - t.Fatalf("got %d components, want 3", got) + if got := len(s.Components); got != 4 { + t.Fatalf("got %d components, want 4", got) } if got := s.Status.Description; got != "Partial System Outage" { t.Errorf("status description = %q", got) @@ -90,7 +90,7 @@ func TestRender(t *testing.T) { t.Fatal(err) } - want := fmt.Sprintf("%-60s%s\n%-60s%s\n%-60s%s\n", + want := fmt.Sprintf("Partial System Outage\n\n%-60s%s\n%-60s%s\n%-60s%s\n", "Git Operations", "operational", "Actions", "major_outage", "Pages", "degraded_performance") @@ -98,3 +98,22 @@ func TestRender(t *testing.T) { t.Errorf("render output mismatch\ngot:\n%s\nwant:\n%s", got, want) } } + +func TestStatusColor(t *testing.T) { + tests := []struct { + status string + want color.Attribute + }{ + {"operational", color.FgGreen}, + {"degraded_performance", color.FgYellow}, + {"partial_outage", color.FgYellow}, + {"major_outage", color.FgRed}, + {"under_maintenance", color.FgBlue}, + {"something_new", color.FgYellow}, + } + for _, tt := range tests { + if !statusColor(tt.status).Equals(color.New(tt.want)) { + t.Errorf("statusColor(%q) is not %v", tt.status, tt.want) + } + } +} diff --git a/testdata/summary.json b/testdata/summary.json index 56746c6..7140de3 100644 --- a/testdata/summary.json +++ b/testdata/summary.json @@ -7,9 +7,10 @@ "updated_at": "2026-11-14T12:00:00.000Z" }, "components": [ - {"id": "1", "name": "Git Operations", "status": "operational", "group": false, "position": 1}, - {"id": "2", "name": "Actions", "status": "major_outage", "group": false, "position": 2}, - {"id": "3", "name": "Pages", "status": "degraded_performance", "group": false, "position": 3} + {"id": "1", "name": "Git Operations", "status": "operational", "showcase": true, "group": false, "position": 1}, + {"id": "2", "name": "Actions", "status": "major_outage", "showcase": true, "group": false, "position": 2}, + {"id": "3", "name": "Visit www.githubstatus.com for more information", "status": "operational", "showcase": false, "group": false, "position": 3}, + {"id": "4", "name": "Pages", "status": "degraded_performance", "showcase": true, "group": false, "position": 4} ], "incidents": [], "scheduled_maintenances": [],