From c8f1842f2c45e7148df9af5f3c4e846e95c38a6b Mon Sep 17 00:00:00 2001 From: Michael Madden Date: Sat, 14 Nov 2026 08:23:24 -0600 Subject: [PATCH 1/2] Add -check flag to exit 1 when a component is degraded Default behavior is unchanged. With -check, github_status exits with status 1 if any component shown on the status page is not operational, so it can be used in scripts. Co-Authored-By: Claude Opus 5.5 --- TODO.md | 2 +- github_status.go | 19 +++++++++++++++++++ github_status_test.go | 22 ++++++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index f9bc1a8..e802999 100644 --- a/TODO.md +++ b/TODO.md @@ -30,7 +30,7 @@ - [x] Use `json.NewDecoder(resp.Body).Decode` instead of `io.ReadAll` + `Unmarshal` - [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 +- [x] Exit non-zero when any component isn't operational (with `-check`) ## Tests / CI diff --git a/github_status.go b/github_status.go index 7ec3a2e..b4673c7 100644 --- a/github_status.go +++ b/github_status.go @@ -6,6 +6,7 @@ package main import ( "encoding/json" + "flag" "fmt" "io" "net/http" @@ -29,9 +30,16 @@ type summary struct { const summaryURL = "https://www.githubstatus.com/api/v2/summary.json" func main() { + check := flag.Bool("check", false, "exit with status 1 if any component is not operational") + flag.Parse() + s, err := fetch(summaryURL) checkErr(err) checkErr(render(os.Stdout, s)) + + if *check && !operational(s) { + os.Exit(1) + } } // fetch retrieves and decodes the status page summary at url. @@ -76,6 +84,17 @@ func render(w io.Writer, s summary) error { return nil } +// operational reports whether every component shown on the status page is +// operational. +func operational(s summary) bool { + for _, component := range s.Components { + if component.Showcase && component.Status != "operational" { + return false + } + } + return true +} + // statusColor returns the color used to display a component status. func statusColor(status string) *color.Color { switch status { diff --git a/github_status_test.go b/github_status_test.go index 39c3c77..81fbeae 100644 --- a/github_status_test.go +++ b/github_status_test.go @@ -117,3 +117,25 @@ func TestStatusColor(t *testing.T) { } } } + +func TestOperational(t *testing.T) { + s, err := fetch(serveFixture(t).URL) + if err != nil { + t.Fatal(err) + } + if operational(s) { + t.Error("fixture has an outage, want operational false") + } + + for i := range s.Components { + s.Components[i].Status = "operational" + } + if !operational(s) { + t.Error("all components operational, want operational true") + } + + s.Components[2].Status = "major_outage" // the non-showcase filler row + if !operational(s) { + t.Error("only a non-showcase component is down, want operational true") + } +} From bb9527c5e9506897666101904bc2c4c63d4446d0 Mon Sep 17 00:00:00 2001 From: Michael Madden Date: Sat, 14 Nov 2026 08:23:44 -0600 Subject: [PATCH 2/2] Housekeeping: README, Makefile, gitignore, drop Jenkinsfile - Expand README with install, usage, -check and development notes. - Rename make release to make snapshot; it never published. - Ignore the github_status binary and goreleaser dist/. - Remove the Jenkinsfile; GitHub Actions and goreleaser cover builds. Co-Authored-By: Claude Opus 5.5 --- .gitignore | 4 ++++ Jenkinsfile | 34 --------------------------------- Makefile | 4 ++-- README.md | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++- TODO.md | 8 ++++---- 5 files changed, 64 insertions(+), 41 deletions(-) delete mode 100644 Jenkinsfile diff --git a/.gitignore b/.gitignore index f1c181e..658bb4b 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,7 @@ # Output of the go coverage tool, specifically when used with LiteIDE *.out + +# Build output +/github_status +/dist/ diff --git a/Jenkinsfile b/Jenkinsfile deleted file mode 100644 index 61db75a..0000000 --- a/Jenkinsfile +++ /dev/null @@ -1,34 +0,0 @@ -pipeline{ - agent any - options { - buildDiscarder logRotator(artifactDaysToKeepStr: '30', artifactNumToKeepStr: '2', daysToKeepStr: '30', numToKeepStr: '10') - } - stages{ - stage("checkout"){ - steps{ - git 'https://github.com/mikemadden42/github_status.git' - } - } - stage("build"){ - steps{ - sh label: '', script: 'go build -ldflags="-s -w" -v -o github_status' - } - } - stage("archive"){ - steps{ - archiveArtifacts 'github_status' - } - } - } - post{ - always{ - echo "========always========" - } - success{ - echo "========pipeline executed successfully ========" - } - failure{ - echo "========pipeline execution failed========" - } - } -} \ No newline at end of file diff --git a/Makefile b/Makefile index d69c34f..c777c9d 100644 --- a/Makefile +++ b/Makefile @@ -4,7 +4,7 @@ build: check: golangci-lint run -release: +snapshot: goreleaser release --snapshot --clean -.PHONY: build check release +.PHONY: build check snapshot diff --git a/README.md b/README.md index d54d449..3176550 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,57 @@ # github_status -GitHub Status ![example workflow](https://github.com/mikemadden42/github_status/actions/workflows/go.yml/badge.svg) + +Print the current status of each GitHub component, from +[githubstatus.com](https://www.githubstatus.com). + +## Install + +```sh +go install github.com/mikemadden42/github_status@latest +``` + +## Usage + +```sh +github_status +``` + +``` +All Systems Operational + +Git Operations operational +Webhooks operational +API Requests operational +Issues operational +Pull Requests operational +Actions operational +Packages operational +Pages operational +Copilot operational +Codespaces operational +Copilot AI Model Providers operational +``` + +Statuses are colored: green for operational, yellow for degraded performance or +partial outage, red for major outage and blue for maintenance. + +### Scripting + +With `-check`, `github_status` exits with status 1 if any component is not +operational: + +```sh +github_status -check || echo "GitHub is having problems" +``` + +It also exits with status 1 if the status page can't be reached. + +## Development + +```sh +go test ./... +make check # golangci-lint +make build # goreleaser snapshot build into dist/ +make snapshot # full goreleaser snapshot release (local only, nothing is published) +``` diff --git a/TODO.md b/TODO.md index e802999..3a32f98 100644 --- a/TODO.md +++ b/TODO.md @@ -45,7 +45,7 @@ ## Housekeeping -- [ ] Delete the Jenkinsfile unless Jenkins is in use (build-only, no tests, echo-only post blocks) -- [ ] `make release` uses `--snapshot`, so it never publishes. Rename the target or drop the flag -- [ ] Add `dist/` and `github_status` to `.gitignore` -- [ ] Expand the README with usage, install and example output +- [x] Delete the Jenkinsfile unless Jenkins is in use (build-only, no tests, echo-only post blocks) +- [x] `make release` uses `--snapshot`, so it never publishes. Renamed to `make snapshot` +- [x] Add `dist/` and `github_status` to `.gitignore` +- [x] Expand the README with usage, install and example output