This is an automated email from the ASF dual-hosted git repository.

davidzollo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/seatunnel-website.git


The following commit(s) were added to refs/heads/main by this push:
     new d522d91f2d0d [Website] Add PR contributors honor roll to team page 
(#475)
d522d91f2d0d is described below

commit d522d91f2d0d9d17545e9334290e26689e0452be
Author: corgy-w <[email protected]>
AuthorDate: Tue Jun 23 14:20:43 2026 +0800

    [Website] Add PR contributors honor roll to team page (#475)
---
 .github/workflows/deploy.yml           |   2 +
 package.json                           |   3 +
 src/pages/team/index.js                |  73 +++--
 src/pages/team/languages.json          |   8 +-
 src/pages/team/pr-contributors.json    | 530 +++++++++++++++++++++++++++++++++
 tools/generate-team-pr-contributors.js | 155 ++++++++++
 6 files changed, 730 insertions(+), 41 deletions(-)

diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index 1610e90d154c..0591e49eaa98 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -31,6 +31,8 @@ jobs:
         run: |
           npm run sync
       - name: Build
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         run: |
           npm run build
       - name: Copy .asf.yaml and .htaccess
diff --git a/package.json b/package.json
index c1f203520e4a..25005bb3e3ef 100644
--- a/package.json
+++ b/package.json
@@ -5,14 +5,17 @@
   "scripts": {
     "docusaurus": "docusaurus",
     "sync": "node tools/build-docs.js",
+    "prestart": "npm run team:contributors",
     "start": "docusaurus start",
     "start-zh": "docusaurus start --locale zh-CN",
+    "prebuild": "npm run team:contributors",
     "build": "cross-env NODE_OPTIONS=--max_old_space_size=10240 docusaurus 
build",
     "build:fast": "cross-env NODE_OPTIONS=--max_old_space_size=10240 
DOCUSAURUS_PARALLELIZE=true docusaurus build",
     "swizzle": "docusaurus swizzle",
     "deploy": "docusaurus deploy",
     "clear": "docusaurus clear",
     "serve": "docusaurus serve",
+    "team:contributors": "node tools/generate-team-pr-contributors.js",
     "write-translations": "docusaurus write-translations",
     "write-heading-ids": "docusaurus write-heading-ids",
     "typecheck": "tsc",
diff --git a/src/pages/team/index.js b/src/pages/team/index.js
index 220ab0d6346d..766ca813376f 100644
--- a/src/pages/team/index.js
+++ b/src/pages/team/index.js
@@ -2,22 +2,46 @@ import React from 'react';
 import useIsBrowser from '@docusaurus/useIsBrowser';
 import config from "./languages.json";
 import avatarSrc from "./github-avatars.json";
+import prContributors from "./pr-contributors.json";
 import Layout from '@theme/Layout';
 import './index.less';
 
-// Get avatar URL by user ID from Base64 data
-function avatarUrl(id) {
-    const avatarObj = avatarSrc.find((item) => item.id === id);
-    if (avatarObj) {
-        return "data:image/png;base64," + avatarObj.avatar_base64;
-    }
-    return "";
+const avatarByUserId = new Map(
+    avatarSrc.map((item) => [item.id, "data:image/png;base64," + 
item.avatar_base64])
+);
+
+function getAvatarUrl(member) {
+    return avatarByUserId.get(member.userId) || member.avatarUrl || "";
+}
+
+function TeamSection({title, description, members}) {
+    return (
+        <>
+            <h3 className="team_title">{title}</h3>
+            <p className="team_desc">{description}</p>
+            <ul className="character_list">
+                {
+                    members.map((item) => (
+                        <li className="character_item text_center" 
key={item.githubId} style={{listStyle: "none"}}>
+                            <a href={item.profileUrl || "https://github.com/"; 
+ item.githubId} target="_blank" rel="noreferrer">
+                                <img className="character_avatar" 
src={getAvatarUrl(item)} alt={item.name}/>
+                                <div className="character_desc">
+                                    <h3 className="character_id"><span 
className="githubId">githubId:</span>{item.githubId}</h3>
+                                </div>
+                            </a>
+                        </li>
+                    ))
+                }
+            </ul>
+        </>
+    );
 }
 
 export default function () {
     const isBrowser = useIsBrowser();
     const language = isBrowser && location.pathname.indexOf('/zh-CN/') === 0 ? 
'zh-CN' : 'en';
     const dataSource = config?.[language];
+    const contributorDesc = 
dataSource.info.prContributorDesc.replace('{count}', 
String(prContributors.length));
 
     return (
         <Layout>
@@ -25,39 +49,10 @@ export default function () {
                 <h3 className="team_title">SeaTunnel Team</h3>
                 <p className="team_desc" dangerouslySetInnerHTML={ { __html: 
dataSource.info.desc } }/>
 
-                <h3 className="team_title">PMC</h3>
-                <p className="team_desc">{dataSource.info.tip}</p>
-                <ul className="character_list">
-                    {
-                        config.pmc.map((item, i) => (
-                            <a href={'https://github.com/' + item.githubId} 
key={i} target="_blank">
-                                <li className="character_item text_center" 
style={{'listStyle': 'none'}}>
-                                    <img className="character_avatar" 
src={avatarUrl(item.userId)} alt={item.name}/>
-                                    <div className="character_desc">
-                                        <h3 className="character_id"><span 
className="githubId">githubId:</span>{item.githubId}</h3>
-                                    </div>
-                                </li>
-                            </a>
-                        ))
-                    }
-                </ul>
+                <TeamSection title="PMC" description={dataSource.info.tip} 
members={config.pmc}/>
 
-                <h3 className="team_title">Committer</h3>
-                <p className="team_desc">{dataSource.info.tip}</p>
-                <ul className="character_list">
-                  {
-                      config.committer.map((item, i) => (
-                      <a href={'https://github.com/' + item.githubId} key={i} 
target="_blank">
-                        <li className="character_item text_center" 
style={{'listStyle': 'none'}}>
-                          <img className="character_avatar" 
src={avatarUrl(item.userId)} alt={item.name}/>
-                          <div className="character_desc">
-                            <h3 className="character_id"><span 
className="githubId">githubId:</span>{item.githubId}</h3>
-                          </div>
-                        </li>
-                      </a>
-                    ))
-                  }
-                </ul>
+                <TeamSection title="Committer" 
description={dataSource.info.tip} members={config.committer}/>
+                <TeamSection title={dataSource.info.prContributorTitle} 
description={contributorDesc} members={prContributors}/>
             </div>
         </Layout>
     );
diff --git a/src/pages/team/languages.json b/src/pages/team/languages.json
index b3e5339c047e..a292e51eaa75 100644
--- a/src/pages/team/languages.json
+++ b/src/pages/team/languages.json
@@ -2,13 +2,17 @@
   "zh-CN": {
     "info": {
       "desc": "SeaTunnel 社区由贡献者组成。 贡献者可以直接访问 SeaTunnel 
项目的源代码并参与贡献当中(包括但不限于代码的贡献)。 贡献者通过提交补丁和建议来改善项目。 该项目的贡献者数量是无限的。 
无论是琐碎的清理工作,重要的新功能还是其他重大的奖励,对 SeaTunnel 所做的所有贡献都将受到极大的赞赏。",
-      "tip": "(排名不分先后)"
+      "tip": "(排名不分先后)",
+      "prContributorTitle": "PR Contributors",
+      "prContributorDesc": "除上方 PMC 与 Committer 外,以下 {count} 位贡献者都曾向 Apache 
SeaTunnel 官网仓库提交过至少一个 PR,已一并加入荣誉榜。"
     }
   },
   "en": {
     "info": {
       "desc": "The SeaTunnel team is comprised of Members and Contributors. 
Members have direct access to the source of SeaTunnel project and actively 
evolve the code-base. Contributors improve the project through submission of 
patches and suggestions to the Members. The number of Contributors to the 
project is unbounded. All contributions to SeaTunnel are greatly appreciated, 
whether for trivial cleanups, big new features or other material rewards.",
-      "tip": "(In no particular order)"
+      "tip": "(In no particular order)",
+      "prContributorTitle": "PR Contributors",
+      "prContributorDesc": "In addition to the PMC members and committers 
listed above, the following {count} contributors have each submitted at least 
one pull request to the Apache SeaTunnel website repository."
     }
   },
   "pmc": [
diff --git a/src/pages/team/pr-contributors.json 
b/src/pages/team/pr-contributors.json
new file mode 100644
index 000000000000..06972c748c9f
--- /dev/null
+++ b/src/pages/team/pr-contributors.json
@@ -0,0 +1,530 @@
+[
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/45308043?s=240&v=4";,
+    "githubId": "914104331",
+    "name": "cxb",
+    "profileUrl": "https://github.com/914104331";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/93298332?s=240&u=3828a1b4a855b84ca28b5704846191852b5d107f&v=4";,
+    "githubId": "a110q",
+    "name": "久安",
+    "profileUrl": "https://github.com/a110q";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/61214110?s=240&v=4";,
+    "githubId": "Ada-pro",
+    "name": "Ada-pro",
+    "profileUrl": "https://github.com/Ada-pro";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/106758417?s=240&u=bbf2ae63e14038caacddc5b5c870c9b201d140f3&v=4";,
+    "githubId": "amol64546",
+    "name": "Amol Nakhate",
+    "profileUrl": "https://github.com/amol64546";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/8108788?s=240&u=ee61dc4cc1065e236f45594f5765377986da04e1&v=4";,
+    "githubId": "asdf2014",
+    "name": "Benedict Jin",
+    "profileUrl": "https://github.com/asdf2014";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/77265354?s=240&u=c76b290058f23bf54344b7a5ecee77dd2da692a5&v=4";,
+    "githubId": "B1F030",
+    "name": "Odysseus Zhang",
+    "profileUrl": "https://github.com/B1F030";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/44311483?s=240&u=8c8d0ccd6236dad1f2cd30e29484640c43338426&v=4";,
+    "githubId": "baicie",
+    "name": "zhiwei liu",
+    "profileUrl": "https://github.com/baicie";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/41284456?s=240&u=7be80bff27c1e8da26a493a3a11977cbbc7fb5fb&v=4";,
+    "githubId": "bravekong",
+    "name": "brave kong",
+    "profileUrl": "https://github.com/bravekong";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/28802427?s=240&u=fea0166c5329c9bc48bee4e95591b5fb5105affe&v=4";,
+    "githubId": "bwcxyk",
+    "name": "不忘初心",
+    "profileUrl": "https://github.com/bwcxyk";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/7960313?s=240&u=af102ad17ac40548caf7ff615e3e6d34f32a2e6b&v=4";,
+    "githubId": "chaplinthink",
+    "name": "Wei Zhang",
+    "profileUrl": "https://github.com/chaplinthink";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/29588249?s=240&u=4be9190980935d0867d8b9e94c83dbc79fbfbb6c&v=4";,
+    "githubId": "CheneyYin",
+    "name": "Chengyu Yan",
+    "profileUrl": "https://github.com/CheneyYin";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/49471125?s=240&v=4";,
+    "githubId": "Chenkeyu1126",
+    "name": "Chenkeyu1126",
+    "profileUrl": "https://github.com/Chenkeyu1126";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/20869868?s=240&v=4";,
+    "githubId": "chenyz1984",
+    "name": "ChenYingzi",
+    "profileUrl": "https://github.com/chenyz1984";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/47146908?s=240&u=0977538b149f97d8cbc6c79db0197214ba5d661e&v=4";,
+    "githubId": "clownAdam",
+    "name": "clownAdam",
+    "profileUrl": "https://github.com/clownAdam";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/69153398?s=240&u=67bb72ee58166c85c4b9d5ea4433b3c1ecad6670&v=4";,
+    "githubId": "CoolLoong",
+    "name": "CoolLoong",
+    "profileUrl": "https://github.com/CoolLoong";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/413886?s=240&v=4";,
+    "githubId": "ctyytc",
+    "name": "Lex.Chen",
+    "profileUrl": "https://github.com/ctyytc";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/153884653?s=240&u=b7e5dd66bd0d5d3c45651afdb5c8e5cf4c7eb11b&v=4";,
+    "githubId": "Cyanty",
+    "name": "Cyanty",
+    "profileUrl": "https://github.com/Cyanty";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/112263692?s=240&v=4";,
+    "githubId": "dafengy",
+    "name": "dafengy",
+    "profileUrl": "https://github.com/dafengy";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/45312807?s=240&v=4";,
+    "githubId": "daigoopautoy",
+    "name": "daigoopautoy",
+    "profileUrl": "https://github.com/daigoopautoy";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/60906603?s=240&u=e7e4adb764768222f8cbdd56c8fb05a6e87f9e3f&v=4";,
+    "githubId": "DalongZhenZ",
+    "name": "dalong",
+    "profileUrl": "https://github.com/DalongZhenZ";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/48329107?s=240&u=31e36d1d4cd24f12db6700eea8ae002308642b5a&v=4";,
+    "githubId": "DanielLeens",
+    "name": "Daniel",
+    "profileUrl": "https://github.com/DanielLeens";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/136911434?s=240&u=52166601f460fe424e943398c1ac1cd797264491&v=4";,
+    "githubId": "davidfans",
+    "name": "davidfans",
+    "profileUrl": "https://github.com/davidfans";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/in/29110?s=240&v=4";,
+    "githubId": "dependabot",
+    "name": "dependabot",
+    "profileUrl": "https://github.com/apps/dependabot";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/25605496?s=240&u=77b2aba90ff0d1204a003f5225f2de99b621cfb4&v=4";,
+    "githubId": "DismalSnail",
+    "name": "DismalSnail",
+    "profileUrl": "https://github.com/DismalSnail";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/111486498?s=240&u=4f18e3f245ae6b044f09c78c9edc5bcfd8b94cbc&v=4";,
+    "githubId": "e-mhui",
+    "name": "e-mhui",
+    "profileUrl": "https://github.com/e-mhui";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/22633385?s=240&u=29190f6c8aed91fa9574b064a9995f1e49944acf&v=4";,
+    "githubId": "eltociear",
+    "name": "Ikko Eltociear Ashimine",
+    "profileUrl": "https://github.com/eltociear";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/60566194?s=240&u=6258548d4a5a0057a8640f2f516d033a82daed81&v=4";,
+    "githubId": "fcb-xiaobo",
+    "name": "fcb-xiaobo",
+    "profileUrl": "https://github.com/fcb-xiaobo";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/720923?s=240&u=7a39fae3026cd2023e485f2ba0b7d39cea2cc730&v=4";,
+    "githubId": "fedefernandez",
+    "name": "Fede Fernández",
+    "profileUrl": "https://github.com/fedefernandez";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/10903595?s=240&u=432abe1a80e6bf36efbd754c1ee96a1bda4954bb&v=4";,
+    "githubId": "forecasted",
+    "name": "Bill Xu",
+    "profileUrl": "https://github.com/forecasted";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/5908412?s=240&u=9b7d43a3d28655ddb736e1f674d3bba9fa44a253&v=4";,
+    "githubId": "francisoliverlee",
+    "name": "tiger",
+    "profileUrl": "https://github.com/francisoliverlee";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/5416876?s=240&u=559983a6391adf6becdeb1d3a52071b43f820944&v=4";,
+    "githubId": "FrommyMind",
+    "name": "Daniel Duan",
+    "profileUrl": "https://github.com/FrommyMind";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/49440955?s=240&u=22a15777aecb926a79cd8450da4e6884cab383d8&v=4";,
+    "githubId": "gaoweiwei53",
+    "name": "gaoweiwei53",
+    "profileUrl": "https://github.com/gaoweiwei53";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/100080930?s=240&v=4";,
+    "githubId": "gbyangg",
+    "name": "gbyangg",
+    "profileUrl": "https://github.com/gbyangg";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/74084119?s=240&u=b85e7aef92fffd827677c73f1f6dd8180b49ca6f&v=4";,
+    "githubId": "GHkrishna",
+    "name": "Krishna Waske",
+    "profileUrl": "https://github.com/GHkrishna";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/20422210?s=240&v=4";,
+    "githubId": "HaoXuAI",
+    "name": "Hao Xu",
+    "profileUrl": "https://github.com/HaoXuAI";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/26700915?s=240&u=bb733f310edb6e5308ff9b9693e91fefb7e80ddb&v=4";,
+    "githubId": "hengke",
+    "name": "郑恒科",
+    "profileUrl": "https://github.com/hengke";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/38177246?s=240&u=3ebc48694efbf85559041a9b4f9619cbab287cd0&v=4";,
+    "githubId": "HeyAlaia",
+    "name": "Alaia",
+    "profileUrl": "https://github.com/HeyAlaia";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/9191388?s=240&v=4";,
+    "githubId": "javalover123",
+    "name": "javalover123",
+    "profileUrl": "https://github.com/javalover123";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/25904711?s=240&u=711a42892d396d0f25f89bd3c4798b3aa0219a88&v=4";,
+    "githubId": "JeremyXin",
+    "name": "JeremyXin",
+    "profileUrl": "https://github.com/JeremyXin";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/45607883?s=240&v=4";,
+    "githubId": "jonesjaycob",
+    "name": "jonesjaycob",
+    "profileUrl": "https://github.com/jonesjaycob";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/23008405?s=240&u=65eb85fc30b4ee131b73472b35887bc24109bece&v=4";,
+    "githubId": "kit101",
+    "name": "kit101",
+    "profileUrl": "https://github.com/kit101";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/15062456?s=240&u=6e6e326cc5f4d660e2f9714522c4012cfccf70b3&v=4";,
+    "githubId": "labbomb",
+    "name": "labbomb",
+    "profileUrl": "https://github.com/labbomb";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/34700499?s=240&v=4";,
+    "githubId": "ldxwmr",
+    "name": "ldxwmr",
+    "profileUrl": "https://github.com/ldxwmr";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/5850103?s=240&u=5a843e67b98735745319c60a23fc6fc687511f4d&v=4";,
+    "githubId": "lhyundeadsoul",
+    "name": "Li Hongyu",
+    "profileUrl": "https://github.com/lhyundeadsoul";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/49712197?s=240&u=29ca382a3a800bfbbb2faf4976bc4132948bb852&v=4";,
+    "githubId": "lianghuan-xatu",
+    "name": "Huan Liang",
+    "profileUrl": "https://github.com/lianghuan-xatu";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/6852212?s=240&u=1e630b5c85712d527fd8ed87654918d957d6f47c&v=4";,
+    "githubId": "lizhitao0923",
+    "name": "lizhitao0923",
+    "profileUrl": "https://github.com/lizhitao0923";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/29206905?s=240&u=ebb4d83077fac822d645e8c18e577816ca86ce4b&v=4";,
+    "githubId": "marcleibold",
+    "name": "Marc Leibold",
+    "profileUrl": "https://github.com/marcleibold";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/144294443?s=240&u=b93dd2c8bafc55f2964ba1c673911f609e149852&v=4";,
+    "githubId": "mikulc",
+    "name": "抹茶布丁哇",
+    "profileUrl": "https://github.com/mikulc";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/5588042?s=240&v=4";,
+    "githubId": "mirhossain",
+    "name": "Mir Hossain",
+    "profileUrl": "https://github.com/mirhossain";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/21024334?s=240&u=e2e02212da12668cc5f6f4c83fa1d3702d589005&v=4";,
+    "githubId": "misi1987107",
+    "name": "misi",
+    "profileUrl": "https://github.com/misi1987107";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/2803645?s=240&v=4";,
+    "githubId": "mosence",
+    "name": "MoSence",
+    "profileUrl": "https://github.com/mosence";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/97215457?s=240&u=39cbd57f4965c8d129a3ece92ab28e745f665664&v=4";,
+    "githubId": "Niko-Zeng",
+    "name": "Niko-Zeng",
+    "profileUrl": "https://github.com/Niko-Zeng";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/78372151?s=240&u=50b7155116d8ac8d18c53654ed93cc98c6729b92&v=4";,
+    "githubId": "prclin",
+    "name": "huangkuilin",
+    "profileUrl": "https://github.com/prclin";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/258958689?s=240&v=4";,
+    "githubId": "qinliyi",
+    "name": "qinliyi",
+    "profileUrl": "https://github.com/qinliyi";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/131856?s=240&u=95905e958b1f5efd14211753b8ae85836a9b01c5&v=4";,
+    "githubId": "raboof",
+    "name": "Arnout Engelen",
+    "profileUrl": "https://github.com/raboof";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/255808?s=240&u=51eddef8dbb8db8ba09a0a3d365470641278e1fe&v=4";,
+    "githubId": "rbowen",
+    "name": "Rich Bowen",
+    "profileUrl": "https://github.com/rbowen";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/42276568?s=240&u=248d0f65303d8c8f4a89a7100739cf01e26408cb&v=4";,
+    "githubId": "realdengziqi",
+    "name": "realdengziqi",
+    "profileUrl": "https://github.com/realdengziqi";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/34889415?s=240&u=d04e49dd40d28e532f70ababdfc87b8117ae0dbc&v=4";,
+    "githubId": "s7monk",
+    "name": "s7monk",
+    "profileUrl": "https://github.com/s7monk";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/150337624?s=240&v=4";,
+    "githubId": "satishjayagopal",
+    "name": "Satish Jayagopal ",
+    "profileUrl": "https://github.com/satishjayagopal";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/47138038?s=240&u=c6e83f6aaa4666f7a3104971cb6e74adecc5ad8b&v=4";,
+    "githubId": "scorpig",
+    "name": "scorpig",
+    "profileUrl": "https://github.com/scorpig";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/12607748?s=240&u=93a424d2fcc1235efd2b802ef31d8b0ec7f24a4c&v=4";,
+    "githubId": "sheng-jie",
+    "name": "Shengjie Yan",
+    "profileUrl": "https://github.com/sheng-jie";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/107047517?s=240&v=4";,
+    "githubId": "shichao-timeout",
+    "name": "shichao-timeout",
+    "profileUrl": "https://github.com/shichao-timeout";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/15671474?s=240&u=0e5e1cb6c216d828ef2628782c565503506e5235&v=4";,
+    "githubId": "Shlpeng",
+    "name": "Shlpeng",
+    "profileUrl": "https://github.com/Shlpeng";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/46993277?s=240&v=4";,
+    "githubId": "ShuiMu-peng",
+    "name": "Nana Jerde",
+    "profileUrl": "https://github.com/ShuiMu-peng";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/86795098?s=240&u=f3b4ca2e98bbcab74f587f7df0804960bae8982f&v=4";,
+    "githubId": "smunveruddin-mwb",
+    "name": "Shabeeruddin",
+    "profileUrl": "https://github.com/smunveruddin-mwb";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/19239641?s=240&u=40feb567f446c3cd64c18b2026a971e6cf09f86b&v=4";,
+    "githubId": "songjianet",
+    "name": "songjianet",
+    "profileUrl": "https://github.com/songjianet";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/109743341?s=240&u=2f9299166a4811ae83adad741e6fdf1d7595f512&v=4";,
+    "githubId": "suntectec",
+    "name": "suntectec",
+    "profileUrl": "https://github.com/suntectec";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/24221472?s=240&u=1edfaae635f3ab46f12f3093ba1b58806cd802c8&v=4";,
+    "githubId": "suyanhanx",
+    "name": "Suyan",
+    "profileUrl": "https://github.com/suyanhanx";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/16768136?s=240&u=7f345f374c1097b1b4e53ca4bd27d445a733c66a&v=4";,
+    "githubId": "wanghuan2054",
+    "name": "Wanghuan",
+    "profileUrl": "https://github.com/wanghuan2054";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/78582861?s=240&u=67bf5c9aed5dc7aa26200f2e15f9215859874d23&v=4";,
+    "githubId": "weifuwan",
+    "name": "lefeng",
+    "profileUrl": "https://github.com/weifuwan";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/1001616?s=240&u=f8b4b4b6204862550ec55700b9518c2cdbb92a7a&v=4";,
+    "githubId": "wgzhao",
+    "name": "Steven Zhao",
+    "profileUrl": "https://github.com/wgzhao";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/39487209?s=240&v=4";,
+    "githubId": "wssmao",
+    "name": "wssmao",
+    "profileUrl": "https://github.com/wssmao";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/34700551?s=240&u=d1214122b609dcbbdee3325c96efc77a3b13d06b&v=4";,
+    "githubId": "xiamidavid00",
+    "name": "xiami",
+    "profileUrl": "https://github.com/xiamidavid00";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/27844885?s=240&u=52ff8e4b7b654a315d41250b63e75d1b4c8797af&v=4";,
+    "githubId": "xiaochen-zhou",
+    "name": "xiaochen",
+    "profileUrl": "https://github.com/xiaochen-zhou";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/12961802?s=240&v=4";,
+    "githubId": "xifeng",
+    "name": "will27",
+    "profileUrl": "https://github.com/xifeng";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/49302071?s=240&u=3e5008e2c9123281fe43282474ddc95757a60f8b&v=4";,
+    "githubId": "xinxingi",
+    "name": "XinXing",
+    "profileUrl": "https://github.com/xinxingi";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/7881241?s=240&u=b75dc9187a95c70853b4fd648f5d2bdde39a5704&v=4";,
+    "githubId": "xinzhuxiansheng",
+    "name": "阿洋",
+    "profileUrl": "https://github.com/xinzhuxiansheng";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/32639811?s=240&v=4";,
+    "githubId": "XuyiK",
+    "name": "Shen Yi",
+    "profileUrl": "https://github.com/XuyiK";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/1897543?s=240&v=4";,
+    "githubId": "xvm03",
+    "name": "xvm03",
+    "profileUrl": "https://github.com/xvm03";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/88439948?s=240&u=c3c5aa08c0013f28c001452ce09e36cf285b7602&v=4";,
+    "githubId": "yujian225",
+    "name": "yujian",
+    "profileUrl": "https://github.com/yujian225";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/22396672?s=240&u=1ddf7d95fd47206181c5cf7ac0baaebc4cc6634e&v=4";,
+    "githubId": "zhangm365",
+    "name": "zhangm365",
+    "profileUrl": "https://github.com/zhangm365";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/49311144?s=240&v=4";,
+    "githubId": "zhangyuge1",
+    "name": "zhangyuge1",
+    "profileUrl": "https://github.com/zhangyuge1";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/37063904?s=240&u=82186c09a8a98b1101b100f1ba0a70a5b0b488f2&v=4";,
+    "githubId": "zhuangchong",
+    "name": "Kerwin",
+    "profileUrl": "https://github.com/zhuangchong";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/126888254?s=240&u=2f77db4e2cd545093888b547d96030c1802682a5&v=4";,
+    "githubId": "zhuchaoling",
+    "name": "chaoling",
+    "profileUrl": "https://github.com/zhuchaoling";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/13765310?s=240&u=c5734f797ad80d239cfa731ac75b13fc3f95c30d&v=4";,
+    "githubId": "zhuxt2015",
+    "name": "zhuxt2015",
+    "profileUrl": "https://github.com/zhuxt2015";
+  },
+  {
+    "avatarUrl": "https://avatars.githubusercontent.com/u/209306256?s=240&v=4";,
+    "githubId": "zhuyifeiRuichuang",
+    "name": "zhu",
+    "profileUrl": "https://github.com/zhuyifeiRuichuang";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/75787789?s=240&u=ce35f3e164872c43056b1c11ba4b6ad2eed69374&v=4";,
+    "githubId": "zooo-code",
+    "name": "zoo-code",
+    "profileUrl": "https://github.com/zooo-code";
+  },
+  {
+    "avatarUrl": 
"https://avatars.githubusercontent.com/u/70795751?s=240&u=4f318a59e599f339241e1aca6f413eeecb427eec&v=4";,
+    "githubId": "zy-kkk",
+    "name": "zy-kkk",
+    "profileUrl": "https://github.com/zy-kkk";
+  }
+]
diff --git a/tools/generate-team-pr-contributors.js 
b/tools/generate-team-pr-contributors.js
new file mode 100644
index 000000000000..9320d4aa5b70
--- /dev/null
+++ b/tools/generate-team-pr-contributors.js
@@ -0,0 +1,155 @@
+const fs = require('fs');
+const path = require('path');
+const {execSync} = require('child_process');
+
+const REPO_OWNER = 'apache';
+const REPO_NAME = 'seatunnel-website';
+const ROOT_DIR = path.resolve(__dirname, '..');
+const TEAM_CONFIG_PATH = path.join(ROOT_DIR, 'src/pages/team/languages.json');
+const OUTPUT_PATH = path.join(ROOT_DIR, 'src/pages/team/pr-contributors.json');
+const GRAPHQL_ENDPOINT = 'https://api.github.com/graphql';
+
+function readGithubToken() {
+  const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
+
+  if (envToken) {
+    return envToken.trim();
+  }
+
+  try {
+    return execSync('gh auth token', {
+      cwd: ROOT_DIR,
+      encoding: 'utf8',
+      stdio: ['ignore', 'pipe', 'ignore'],
+    }).trim();
+  } catch (error) {
+    throw new Error(
+      'GitHub token not found. Set GITHUB_TOKEN/GH_TOKEN or run `gh auth 
login` first.'
+    );
+  }
+}
+
+async function graphqlRequest(token, query, variables) {
+  const response = await fetch(GRAPHQL_ENDPOINT, {
+    method: 'POST',
+    headers: {
+      Authorization: `Bearer ${token}`,
+      'Content-Type': 'application/json',
+      'User-Agent': 'seatunnel-website-team-contributors-script',
+    },
+    body: JSON.stringify({query, variables}),
+  });
+
+  if (!response.ok) {
+    throw new Error(`GitHub GraphQL request failed: ${response.status} 
${response.statusText}`);
+  }
+
+  const payload = await response.json();
+
+  if (payload.errors?.length) {
+    throw new Error(
+      `GitHub GraphQL errors: ${payload.errors.map((item) => 
item.message).join('; ')}`
+    );
+  }
+
+  return payload.data;
+}
+
+function readExistingGithubIds() {
+  const teamConfig = JSON.parse(fs.readFileSync(TEAM_CONFIG_PATH, 'utf8'));
+  return new Set(
+    [...teamConfig.pmc, ...teamConfig.committer]
+      .map((member) => member.githubId.toLowerCase())
+  );
+}
+
+async function fetchAllPullRequestAuthors(token) {
+  const query = `
+    query PullRequestAuthors($owner: String!, $name: String!, $cursor: String) 
{
+      repository(owner: $owner, name: $name) {
+        pullRequests(
+          first: 100
+          after: $cursor
+          states: [OPEN, CLOSED, MERGED]
+          orderBy: {field: CREATED_AT, direction: ASC}
+        ) {
+          pageInfo {
+            hasNextPage
+            endCursor
+          }
+          nodes {
+            author {
+              __typename
+              login
+              avatarUrl(size: 240)
+              url
+              ... on User {
+                name
+              }
+            }
+          }
+        }
+      }
+    }
+  `;
+
+  const authors = new Map();
+  let cursor = null;
+  let hasNextPage = true;
+
+  while (hasNextPage) {
+    const data = await graphqlRequest(token, query, {
+      owner: REPO_OWNER,
+      name: REPO_NAME,
+      cursor,
+    });
+    const pullRequests = data.repository.pullRequests;
+
+    for (const node of pullRequests.nodes) {
+      const author = node.author;
+
+      if (!author?.login) {
+        continue;
+      }
+
+      const login = author.login.trim();
+      const normalizedLogin = login.toLowerCase();
+
+      if (normalizedLogin === 'ghost' || normalizedLogin.endsWith('[bot]')) {
+        continue;
+      }
+
+      if (!authors.has(normalizedLogin)) {
+        authors.set(normalizedLogin, {
+          avatarUrl: author.avatarUrl,
+          githubId: login,
+          name: author.name || login,
+          profileUrl: author.url,
+        });
+      }
+    }
+
+    hasNextPage = pullRequests.pageInfo.hasNextPage;
+    cursor = pullRequests.pageInfo.endCursor;
+  }
+
+  return authors;
+}
+
+async function main() {
+  const token = readGithubToken();
+  const existingGithubIds = readExistingGithubIds();
+  const authors = await fetchAllPullRequestAuthors(token);
+  const contributors = [...authors.entries()]
+    .filter(([githubId]) => !existingGithubIds.has(githubId))
+    .map(([, contributor]) => contributor)
+    .sort((left, right) => left.githubId.localeCompare(right.githubId, 'en', 
{sensitivity: 'base'}));
+
+  fs.writeFileSync(OUTPUT_PATH, JSON.stringify(contributors, null, 2) + '\n');
+  console.log(`Generated ${contributors.length} PR contributors in 
${path.relative(ROOT_DIR, OUTPUT_PATH)}.`);
+}
+
+main().catch((error) => {
+  console.error(error.message);
+  process.exitCode = 1;
+});

Reply via email to