mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-10-19 22:27:20 +02:00
fix: handle production source failures and reduce ZIP work
This commit is contained in:
1 parent
bd7d6f0bef
commit
705f7c428a
16 files changed
+612
-86
No files matched your search
@@ -0,0 +1,65 @@
|
||||
import { createHash } from "crypto";
|
||||
import AnonymousError from "../AnonymousError";
|
||||
import { ExpiringMap } from "../expiring-map";
|
||||
import { octokit, waitForTokenGate, RateLimitDelayError } from "../GitHubUtils";
|
||||
import { GitHubBaseData } from "./GitHubBase";
|
||||
|
||||
type Miss = "file_not_found" | "repo_not_found" | "commit_not_found";
|
||||
const results = new ExpiringMap<Miss>(2048);
|
||||
const pending = new Map<string, Promise<Miss>>();
|
||||
|
||||
function missKey(data: GitHubBaseData, token: string): string {
|
||||
return createHash("sha256").update(JSON.stringify([
|
||||
data.repoId, data.organization, data.repoName, data.commit, data.cacheGeneration, data.cacheRevision, token,
|
||||
])).digest("hex");
|
||||
}
|
||||
|
||||
export function cachedContentMiss(data: GitHubBaseData, token: string): Miss | undefined {
|
||||
return results.get(missKey(data, token));
|
||||
}
|
||||
|
||||
/** A raw 404 can mean a missing file, lost repository access, or a removed revision. */
|
||||
export async function classifyContentMiss(data: GitHubBaseData, token: string): Promise<Miss> {
|
||||
const key = missKey(data, token);
|
||||
const cached = results.get(key);
|
||||
if (cached) return cached;
|
||||
const active = pending.get(key);
|
||||
if (active) return active;
|
||||
if (pending.size >= 64) throw new AnonymousError("cache_busy", { httpStatus: 503 });
|
||||
const probe = (async (): Promise<Miss> => {
|
||||
const oct = octokit(token);
|
||||
const options = { owner: data.organization, repo: data.repoName, request: { timeout: 15_000 } };
|
||||
try {
|
||||
await waitForTokenGate(token);
|
||||
await oct.repos.get(options);
|
||||
} catch (error) {
|
||||
if ((error as { status?: number }).status === 404) return "repo_not_found";
|
||||
throw probeError(error);
|
||||
}
|
||||
try {
|
||||
await waitForTokenGate(token);
|
||||
// Only check that the revision resolves; do not download a potentially
|
||||
// large commit diff just to classify a missing file.
|
||||
await oct.repos.getCommit({ ...options, ref: data.commit || "HEAD", mediaType: { format: "sha" } });
|
||||
} catch (error) {
|
||||
const upstream = error as { status?: number; response?: { data?: { message?: string } } };
|
||||
if (upstream.status === 404 || upstream.status === 409 || (upstream.status === 422 &&
|
||||
/^No commit found for SHA\b/.test(upstream.response?.data?.message || ""))) return "commit_not_found";
|
||||
throw probeError(error);
|
||||
}
|
||||
return "file_not_found";
|
||||
})().then(result => { results.set(key, result, 30_000); return result; });
|
||||
pending.set(key, probe);
|
||||
try { return await probe; }
|
||||
finally { if (pending.get(key) === probe) pending.delete(key); }
|
||||
}
|
||||
|
||||
function probeError(error: unknown): AnonymousError {
|
||||
if (error instanceof AnonymousError) return error;
|
||||
if (error instanceof RateLimitDelayError) return new AnonymousError("github_rate_limit_exceeded", { httpStatus: 429, cause: error });
|
||||
const status = (error as { status?: number }).status;
|
||||
return new AnonymousError(status === 401 ? "token_expired" : status === 403 ? "repo_not_accessible" : "github_unavailable", {
|
||||
httpStatus: status === 401 || status === 403 ? status : 502,
|
||||
cause: error as Error,
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user