Skip to content

chore: add github sponsors on supporters#8531

Open
bjohansebas wants to merge 18 commits intomainfrom
github_sponsors
Open

chore: add github sponsors on supporters#8531
bjohansebas wants to merge 18 commits intomainfrom
github_sponsors

Conversation

@bjohansebas
Copy link
Copy Markdown
Member

@bjohansebas bjohansebas commented Jan 9, 2026

Description

Note that there’s no REST API for GitHub Sponsors, only a GraphQL API. I’m still working on this.

Preview:
imagen

Validation

Related Issues

closes #8199

Check List

  • I have read the Contributing Guidelines and made commit messages that follow the guideline.
  • I have run pnpm format to ensure the code follows the style guide.
  • I have run pnpm test to check if all tests are passing.
  • I have run pnpm build to check if the website builds without errors.
  • I've covered new added functionality with unit tests if necessary.

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 9, 2026

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

Project Deployment Actions Updated (UTC)
nodejs-org Ready Ready Preview Apr 12, 2026 7:50pm

Request Review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 9, 2026

👋 Codeowner Review Request

The following codeowners have been identified for the changed files:

Team reviewers: @nodejs/nodejs-website

Please review the changes when you have a chance. Thank you! 🙏

@codecov

This comment was marked as off-topic.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Jan 9, 2026

📦 Build Size Comparison

Summary

Metric Value
Old Total Size 3.51 MB
New Total Size 3.51 MB
Delta 5.00 B (0.00%)

Changes

➕ Added Assets (2)
Name Size
.next/static/chunks/4ab402c4ccfd82ec.js 41.55 KB
.next/static/chunks/9683dd9e3611b206.js 197.63 KB
➖ Removed Assets (2)
Name Size
.next/static/chunks/46deeb46308b8a8b.js 41.54 KB
.next/static/chunks/3a4ee3cc36de6c5f.js 197.63 KB

Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
@bjohansebas
Copy link
Copy Markdown
Member Author

@nodejs/web-infra I think the new environment variable should also be set in Vercel, since that’s where the website is built, both for production and for previews

imagen

@bjohansebas bjohansebas marked this pull request as ready for review February 8, 2026 04:22
@bjohansebas bjohansebas requested a review from a team as a code owner February 8, 2026 04:22
Copilot AI review requested due to automatic review settings February 8, 2026 04:22

This comment was marked as resolved.

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
Comment on lines +113 to +114
organization(login: "nodejs") {
sponsorshipsAsMaintainer (first: 100, includePrivate: false, after: "${cursor}", activeOnly: false) {
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

definitely, I’d prefer that we only show sponsors that are active, but why do we want to show everyone? see #8268.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

wdyt @nodejs/nodejs-website @nodejs/marketing ?

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Sebastian Beltran <bjohansebas@gmail.com>
@bmuenzenmeyer
Copy link
Copy Markdown
Contributor

@bjohansebas do you need any help landing this?

@ovflowd
Copy link
Copy Markdown
Member

ovflowd commented Apr 10, 2026

@bjohansebas indeed, friendly bump, is there any progress here?

@bjohansebas
Copy link
Copy Markdown
Member Author

I had forgotten about this, sorry! Yep, like I mentioned here #8531 (comment), those variables are also needed in Vercel for the preview.

@ovflowd
Copy link
Copy Markdown
Member

ovflowd commented Apr 11, 2026

I had forgotten about this, sorry! Yep, like I mentioned here #8531 (comment), those variables are also needed in Vercel for the preview.

@nodejs/web-infra can someone set them up there, please?

@MattIPv4
Copy link
Copy Markdown
Member

MattIPv4 commented Apr 12, 2026

I have saved a token for NEXT_GITHUB_READ_API_KEY in Vercel, for production + preview. A GitHub Actions secret of the same name is also available if needed. It is a fine-grained access token for the @openjs-vercel account with read-only repo access and no additional permissions.

@ovflowd
Copy link
Copy Markdown
Member

ovflowd commented Apr 12, 2026

If that was done already, then @bjohansebas please proceed.

@cursor
Copy link
Copy Markdown

cursor bot commented Apr 12, 2026

PR Summary

Medium Risk
Adds a new GitHub GraphQL data source (token-authenticated) and changes how supporter URLs/keys are derived, which may impact build-time data generation and rate/permission failures.

Overview
Supporters data generation now merges OpenCollective backers + GitHub Sponsors (via GitHub GraphQL) and shuffles the combined list periodically, tolerating individual source failures via Promise.allSettled.

This updates the Supporter typing/UI to accept github sources, uses url consistently for avatar links (and a source:name key), and renames/configures the GitHub read token/env (GITHUB_READ_API_KEY) plus adds GITHUB_GRAPHQL_URL. The partners page copy is updated to mention both funding platforms.

Reviewed by Cursor Bugbot for commit dfdf2ab. Bugbot is set up for automated code reviews on this repo. Configure here.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 4 potential issues.

Autofix Details

Bugbot Autofix prepared fixes for all 4 issues found in the latest run.

  • ✅ Fixed: GraphQL cursor interpolated as string "null" on first request
    • The sponsorship query now serializes cursor with JSON.stringify(cursor), so the first request sends GraphQL null instead of the string "null".
  • ✅ Fixed: Duplicate n.sponsorEntity fallback provides no additional coverage
    • Sponsor extraction was consolidated to node.sponsor || node.sponsorEntity, removing the duplicated fallback in both mapping paths.
  • ✅ Fixed: Component loses OpenCollective profile links by switching to url
    • The supporters component now passes profile || url to Avatar, restoring OpenCollective profile links while keeping website URLs as fallback.
  • ✅ Fixed: Missing deduplication between sponsorship and activity queries
    • GitHub sponsors are now added through a deduplicating helper keyed by sponsor identity so overlaps between sponsorships and activity events no longer duplicate entries.

Create PR

Or push these changes by commenting:

@cursor push 77dce1aa43
Preview (77dce1aa43)
diff --git a/apps/site/components/Common/Supporters/index.tsx b/apps/site/components/Common/Supporters/index.tsx
--- a/apps/site/components/Common/Supporters/index.tsx
+++ b/apps/site/components/Common/Supporters/index.tsx
@@ -11,13 +11,13 @@
 
 const SupportersList: FC<SupportersListProps> = ({ supporters }) => (
   <div className="flex max-w-full flex-wrap items-center justify-center gap-1">
-    {supporters.map(({ name, image, url }) => (
+    {supporters.map(({ name, image, profile, url }) => (
       <Avatar
         nickname={name}
         fallback={getAcronymFromString(name)}
         image={image}
         key={name}
-        url={url}
+        url={profile || url}
       />
     ))}
   </div>

diff --git a/apps/site/next-data/generators/supportersData.mjs b/apps/site/next-data/generators/supportersData.mjs
--- a/apps/site/next-data/generators/supportersData.mjs
+++ b/apps/site/next-data/generators/supportersData.mjs
@@ -46,7 +46,26 @@
   }
 
   const sponsors = [];
+  const seenSponsorKeys = new Set();
 
+  const addSponsor = node => {
+    const s = node.sponsor || node.sponsorEntity; // support different field names
+    const key = s?.id ?? s?.login ?? s?.url ?? s?.name ?? s?.avatarUrl;
+    if (key && seenSponsorKeys.has(key)) {
+      return;
+    }
+    if (key) {
+      seenSponsorKeys.add(key);
+    }
+
+    sponsors.push({
+      name: s?.name || s?.login || null,
+      image: s?.avatarUrl || null,
+      url: s?.url || null,
+      source: 'github',
+    });
+  };
+
   // Fetch sponsorship pages
   let cursor = null;
 
@@ -64,18 +83,8 @@
     }
 
     const { nodes, pageInfo } = nodeRes;
-    const mapped = nodes.map(n => {
-      const s = n.sponsor || n.sponsorEntity || n.sponsorEntity; // support different field names
-      return {
-        name: s?.name || s?.login || null,
-        image: s?.avatarUrl || null,
-        url: s?.url || null,
-        source: 'github',
-      };
-    });
+    nodes.forEach(addSponsor);
 
-    sponsors.push(...mapped);
-
     if (!pageInfo.hasNextPage) {
       break;
     }
@@ -96,18 +105,8 @@
   }
 
   const { nodes } = nodeRes;
-  const mapped = nodes.map(n => {
-    const s = n.sponsor || n.sponsorEntity || n.sponsorEntity; // support different field names
-    return {
-      name: s?.name || s?.login || null,
-      image: s?.avatarUrl || null,
-      url: s?.url || null,
-      source: 'github',
-    };
-  });
+  nodes.forEach(addSponsor);
 
-  sponsors.push(...mapped);
-
   return sponsors;
 }
 
@@ -115,7 +114,7 @@
   return `
     query {
         organization(login: "nodejs") {
-            sponsorshipsAsMaintainer (first: 100, includePrivate: false, after: "${cursor}", activeOnly: false) {
+            sponsorshipsAsMaintainer (first: 100, includePrivate: false, after: ${JSON.stringify(cursor)}, activeOnly: false) {
                 nodes {
                     sponsor: sponsorEntity {
                         ...on User {

This Bugbot Autofix run was free. To enable autofix for future PRs, go to the Cursor dashboard.

Copy link
Copy Markdown

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

There are 2 total unresolved issues (including 1 from previous review).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 492c9ed. Configure here.


const { nodes, pageInfo } = nodeRes;
const mapped = nodes.map(n => {
const s = n.sponsor || n.sponsorEntity; // support different field names
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

GraphQL alias makes n.sponsorEntity fallback unreachable dead code

Low Severity

The || n.sponsorEntity fallback in both fetchSponsorshipsQuery and fetchDonationsQuery is unreachable. In SPONSORSHIPS_QUERY, the GraphQL alias sponsor: sponsorEntity ensures the response field is always sponsorsponsorEntity never appears in the JSON response. In DONATIONS_QUERY, the field is natively named sponsor. So n.sponsorEntity is always undefined, making the fallback dead code with a misleading comment.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 492c9ed. Configure here.

@bjohansebas bjohansebas requested a review from a team as a code owner April 12, 2026 19:42
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.

Implement GitHub Sponsors data fetching

8 participants