From 789d1fd73d0e1bc7db81be37dbab025402ecbe9e Mon Sep 17 00:00:00 2001 From: Timothy Ehlers Date: Fri, 11 Sep 2026 13:59:18 -0500 Subject: [PATCH] fix(github-runner): report GitHub App rate limit from registration token response getGithubRunnerRegistrationToken() (classic, non-JIT registration flow) never called metricGitHubAppRateLimit, unlike its JIT sibling three lines below it in createJitConfig(). This means metrics.enable + enable_github_app_rate_limit only ever surfaced rate-limit data for the isJobQueued check and JIT config generation - never for the actual createRegistrationTokenForOrg/ForRepo call, which is the one that mints the token every non-JIT runner registration depends on. No new API calls added: the GitHub response already carries x-ratelimit-remaining/x-ratelimit-limit headers on every request, this just reads and publishes what's already there. Adds coverage in scale-up.test.ts mirroring the existing "creates a token when maximum runners has not been reached" test. Signed-off-by: Timothy Ehlers --- .../control-plane/src/scale-runners/github-runner.ts | 2 ++ .../control-plane/src/scale-runners/scale-up.test.ts | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/lambdas/functions/control-plane/src/scale-runners/github-runner.ts b/lambdas/functions/control-plane/src/scale-runners/github-runner.ts index f968fa3008..e957ef6f88 100644 --- a/lambdas/functions/control-plane/src/scale-runners/github-runner.ts +++ b/lambdas/functions/control-plane/src/scale-runners/github-runner.ts @@ -69,6 +69,8 @@ async function getGithubRunnerRegistrationToken(githubRunnerConfig: CreateGitHub repo: githubRunnerConfig.runnerOwner.split('/')[1], }); + metricGitHubAppRateLimit(registrationToken.headers, githubRunnerConfig.appIndex); + return registrationToken.data.token; } diff --git a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts index a8ef79c3a9..f83a984ec2 100644 --- a/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts +++ b/lambdas/functions/control-plane/src/scale-runners/scale-up.test.ts @@ -3,6 +3,7 @@ import { performance } from 'perf_hooks'; import { controlPlaneProviderRegistry } from '../control-plane-providers'; import * as ghAuth from '../github/auth'; +import * as rateLimitModule from '../github/rate-limit'; import { createStartRunnerConfig } from './github-runner'; import { publishRetryMessage } from './job-retry'; import * as scaleUpModule from './scale-up'; @@ -340,6 +341,13 @@ describe('scaleUp with GHES', () => { expect(mockOctokit.actions.createRegistrationTokenForRepo).not.toBeCalled(); }); + it('reports the GitHub App rate limit from the registration token response', async () => { + process.env.ENABLE_EPHEMERAL_RUNNERS = 'false'; + const metricSpy = vi.spyOn(rateLimitModule, 'metricGitHubAppRateLimit'); + await scaleUpModule.scaleUp(TEST_DATA); + expect(metricSpy).toHaveBeenCalledWith({ 'x-ratelimit-remaining': '4999', 'x-ratelimit-limit': '5000' }, 0); + }); + it('creates a runner with labels in a specific group', async () => { process.env.RUNNER_LABELS = 'label1,label2'; process.env.RUNNER_GROUP_NAME = 'TEST_GROUP'; @@ -2331,6 +2339,10 @@ function defaultOctokitMockImpl() { data: { token: '1234abcd', }, + headers: { + 'x-ratelimit-remaining': '4999', + 'x-ratelimit-limit': '5000', + }, }; const mockInstallationIdReturnValueOrgs = { data: {