Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/api/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3106,7 +3106,13 @@ export function createApp() {
if (!isRagEnabled(c.env)) return c.json({ error: "not_found" }, 404);
const body = (await c.req.json().catch(() => ({}))) as { repoFullName?: unknown };
const repoFullName = typeof body?.repoFullName === "string" && body.repoFullName.trim().length > 0 ? body.repoFullName.trim() : undefined;
const message: JobMessage = { type: "rag-index-repo", requestedBy: "api", ...(repoFullName ? { repoFullName } : {}) };
const repo = repoFullName ? await getRepository(c.env, repoFullName) : null;
const message: JobMessage = {
type: "rag-index-repo",
requestedBy: "api",
...(repoFullName ? { repoFullName } : {}),
...(typeof repo?.installationId === "number" ? { installationId: repo.installationId } : {}),
};
await c.env.JOBS.send(message);
return c.json({ ok: true, status: "queued", scope: repoFullName ?? "all-configured-repos" }, 202);
});
Expand Down Expand Up @@ -3139,10 +3145,12 @@ export function createApp() {
const segment = parseBackfillSegment(body?.segment);
if (!segment) return c.json({ error: "valid_segment_required" }, 400);
const mode = body?.mode === "full" || body?.mode === "resume" ? body.mode : "light";
const repo = await getRepository(c.env, body.repoFullName);
const message: JobMessage = {
type: "backfill-repo-segment",
requestedBy: "api",
repoFullName: body.repoFullName,
...(typeof repo?.installationId === "number" ? { installationId: repo.installationId } : {}),
segment,
mode,
force: body?.force === true,
Expand Down Expand Up @@ -3174,10 +3182,12 @@ export function createApp() {
const body = await c.req.json().catch(() => ({}));
if (typeof body?.repoFullName !== "string" || body.repoFullName.length === 0) return c.json({ error: "repo_full_name_required" }, 400);
const mode = body?.mode === "full" || body?.mode === "resume" ? body.mode : "light";
const repo = await getRepository(c.env, body.repoFullName);
const message: JobMessage = {
type: "backfill-pr-details",
requestedBy: "api",
repoFullName: body.repoFullName,
...(typeof repo?.installationId === "number" ? { installationId: repo.installationId } : {}),
mode,
...(Number.isFinite(Number(body?.cursor)) ? { cursor: Number(body.cursor) } : {}),
};
Expand Down
298 changes: 238 additions & 60 deletions src/github/backfill.ts

Large diffs are not rendered by default.

32 changes: 29 additions & 3 deletions src/github/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,26 @@ export function githubRateLimitAdmissionKeyForInstallation(installationId: numbe
return `installation:${Math.trunc(installationId)}`;
}

export function githubRateLimitAdmissionKeyForPublicToken(): GitHubRateLimitAdmissionKey {
return "public-token";
}

/** The SINGLE token→admission-key resolver, so every GitHub read attributes consistently and a token can never
* travel without its matching key: the public bucket for the shared public token, the installation bucket for an
* installation token with a known installation id, else undefined (unattributed). Callers pass whichever token
* they will actually read with, so the key is always derived from the SAME token and cannot drift apart from it. */
export function githubRateLimitAdmissionKeyForToken(
env: { GITHUB_PUBLIC_TOKEN?: string },
token: string | undefined,
installationId: number | null | undefined,
): GitHubRateLimitAdmissionKey | undefined {
if (!token) return undefined;
if (token === env.GITHUB_PUBLIC_TOKEN) return githubRateLimitAdmissionKeyForPublicToken();
return typeof installationId === "number" && Number.isFinite(installationId)
? githubRateLimitAdmissionKeyForInstallation(installationId)
: undefined;
}

/** Only cache explicitly stable GitHub REST reads. PR/issue/comment/label/event/check/status reads are mutable
* review inputs and must always reflect the current GitHub state. Exported for tests. */
export function isCacheableGithubUrl(url: string): boolean {
Expand Down Expand Up @@ -128,9 +148,15 @@ function recordGitHubCacheMetric(result: "hit" | "miss" | "set" | "coalesced" |
incr(GITHUB_RESPONSE_CACHE_METRIC, { result, class: cls });
}

function githubAdmissionKeyScope(admissionKey: GitHubRateLimitAdmissionKey | null | undefined): "installation" | "global" | "other" {
if (!admissionKey) return "global";
return admissionKey.startsWith("installation:") ? "installation" : "other";
// Keep this classification identical to selfhost/queue-common's githubRateLimitAdmissionKeyScope so both metric
// surfaces label a given admission key the same way (installation / public / global / unknown / other). Exported so
// the classification is unit-tested directly (mirroring the queue-common helper's test), not only via rendered metrics.
export function githubAdmissionKeyScope(admissionKey: GitHubRateLimitAdmissionKey | null | undefined): "installation" | "public" | "global" | "unknown" | "other" {
if (!admissionKey) return "unknown";
if (admissionKey.startsWith("installation:")) return "installation";
if (admissionKey === githubRateLimitAdmissionKeyForPublicToken()) return "public";
if (admissionKey.startsWith("global:")) return "global";
return "other";
}

function restRemainingBucket(remaining: number): "0" | "1-75" | "76-150" | "151+" {
Expand Down
4 changes: 3 additions & 1 deletion src/github/pr-freshness.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createInstallationToken } from "./app";
import { fetchLivePullRequest } from "./backfill";
import { githubRateLimitAdmissionKeyForToken } from "./client";
import type { GitHubPullRequestPayload } from "../types";

export type PullRequestFreshness =
Expand Down Expand Up @@ -66,7 +67,8 @@ export async function fetchPullRequestFreshness(
(await createInstallationToken(env, args.installationId).catch(() => undefined)) ??
env.GITHUB_PUBLIC_TOKEN;
if (!token) return classifyPullRequestFreshness(undefined, args.expectedHeadSha);
const live = await fetchLivePullRequest(env, args.repoFullName, args.pullNumber, token);
const admissionKey = githubRateLimitAdmissionKeyForToken(env, token, args.installationId);
const live = await fetchLivePullRequest(env, args.repoFullName, args.pullNumber, token, admissionKey);
return classifyPullRequestFreshness(live, args.expectedHeadSha);
}

Expand Down
Loading
Loading