Huge speed improvements to commit fetching

This commit is contained in:
austinkelsay 2024-09-21 13:49:29 -05:00
parent 9f839787af
commit d6264ef4ac
2 changed files with 62 additions and 57 deletions

View File

@ -1,9 +1,11 @@
import React, { useState, useEffect, useCallback } from 'react'; import React, { useState, useEffect, useCallback } from 'react';
import { getContributions } from '../../lib/github'; import { getAllCommits } from '@/lib/github';
const GithubContributionChart = ({ username }) => { const GithubContributionChart = ({ username }) => {
const [contributionData, setContributionData] = useState({}); const [contributionData, setContributionData] = useState({});
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [timeTaken, setTimeTaken] = useState(null);
const [totalCommits, setTotalCommits] = useState(0);
const getColor = useCallback((count) => { const getColor = useCallback((count) => {
if (count === 0) return 'bg-gray-100'; if (count === 0) return 'bg-gray-100';
@ -30,15 +32,27 @@ const GithubContributionChart = ({ username }) => {
useEffect(() => { useEffect(() => {
const fetchData = async () => { const fetchData = async () => {
setLoading(true); setLoading(true);
try { const startTime = Date.now();
await getContributions(username, (data) => { const sixMonthsAgo = new Date();
setContributionData(data); sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
setLoading(false);
let commitCount = 0;
for await (const commit of getAllCommits(username, sixMonthsAgo)) {
const date = commit.commit.author.date.split('T')[0];
setContributionData(prev => {
const newData = { ...prev };
newData[date] = (newData[date] || 0) + 1;
return newData;
}); });
} catch (error) { commitCount++;
console.error("Error fetching contribution data:", error); setTotalCommits(commitCount);
setLoading(false);
} }
const endTime = Date.now();
setTimeTaken(((endTime - startTime) / 1000).toFixed(2));
setLoading(false);
console.log(`Total commits fetched: ${commitCount}`);
}; };
fetchData(); fetchData();
@ -49,7 +63,9 @@ const GithubContributionChart = ({ username }) => {
return ( return (
<div className="p-4"> <div className="p-4">
<h2 className="text-xl font-bold mb-4">Github Contributions for {username}</h2> <h2 className="text-xl font-bold mb-4">Github Contributions for {username}</h2>
{loading && <p>Loading contribution data...</p>} {loading && <p>Loading contribution data... ({totalCommits} commits fetched)</p>}
{timeTaken && <p className="mb-2">Time taken to fetch data: {timeTaken} seconds</p>}
{!loading && <p className="mb-2">Total commits: {totalCommits}</p>}
<div className="flex flex-wrap gap-1"> <div className="flex flex-wrap gap-1">
{calendar.map((day, index) => ( {calendar.map((day, index) => (
<div <div

View File

@ -1,11 +1,10 @@
const { Octokit } = require("@octokit/rest"); import { Octokit } from "@octokit/rest";
const { throttling } = require("@octokit/plugin-throttling"); import { throttling } from "@octokit/plugin-throttling";
const ThrottledOctokit = Octokit.plugin(throttling); const ThrottledOctokit = Octokit.plugin(throttling);
const ACCESS_TOKEN = process.env.NEXT_PUBLIC_GITHUB_ACCESS_KEY;
const octokit = new ThrottledOctokit({ const octokit = new ThrottledOctokit({
auth: ACCESS_TOKEN, auth: process.env.NEXT_PUBLIC_GITHUB_ACCESS_KEY,
throttle: { throttle: {
onRateLimit: (retryAfter, options, octokit, retryCount) => { onRateLimit: (retryAfter, options, octokit, retryCount) => {
octokit.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`); octokit.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`);
@ -21,55 +20,45 @@ const octokit = new ThrottledOctokit({
}, },
}); });
async function getContributions(username, updateCallback) { export async function* getAllCommits(username, since) {
const sixMonthsAgo = new Date(); let page = 1;
sixMonthsAgo.setMonth(sixMonthsAgo.getMonth() - 6);
const sinceDate = sixMonthsAgo.toISOString();
const contributionData = {}; while (true) {
try {
const { data: repos } = await octokit.repos.listForUser({
username,
per_page: 100,
page,
});
try { if (repos.length === 0) break;
const repos = await octokit.paginate(octokit.repos.listForUser, {
username,
per_page: 100,
});
console.log(`Fetched ${repos.length} repositories for ${username}`); const repoPromises = repos.map(repo =>
octokit.repos.listCommits({
owner: username,
repo: repo.name,
since: since.toISOString(),
per_page: 100,
})
);
// Call updateCallback immediately after fetching repos const repoResults = await Promise.allSettled(repoPromises);
updateCallback({});
for (const repo of repos) { for (const result of repoResults) {
console.log(`Fetching commits for ${repo.name}`); if (result.status === 'fulfilled') {
try { for (const commit of result.value.data) {
const commits = await octokit.paginate(octokit.repos.listCommits, { yield commit;
owner: repo.owner.login, }
repo: repo.name, } else {
author: username, console.warn(`Error fetching commits: ${result.reason}`);
since: sinceDate, }
per_page: 100, }
});
console.log(`Fetched ${commits.length} commits for ${repo.name}`); page++;
} catch (error) {
commits.forEach(commit => { console.error("Error fetching repositories:", error.message);
const date = commit.commit.author.date.split('T')[0]; break;
contributionData[date] = (contributionData[date] || 0) + 1; }
// Call the update callback after processing each commit
updateCallback({...contributionData});
});
} catch (repoError) {
console.error(`Error fetching commits for ${repo.name}:`, repoError.message);
}
} }
console.log('Final contribution data:', contributionData);
return contributionData;
} catch (error) {
console.error("Error fetching contribution data:", error);
throw error;
}
} }
export { getContributions };