Skip to content

fix: cache overview stats in gist for fast combined card#13

Merged
compscidr merged 1 commit into
masterfrom
fix/combined-card-caching
Mar 20, 2026
Merged

fix: cache overview stats in gist for fast combined card#13
compscidr merged 1 commit into
masterfrom
fix/combined-card-caching

Conversation

@compscidr

Copy link
Copy Markdown
Owner

Summary

The combined card was timing out because fetchOverview paginates through all repos (too slow for Vercel's 10s timeout). Now:

  • GitHub Action computes and caches overview stats (name, stars, forks, contributions, repos count) in the gist alongside lines changed + views
  • Combined endpoint reads overview from the gist (instant HTTP fetch) instead of calling fetchOverview
  • Separate /api/overview still uses the live GraphQL fetcher (unchanged)

The gist JSON now contains: name, totalStars, totalForks, totalCommits, contributedTo, linesChanged, repoViews, updatedAt

After merging, run the GitHub Action manually to populate the new gist fields.

Test plan

  • Run the GitHub Action manually
  • Verify gist has the new fields
  • Verify combined card loads fast and doesn't timeout
  • Verify separate overview card still works

🤖 Generated with Claude Code

The combined endpoint was calling fetchOverview which paginates through
all repos — too slow for Vercel's 10s timeout. Now:

- GitHub Action computes and caches overview stats (stars, forks,
  contributions, repos count) in the gist alongside lines/views
- Combined endpoint reads overview from the gist (instant) instead of
  calling fetchOverview
- Separate /api/overview endpoint still uses live GraphQL fetcher

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Mar 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
github-readme-stats Ready Ready Preview, Comment Mar 20, 2026 4:58am

Copilot AI review requested due to automatic review settings March 20, 2026 04:58
@compscidr compscidr merged commit b8ed24c into master Mar 20, 2026
9 checks passed
@codecov

codecov Bot commented Mar 20, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 96.53%. Comparing base (6a773ff) to head (15d81b0).
⚠️ Report is 15 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master      #13   +/-   ##
=======================================
  Coverage   96.53%   96.53%           
=======================================
  Files          36       36           
  Lines        7363     7363           
  Branches      614      614           
=======================================
  Hits         7108     7108           
  Misses        246      246           
  Partials        9        9           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses combined-card timeouts on Vercel by moving expensive “overview” aggregation work into a GitHub Action that caches results in a public gist, and updating the combined endpoint to read those cached values instead of paginating via live GraphQL.

Changes:

  • Extend scripts/update-stats.js to compute and persist overview stats (name, stars, forks, all-time contributions, contributed-to count) into the gist JSON alongside existing lines-changed and views.
  • Update /api/combined to fetch overview stats from the cached gist (while keeping streak and top-languages fetched live).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
scripts/update-stats.js Adds GraphQL helpers + overview aggregation and writes the expanded stats payload to github-stats.json in the gist.
api/combined.js Replaces live fetchOverview usage with a gist-backed fetchCachedOverview to make combined-card generation fast.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread scripts/update-stats.js
Comment on lines +99 to 104
while (hasOwnedNext || hasContribNext) {
const json = await graphql(query, {
login: USERNAME,
ownedAfter: hasOwnedNext ? ownedAfter : null,
contribAfter: hasContribNext ? contribAfter : null,
});

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchAllRepos can get stuck in an endless pagination loop. When hasOwnedNext (or hasContribNext) becomes false, the code starts sending ownedAfter: null again, which restarts that connection at page 1; pageInfo.hasNextPage can then flip back to true and the loop never terminates for users with >100 repos on the other connection. Consider paginating owned and contributed repos in separate loops, or use GraphQL @include directives + boolean vars to omit a connection entirely once it’s complete (so you don’t re-read its pageInfo).

Copilot uses AI. Check for mistakes.
Comment thread scripts/update-stats.js
Comment on lines +156 to +157
const query = `query($login: String!) { user(login: $login) { ${yearFragments} } }`;
const json = await graphql(query, { login: USERNAME });

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If contributionYears is empty, yearFragments becomes an empty string and this builds a GraphQL query where user { } has an empty selection set, which is invalid GraphQL and will fail the action. Add an early return (e.g., 0) when years.length === 0 (or guard the query construction accordingly).

Copilot uses AI. Check for mistakes.
Comment thread api/combined.js
Comment on lines +32 to +36
const res = await axios({
method: "get",
url: `https://gist.githubusercontent.com/${username}/${gistId}/raw/github-stats.json`,
});
const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data;

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fetchCachedOverview no longer validates username (previously fetchOverview enforced github-username-regex). With the current code, malformed usernames can produce a bad gist URL / confusing errors. Consider validating username here (or reusing an existing username validation helper) and throwing the same “Invalid username provided.” error for consistency across endpoints.

Copilot uses AI. Check for mistakes.
Comment thread api/combined.js
Comment on lines +21 to +28
/**
* Fetch cached overview stats from the public gist.
*
* @param {string} username GitHub username (gist owner).
* @returns {Promise<object>} Cached overview stats.
*/
const fetchCachedOverview = async (username) => {
const gistId = process.env.GIST_ID;

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This gist-fetching/parsing logic is duplicated with fetchGistStats in src/fetchers/overview.js (same URL pattern + JSON parsing). To reduce drift and keep behavior consistent (timeouts, error handling, defaults), consider extracting a shared helper (e.g., src/fetchers/gist-stats.js) and using it from both overview and combined.

Copilot uses AI. Check for mistakes.
Comment thread api/combined.js
Comment on lines 89 to 93
const [overview, streak, langs] = await Promise.all([
fetchOverview(username),
fetchCachedOverview(username),
fetchStreak(username),
fetchTopLanguages(username, parseArray(exclude_repo)),
]);

Copilot AI Mar 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are Jest tests for other API routes, but none covering /api/combined, and this PR changes its data source (now depends on the gist fetch/parse). Adding a unit test that mocks the gist HTTP GET and verifies the rendered SVG uses the cached overview fields would help prevent regressions.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants