From 8a8611594dbf5b06ecec866559ad32e5eefe6125 Mon Sep 17 00:00:00 2001 From: Joris Wouter Jonkers Date: Sat, 29 Aug 2026 23:07:10 +0200 Subject: [PATCH] fix(budget): request the billing permission and say so when it is missing The first real run reached the billing endpoint and got 403 "Resource not accessible by integration", while the same App token happily listed all 33 repos in the org. Billing sits behind its own App permission -- "Organization plan", read-only -- which the token was never asking for. Requesting it here is necessary but not sufficient: the App must also have been granted that permission and the updated installation permissions accepted, which is an owner action rather than something a workflow can do. So the failure message now names the actual cause and the remedy instead of the vague "endpoint or token scope may have changed", and points out the diagnostic that gives it away: repo listing succeeding while billing 403s is precisely this and nothing else. It also records that the classic billing endpoint is gone (410), so nobody re-diagnoses that from scratch. The watchdog still fails loud rather than reporting zero when it cannot read the budget. A budget watchdog that quietly reports 0% because it lost access is worse than an absent one: it looks like good news. Refs #126 --- .github/workflows/actions-budget-watchdog.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/actions-budget-watchdog.yml b/.github/workflows/actions-budget-watchdog.yml index 678430a..1506570 100644 --- a/.github/workflows/actions-budget-watchdog.yml +++ b/.github/workflows/actions-budget-watchdog.yml @@ -69,6 +69,13 @@ name: 'Actions Budget Watchdog' 'owner': '${{ github.repository_owner }}' 'permission-metadata': 'read' 'permission-issues': 'write' + # Billing lives behind its own App permission ("Organization plan", + # read-only). Without it the usage endpoint answers 403 "Resource not + # accessible by integration" even though the same token can list + # every repo in the org -- requesting it here is necessary but not + # sufficient: the App must also have been granted it, and the updated + # installation permissions accepted. + 'permission-organization-plan': 'read' - 'name': 'Check the Actions minute budget' 'env': @@ -144,7 +151,17 @@ name: 'Actions Budget Watchdog' f'/organizations/{owner}/settings/billing/usage?year={now.year}&month={now.month}', default=None) if usage is None or 'usageItems' not in usage: - print('::error::could not read billing usage; the endpoint or token scope may have changed') + # Fail loud rather than reporting zero. A budget watchdog that + # quietly reports 0% when it cannot read the budget is worse than + # one that is absent, because it looks like good news. + print('::error::could not read the org billing usage endpoint.') + print('::error::A 403 "Resource not accessible by integration" here means the ' + 'App lacks the "Organization plan" (read) permission: request it on the ' + 'App, then accept the updated installation permissions. Repo listing ' + 'working while this fails is exactly that case.') + print('::error::Otherwise supply BUDGET_TOKEN -- a token with org billing read ' + 'access. Note the classic /orgs/{org}/settings/billing/actions endpoint is ' + 'gone (410); this uses the enhanced billing endpoint.') sys.exit(1) per_repo = {}