Fix github query to grab all contributions

This commit is contained in:
Alex Selimov 2026-05-13 06:54:12 -04:00
parent 620121b505
commit 53084c4e6f

View file

@ -11,19 +11,27 @@ const config = {
forgejoURL: requireEnv("FJ_URL"), forgejoURL: requireEnv("FJ_URL"),
}; };
const now = new Date();
const oneYearAgo = new Date(); const oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1); oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
const formattedOneYearAgo = oneYearAgo.toISOString().replace("Z", "+00:00"); const formattedOneYearAgo = oneYearAgo.toISOString().replace("Z", "+00:00");
const ghQuery = ` function buildGhQuery(from: string, to: string): string {
{ return `{
user(login: "aselimov") { viewer {
contributionsCollection { contributionsCollection(from: "${from}", to: "${to}") {
contributionCalendar {
weeks {
contributionDays {
date
contributionCount
}
}
}
commitContributionsByRepository(maxRepositories: 100) { commitContributionsByRepository(maxRepositories: 100) {
repository { repository {
nameWithOwner nameWithOwner
isMirror isMirror
isPrivate
} }
contributions(first: 100) { contributions(first: 100) {
nodes { nodes {
@ -34,8 +42,8 @@ const ghQuery = `
} }
} }
} }
}`;
} }
`;
type DayContributions = { type DayContributions = {
[repo: string]: number; [repo: string]: number;
@ -126,25 +134,33 @@ function deduplicateAcrossPlatforms(
function parseGitHubContributions(data: any): ActivityMap { function parseGitHubContributions(data: any): ActivityMap {
const result: ActivityMap = {}; const result: ActivityMap = {};
const repos = const collection = data?.data?.viewer?.contributionsCollection;
data?.data?.user?.contributionsCollection
?.commitContributionsByRepository ?? [];
for (const repoEntry of repos) { const calendarMap: { [day: string]: number } = {};
for (const week of collection?.contributionCalendar?.weeks ?? []) {
for (const day of week.contributionDays) {
if (day.contributionCount > 0) calendarMap[day.date] = day.contributionCount;
}
}
for (const repoEntry of collection?.commitContributionsByRepository ?? []) {
const repoName: string = repoEntry.repository.nameWithOwner; const repoName: string = repoEntry.repository.nameWithOwner;
const isMirror: boolean = repoEntry.repository.isMirror; if (repoEntry.repository.isMirror) continue;
const isPrivate: boolean = repoEntry.repository.isPrivate;
if (isMirror) continue;
const displayName = isPrivate ? "private" : repoName;
for (const contribution of repoEntry.contributions.nodes) { for (const contribution of repoEntry.contributions.nodes) {
const day: string = contribution.occurredAt.split("T")[0]; const day: string = contribution.occurredAt.split("T")[0];
const count: number = contribution.commitCount; const count: number = contribution.commitCount;
if (!result[day]) result[day] = {}; if (!result[day]) result[day] = {};
result[day]![displayName] = (result[day]![displayName] ?? 0) + count; result[day]![repoName] = (result[day]![repoName] ?? 0) + count;
}
}
for (const [day, total] of Object.entries(calendarMap)) {
const known = Object.values(result[day] ?? {}).reduce((a, b) => a + b, 0);
const hidden = total - known;
if (hidden > 0) {
if (!result[day]) result[day] = {};
result[day]!["private"] = (result[day]!["private"] ?? 0) + hidden;
} }
} }
@ -215,25 +231,49 @@ async function getForgejoActivityMap(): Promise<ActivityMap> {
return map; return map;
} }
async function makeActivityMap(): Promise<ActivityMap> { async function ghFetch(query: string): Promise<any> {
const ghResponse = await fetch("https://api.github.com/graphql", { const response = await fetch("https://api.github.com/graphql", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${config.githubApiKey}`, Authorization: `Bearer ${config.githubApiKey}`,
}, },
body: JSON.stringify({ query: ghQuery }), body: JSON.stringify({ query }),
}); });
if (!response.ok) {
if (!ghResponse.ok) {
throw new Error( throw new Error(
`GitHub API error: ${ghResponse.status} ${ghResponse.statusText}`, `GitHub API error: ${response.status} ${response.statusText}`,
); );
} }
return response.json();
}
const ghData = await ghResponse.json(); async function getGitHubActivityMap(): Promise<ActivityMap> {
const ghMap = parseGitHubContributions(ghData); const ranges = Array.from({ length: 12 }, (_, i) => {
const fjMap = await getForgejoActivityMap(); const to = new Date(now);
to.setMonth(to.getMonth() - i);
const from = new Date(to);
from.setMonth(from.getMonth() - 1);
return { from: from.toISOString(), to: to.toISOString() };
});
const results = await Promise.all(
ranges.map(({ from, to }) => ghFetch(buildGhQuery(from, to))),
);
const map: ActivityMap = {};
for (const data of results) {
mergeInto(map, parseGitHubContributions(data));
}
return map;
}
async function makeActivityMap(): Promise<ActivityMap> {
const [ghMap, fjMap] = await Promise.all([
getGitHubActivityMap(),
getForgejoActivityMap(),
]);
return deduplicateAcrossPlatforms(ghMap, fjMap); return deduplicateAcrossPlatforms(ghMap, fjMap);
} }