Skip to content

feat(budget): warn before the org runs out of Actions minutes - #128

Merged
ExtraToast merged 1 commit into
mainfrom
feat/actions-budget-watchdog
Aug 29, 2026
Merged

feat(budget): warn before the org runs out of Actions minutes#128
ExtraToast merged 1 commit into
mainfrom
feat/actions-budget-watchdog

Conversation

@ExtraToast

Copy link
Copy Markdown
Contributor

Why

The org ran out of Actions minutes mid-working-session with no warning at all. Every private repo went dark — runs created, no runner allocated, failing within seconds with zero steps executed — and the first sign of it was workflows failing for reasons that looked like broken code. Nothing watched the spend.

What it found immediately

Run against live billing data:

3159 of 3000 included minutes used (105%) by private repos in August 2026

| repo                  | minutes |
| fleet-infra           |    2313 |
| workspace             |     483 |
| nix-config            |     267 |
| homelab-collections   |      55 |
| homelab-inventory      |      39 |
| demo-repository       |       2 |

The org is already past its allowance for August — which is why a monetary budget was needed. Note this is higher than the ~2,975 I reported on the 27th: that figure came from a top-N repo list and missed three private repos.

Three things that would otherwise make it report nonsense

  • The classic endpoint is gone. /orgs/{org}/settings/billing/actions returns 410. The replacement needs no admin:org scope, only an ordinary token — confirmed.
  • Public usage is discounted, not absent. It appears in the same response, so counting it overstates consumption several times over. Repo visibility is resolved and public excluded (10 private of 33).
  • Included minutes carry a runner multiplier. A macOS minute costs ten, Windows two. Raw minutes would under-report a macOS-heavy month tenfold. Unrecognised minute SKUs warn rather than being assumed 1x — silently under-counting is precisely the failure that lets a cap arrive unannounced. Actions storage is excluded outright; it is billed in GigabyteHours, not minutes.

Verified that the alarm fires, not just that it stays quiet

allowance usage branch exit
10000 32% notice only 0
4200 75% ::warning:: 0
3000 (real) 105% ::error:: 1

Critical fails the run so it is visibly red even if nobody reads issues. Warning stays green — it is information, not an incident. The alert issue is sticky, updated in place rather than reopened each run, same as the hygiene sweep's comment.

One thing a human still needs to confirm

My weighted sum is 3,159. That is the billing API summed correctly as far as I can verify — storage excluded, public excluded, multipliers applied — but whether GitHub's own included-minutes accounting agrees to the minute is worth checking against the billing UI once. If it disagrees materially, the multiplier mapping is the first thing to suspect. Noted on #126.

Caveat on cadence

Its caller will be a scheduled workflow, and GitHub throttles schedules hard in this org — measured at 4–7 runs per repo per day regardless of declared frequency (JorisJonkers-dev/.github#42). For a budget check that is fine: a handful of checks a day is ample, unlike issue boarding. But it cannot promise to warn within minutes of a threshold being crossed, and should not be described as if it could.

Refs #126

This estate ran out mid-working-session with no warning at all. Every private
repo went dark -- runs created, no runner allocated, failing within seconds
with zero steps executed -- and the first sign of it was workflows failing for
reasons that looked like broken code.

Nothing watched the spend. This does: it reads the org's Actions consumption
for the current month, compares it against the plan's included minutes, and
warns while there is still room to act.

Three things it has to get right, each of which would otherwise produce a
watchdog that reads plausibly and reports nonsense:

  - The classic /orgs/{org}/settings/billing/actions endpoint returns 410 Gone.
    The replacement needs no admin:org scope, only an ordinary token.
  - The response covers public and private repos together, and public usage is
    fully discounted rather than absent. Counting it overstates consumption
    several times over, so repo visibility is resolved and public excluded.
  - Included minutes are consumed at a per-runner multiplier, not one minute
    per minute: a macOS minute costs ten, Windows two. Raw minutes would
    under-report a macOS-heavy month by an order of magnitude. Unrecognised
    minute SKUs warn rather than being assumed 1x, because silently
    under-counting is exactly the failure that lets a cap arrive unannounced.
    'Actions storage' is excluded outright; it is billed in GigabyteHours.

Critical fails the run so it is visibly red even if nobody reads issues;
warning stays green because it is information, not an incident. The alert
issue is sticky -- updated in place rather than reopened every run -- for the
same reason the hygiene sweep's comment is.

Verified against live billing data, including that the alarm fires rather than
only that it stays quiet:
  - allowance 10000 -> 32%, notice only, exit 0
  - allowance 4200  -> 75%, ::warning::,  exit 0
  - allowance 3000  -> 105%, ::error::,   exit 1

That 105% is real: private repos have used 3,159 of 3,000 included minutes for
August 2026, so the org is currently past its allowance.

Refs #126
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant