mirror of
https://github.com/block/buzz.git
synced 2026-11-08 10:21:46 +00:00
Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
102 lines
3.1 KiB
TypeScript
102 lines
3.1 KiB
TypeScript
import { useQuery } from "@tanstack/react-query";
|
|
import * as React from "react";
|
|
|
|
import {
|
|
getProjectLocalRepoSnapshot,
|
|
getProjectRepoSnapshot,
|
|
} from "@/shared/api/projectGit";
|
|
import type { ProjectRepoSnapshot } from "@/shared/api/types";
|
|
import type { Project } from "./hooks";
|
|
|
|
// Remote snapshots are backed by a blobless `git clone` per repository, so the
|
|
// overview scan is deliberately throttled and cached for a long time.
|
|
const OVERVIEW_SNAPSHOT_CONCURRENCY = 3;
|
|
|
|
function snapshotHasData(snapshot: ProjectRepoSnapshot | null | undefined) {
|
|
return Boolean(
|
|
snapshot && (snapshot.files.length > 0 || snapshot.latestCommit),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Local checkouts are instant (no network, no clone) and keep working when
|
|
* the relay's git storage is empty or unreachable, so they are preferred.
|
|
* Only repositories without usable local data fall back to a remote clone.
|
|
*/
|
|
async function fetchProjectSnapshot(
|
|
project: Project,
|
|
reposDir: string | null | undefined,
|
|
): Promise<ProjectRepoSnapshot | null> {
|
|
try {
|
|
const local = await getProjectLocalRepoSnapshot({
|
|
reposDir,
|
|
projectDtag: project.dtag,
|
|
cloneUrl: project.cloneUrls[0] ?? null,
|
|
defaultBranch: project.defaultBranch,
|
|
baseBranch: project.defaultBranch,
|
|
});
|
|
if (snapshotHasData(local?.snapshot)) return local?.snapshot ?? null;
|
|
} catch {
|
|
// Best-effort: fall through to the remote snapshot.
|
|
}
|
|
|
|
const cloneUrl = project.cloneUrls[0];
|
|
if (!cloneUrl) return null;
|
|
return getProjectRepoSnapshot({
|
|
cloneUrl,
|
|
defaultBranch: project.defaultBranch,
|
|
baseBranch: project.defaultBranch,
|
|
});
|
|
}
|
|
|
|
async function fetchProjectsRepoSnapshots(
|
|
projects: Project[],
|
|
reposDir: string | null | undefined,
|
|
): Promise<Record<string, ProjectRepoSnapshot>> {
|
|
const snapshots: Record<string, ProjectRepoSnapshot> = {};
|
|
const queue = [...projects];
|
|
|
|
const workers = Array.from(
|
|
{ length: Math.min(OVERVIEW_SNAPSHOT_CONCURRENCY, queue.length) },
|
|
async () => {
|
|
for (;;) {
|
|
const project = queue.shift();
|
|
if (!project) return;
|
|
try {
|
|
const snapshot = await fetchProjectSnapshot(project, reposDir);
|
|
if (snapshot) snapshots[project.id] = snapshot;
|
|
} catch {
|
|
// Best-effort: unreachable or empty repositories are skipped.
|
|
}
|
|
}
|
|
},
|
|
);
|
|
|
|
await Promise.all(workers);
|
|
return snapshots;
|
|
}
|
|
|
|
/**
|
|
* Fetches repo snapshots for a set of projects (throttled, failure-tolerant)
|
|
* for community-wide aggregates like the overview language breakdown.
|
|
* Prefers local checkouts under `reposDir`; falls back to remote clones.
|
|
* Callers should pre-filter and cap `projects` — up to one git clone per entry.
|
|
*/
|
|
export function useProjectsRepoSnapshotsQuery(
|
|
projects: Project[],
|
|
reposDir?: string | null,
|
|
) {
|
|
const projectIds = React.useMemo(
|
|
() => projects.map((project) => project.id).sort(),
|
|
[projects],
|
|
);
|
|
|
|
return useQuery({
|
|
enabled: projects.length > 0,
|
|
queryKey: ["projects", "repo-snapshots", reposDir ?? "default", projectIds],
|
|
queryFn: () => fetchProjectsRepoSnapshots(projects, reposDir),
|
|
staleTime: 15 * 60_000,
|
|
retry: 0,
|
|
});
|
|
}
|