Skip to content

Commit 5cd9423

Browse files
committed
feat(env/github-status): probeGitHubStatus() + registry pin bump for github-status-check action
Two new things: 1. src/env/github-status.ts — probeGitHubStatus(timeoutMs?) queries githubstatus.com/api/v2/components.json and returns the aggregate health of Actions, Git Operations, and API Requests. Fails open (status: 'unknown') when the probe itself fails. Exported at @socketsecurity/lib/env/github-status. Useful in release scripts and pre-flight checks that make GitHub API calls. 2. ci/weekly-update/provenance workflows bumped to socket-registry @a97bb3b0 which includes the new github-status-check composite action — every CI job now emits a warning annotation when any monitored GitHub component is degraded, so operators correlate 'why did my workflow fail?' with upstream outages instead of blaming their code.
1 parent 16c02ea commit 5cd9423

6 files changed

Lines changed: 140 additions & 7 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ concurrency:
2121
jobs:
2222
ci:
2323
name: Run CI Pipeline
24-
uses: SocketDev/socket-registry/.github/workflows/ci.yml@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
24+
uses: SocketDev/socket-registry/.github/workflows/ci.yml@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
2525
with:
2626
test-script: 'pnpm run build && pnpm exec vitest --config .config/repo/vitest.config.mts run'
2727
# Pass the Socket API token so the install step runs sfw-enterprise,
@@ -37,7 +37,7 @@ jobs:
3737
runs-on: ubuntu-latest
3838
timeout-minutes: 10
3939
steps:
40-
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
40+
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
4141
with:
4242
socket-api-token: ${{ secrets.SOCKET_API_TOKEN }}
4343
- name: Build project

.github/workflows/provenance.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ jobs:
4747
# Needed for npm provenance via OIDC trusted publishing.
4848
id-token: write
4949
steps:
50-
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
50+
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
5151
with:
5252
socket-api-token: ${{ secrets.SOCKET_API_TOKEN || secrets.SOCKET_API_KEY }}
5353

.github/workflows/weekly-update.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ jobs:
2929
outputs:
3030
has-updates: ${{ steps.check.outputs.has-updates }}
3131
steps:
32-
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
32+
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
3333
- name: Check for npm updates
3434
id: check
3535
shell: bash
@@ -53,7 +53,7 @@ jobs:
5353
contents: write
5454
pull-requests: write
5555
steps:
56-
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
56+
- uses: SocketDev/socket-registry/.github/actions/setup-and-install@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
5757
- name: Create update branch
5858
id: branch
5959
env:
@@ -64,7 +64,7 @@ jobs:
6464
git checkout -b "$BRANCH_NAME"
6565
echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
6666
67-
- uses: SocketDev/socket-registry/.github/actions/setup-git-signing@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
67+
- uses: SocketDev/socket-registry/.github/actions/setup-git-signing@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
6868
with:
6969
gpg-private-key: ${{ secrets.BOT_GPG_PRIVATE_KEY }}
7070

@@ -336,7 +336,7 @@ jobs:
336336
test-output.log
337337
retention-days: 7
338338

339-
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@0917ae539233e73930a1dc5c271cc7954c684fae # main (2026-06-02)
339+
- uses: SocketDev/socket-registry/.github/actions/cleanup-git-signing@a97bb3b09ae5f36e2ae0e7ec3dc00ed5bdc0af23 # main (2026-06-02)
340340
if: always()
341341

342342
notify:

docs/api-index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ Each entry links to the source module and shows the first sentence of its `@file
251251
| [`@socketsecurity/lib-stable/env/ci`](../src/env/ci.ts) | _(no description)_ |
252252
| [`@socketsecurity/lib-stable/env/debug`](../src/env/debug.ts) | _(no description)_ |
253253
| [`@socketsecurity/lib-stable/env/github`](../src/env/github.ts) | _(no description)_ |
254+
| [`@socketsecurity/lib-stable/env/github-status`](../src/env/github-status.ts) | _(no description)_ |
254255
| [`@socketsecurity/lib-stable/env/home`](../src/env/home.ts) | _(no description)_ |
255256
| [`@socketsecurity/lib-stable/env/locale`](../src/env/locale.ts) | _(no description)_ |
256257
| [`@socketsecurity/lib-stable/env/node-auth-token`](../src/env/node-auth-token.ts) | _(no description)_ |

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,11 @@
815815
"types": "./dist/env/github.d.ts",
816816
"default": "./dist/env/github.js"
817817
},
818+
"./env/github-status": {
819+
"source": "./src/env/github-status.ts",
820+
"types": "./dist/env/github-status.d.ts",
821+
"default": "./dist/env/github-status.js"
822+
},
818823
"./env/home": {
819824
"source": "./src/env/home.ts",
820825
"types": "./dist/env/home.d.ts",

src/env/github-status.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
/**
2+
* @file Probe githubstatus.com to detect platform degradation before making
3+
* GitHub API calls. Useful in scripts, release tooling, and CI pre-flight
4+
* checks where a cryptic "operation was canceled" error would otherwise mask
5+
* an upstream GitHub outage.
6+
*
7+
* Component IDs are stable GitHub-assigned identifiers from
8+
* githubstatus.com/api/v2/components.json. The probe adds at most 8 seconds
9+
* to startup (configurable timeout) and fails open on network error so a
10+
* down status page never blocks a healthy workflow.
11+
*/
12+
13+
// oxlint-disable-next-line socket/no-platform-specific-http-import -- server-only module; node platform is intentional.
14+
import { httpJson } from '../http-request/node'
15+
16+
export type GitHubComponentStatus =
17+
| 'degraded_performance'
18+
| 'major_outage'
19+
| 'operational'
20+
| 'partial_outage'
21+
| 'under_maintenance'
22+
23+
export type GitHubStatusResult = {
24+
/** Worst-case status across all monitored components. */
25+
status: GitHubComponentStatus | 'unknown'
26+
/** Whether any monitored component is not fully operational. */
27+
degraded: boolean
28+
/** Human-readable summary, e.g. "Actions: degraded_performance". */
29+
summary: string
30+
/** Per-component breakdown for the monitored set. */
31+
components: Array<{
32+
id: string
33+
name: string
34+
status: GitHubComponentStatus
35+
}>
36+
}
37+
38+
// Component IDs that matter for CI / GitHub API call workflows.
39+
// Stable identifiers from githubstatus.com; the names are display-only.
40+
const MONITORED_COMPONENT_IDS: ReadonlyMap<string, string> = new Map([
41+
['br0l2tvcx85d', 'Actions'],
42+
['8l4ygp009s5s', 'Git Operations'],
43+
['brv1bkgrwx7q', 'API Requests'],
44+
])
45+
46+
const SEVERITY: ReadonlyMap<string, number> = new Map([
47+
['major_outage', 4],
48+
['partial_outage', 3],
49+
['degraded_performance', 2],
50+
['under_maintenance', 1],
51+
['operational', 0],
52+
])
53+
54+
const STATUS_API_URL = 'https://www.githubstatus.com/api/v2/components.json'
55+
56+
/**
57+
* Probe githubstatus.com and return the health of GitHub Actions, Git
58+
* Operations, and API Requests. Fails open (returns `{ status: 'unknown' }`)
59+
* when the probe itself fails — so a down status page never blocks a healthy
60+
* workflow.
61+
*
62+
* @param timeoutMs - Maximum milliseconds to wait for the probe. Default 8000.
63+
*
64+
* @example
65+
* ;```typescript
66+
* import { probeGitHubStatus } from '@socketsecurity/lib/env/github-status'
67+
*
68+
* const health = await probeGitHubStatus()
69+
* if (health.degraded) {
70+
* console.warn(`GitHub degraded: ${health.summary}`)
71+
* }
72+
* ```
73+
*/
74+
export async function probeGitHubStatus(
75+
timeoutMs = 8000,
76+
): Promise<GitHubStatusResult> {
77+
let body: unknown
78+
try {
79+
body = await httpJson(STATUS_API_URL, {
80+
timeout: timeoutMs,
81+
})
82+
} catch {
83+
return {
84+
status: 'unknown',
85+
degraded: false,
86+
summary: 'githubstatus.com unreachable — cannot confirm GitHub health',
87+
components: [],
88+
}
89+
}
90+
91+
const raw = body as {
92+
components?: Array<{ id: string; name: string; status: string }>
93+
}
94+
const allComponents = raw.components ?? []
95+
96+
const monitored: GitHubStatusResult['components'] = []
97+
let worstSeverity = 0
98+
let worstStatus: GitHubComponentStatus = 'operational'
99+
100+
for (const c of allComponents) {
101+
const name = MONITORED_COMPONENT_IDS.get(c.id)
102+
if (!name) {
103+
continue
104+
}
105+
const status = c.status as GitHubComponentStatus
106+
const sev = SEVERITY.get(status) ?? 0
107+
if (sev > worstSeverity) {
108+
worstSeverity = sev
109+
worstStatus = status
110+
}
111+
monitored.push({ id: c.id, name, status })
112+
}
113+
114+
const degradedComponents = monitored.filter(c => c.status !== 'operational')
115+
const degraded = degradedComponents.length > 0
116+
const summary =
117+
degraded
118+
? degradedComponents.map(c => `${c.name}: ${c.status}`).join(', ')
119+
: 'All monitored GitHub components operational'
120+
121+
return {
122+
status: worstStatus,
123+
degraded,
124+
summary,
125+
components: monitored,
126+
}
127+
}

0 commit comments

Comments
 (0)