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

hsluoyz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/casbin-website.git


The following commit(s) were added to refs/heads/master by this push:
     new 2383ad8c feat: remove homepage GitHub API request (#469)
2383ad8c is described below

commit 2383ad8cf2936720027eeee46d21818180ad411f
Author: cooronx <[email protected]>
AuthorDate: Sat Apr 18 16:13:42 2026 +0800

    feat: remove homepage GitHub API request (#469)
---
 .github/workflows/master.yml         |  3 +++
 .github/workflows/pr_ci.yml          |  3 +++
 package.json                         |  1 +
 scripts/generate-footer-stars.js     | 46 +++++++++-----------------------
 scripts/generate-homepage-release.js | 51 ++++++++++++++++++++++++++++++++++++
 scripts/github-data-utils.js         | 45 +++++++++++++++++++++++++++++++
 src/data/latest-release.json         |  5 ++++
 src/pages/index.js                   | 16 +++++------
 8 files changed, 126 insertions(+), 44 deletions(-)

diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml
index 6c0091a4..ad7bbf4b 100644
--- a/.github/workflows/master.yml
+++ b/.github/workflows/master.yml
@@ -51,6 +51,9 @@ jobs:
       - name: Generate footer stars
         run: yarn generate:footer-stars
 
+      - name: Generate homepage release
+        run: yarn generate:homepage-release
+
       - name: Build
         run: yarn build
 
diff --git a/.github/workflows/pr_ci.yml b/.github/workflows/pr_ci.yml
index 3024e6ce..9a99b461 100644
--- a/.github/workflows/pr_ci.yml
+++ b/.github/workflows/pr_ci.yml
@@ -25,5 +25,8 @@ jobs:
       - name: Generate footer stars
         run: yarn generate:footer-stars
 
+      - name: Generate homepage release
+        run: yarn generate:homepage-release
+
       - name: Build website
         run: yarn build --locale en
diff --git a/package.json b/package.json
index d63b6281..4fc98b49 100644
--- a/package.json
+++ b/package.json
@@ -6,6 +6,7 @@
   "scripts": {
     "docusaurus": "docusaurus",
     "generate:footer-stars": "node scripts/generate-footer-stars.js",
+    "generate:homepage-release": "node scripts/generate-homepage-release.js",
     "start": "node scripts/start.js",
     "build": "docusaurus build",
     "swizzle": "docusaurus swizzle",
diff --git a/scripts/generate-footer-stars.js b/scripts/generate-footer-stars.js
index 0ab1a51a..83beca7f 100644
--- a/scripts/generate-footer-stars.js
+++ b/scripts/generate-footer-stars.js
@@ -1,5 +1,9 @@
-const fs = require("fs");
 const path = require("path");
+const {
+  fetchGitHubJson,
+  readExistingJson,
+  writeJson,
+} = require("./github-data-utils");
 
 const badges = require("../src/components/FooterMoreBadges/badges.json");
 
@@ -16,40 +20,14 @@ function getRepoKey(owner, repo) {
   return `${owner}/${repo}`;
 }
 
-function readExistingStars() {
-  if (!fs.existsSync(outputPath)) {
-    return null;
-  }
-
-  try {
-    return JSON.parse(fs.readFileSync(outputPath, "utf8"));
-  } catch {
-    return null;
-  }
-}
-
 async function fetchStars() {
-  const headers = {
-    Accept: "application/vnd.github+json",
-    "User-Agent": "casbin-website-footer-stars",
-  };
-
-  if (process.env.GITHUB_TOKEN) {
-    headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
-  }
-
   const results = await Promise.all(
     badges.map(async({owner, repo}) => {
-      const response = await 
fetch(`https://api.github.com/repos/${owner}/${repo}`, {
-        headers,
-      });
-
-      if (!response.ok) {
-        const body = await response.text();
-        throw new Error(`Failed to fetch stars for ${owner}/${repo}: 
${response.status} ${body}`);
-      }
-
-      const data = await response.json();
+      const data = await fetchGitHubJson(
+        `https://api.github.com/repos/${owner}/${repo}`,
+        "casbin-website-footer-stars",
+        `stars for ${owner}/${repo}`
+      );
 
       if (typeof data.stargazers_count !== "number") {
         throw new Error(`Missing stargazers_count for ${owner}/${repo}`);
@@ -63,7 +41,7 @@ async function fetchStars() {
 }
 
 async function main() {
-  const existingStars = readExistingStars();
+  const existingStars = readExistingJson(outputPath);
 
   try {
     const stars = await fetchStars();
@@ -72,7 +50,7 @@ async function main() {
       stars,
     };
 
-    fs.writeFileSync(outputPath, `${JSON.stringify(payload, null, 2)}\n`);
+    writeJson(outputPath, payload);
     process.stdout.write(`Updated footer stars at ${outputPath}\n`);
   } catch (error) {
     if (existingStars?.stars) {
diff --git a/scripts/generate-homepage-release.js 
b/scripts/generate-homepage-release.js
new file mode 100644
index 00000000..17eef6ab
--- /dev/null
+++ b/scripts/generate-homepage-release.js
@@ -0,0 +1,51 @@
+const path = require("path");
+const {
+  fetchGitHubJson,
+  readExistingJson,
+  writeJson,
+} = require("./github-data-utils");
+
+const outputPath = path.join(__dirname, "..", "src", "data", 
"latest-release.json");
+const fallbackTag = "v3.4.1";
+const fallbackUrl = 
`https://github.com/apache/casbin/releases/tag/${fallbackTag}`;
+
+async function fetchLatestRelease() {
+  const data = await fetchGitHubJson(
+    "https://api.github.com/repos/casbin/casbin/releases/latest";,
+    "casbin-website-homepage-release",
+    "latest homepage release"
+  );
+
+  return {
+    generatedAt: new Date().toISOString(),
+    tagName: data.tag_name || fallbackTag,
+    url: data.html_url || fallbackUrl,
+  };
+}
+
+async function main() {
+  const existingRelease = readExistingJson(outputPath);
+
+  try {
+    const payload = await fetchLatestRelease();
+    writeJson(outputPath, payload);
+    process.stdout.write(`Updated homepage release at ${outputPath}\n`);
+  } catch (error) {
+    if (existingRelease?.tagName && existingRelease?.url) {
+      process.stderr.write(`${error.message}\nUsing existing homepage release 
JSON.\n`);
+      return;
+    }
+
+    writeJson(outputPath, {
+      generatedAt: new Date().toISOString(),
+      tagName: fallbackTag,
+      url: fallbackUrl,
+    });
+    process.stderr.write(`${error.message}\nUsing fallback homepage release 
JSON.\n`);
+  }
+}
+
+main().catch((error) => {
+  process.stderr.write(`${error.stack || error}\n`);
+  process.exit(1);
+});
diff --git a/scripts/github-data-utils.js b/scripts/github-data-utils.js
new file mode 100644
index 00000000..081b275a
--- /dev/null
+++ b/scripts/github-data-utils.js
@@ -0,0 +1,45 @@
+const fs = require("fs");
+const path = require("path");
+
+function readExistingJson(filePath) {
+  if (!fs.existsSync(filePath)) {
+    return null;
+  }
+
+  try {
+    return JSON.parse(fs.readFileSync(filePath, "utf8"));
+  } catch {
+    return null;
+  }
+}
+
+function writeJson(filePath, payload) {
+  fs.mkdirSync(path.dirname(filePath), {recursive: true});
+  fs.writeFileSync(filePath, `${JSON.stringify(payload, null, 2)}\n`);
+}
+
+function getGitHubHeaders(userAgent) {
+  return {
+    Accept: "application/vnd.github+json",
+    "User-Agent": userAgent,
+  };
+}
+
+async function fetchGitHubJson(url, userAgent, errorLabel) {
+  const response = await fetch(url, {
+    headers: getGitHubHeaders(userAgent),
+  });
+
+  if (!response.ok) {
+    const body = await response.text();
+    throw new Error(`Failed to fetch ${errorLabel}: ${response.status} 
${body}`);
+  }
+
+  return response.json();
+}
+
+module.exports = {
+  fetchGitHubJson,
+  readExistingJson,
+  writeJson,
+};
diff --git a/src/data/latest-release.json b/src/data/latest-release.json
new file mode 100644
index 00000000..f419b372
--- /dev/null
+++ b/src/data/latest-release.json
@@ -0,0 +1,5 @@
+{
+  "generatedAt": "2026-04-18T02:02:59.172Z",
+  "tagName": "v3.10.0",
+  "url": "https://github.com/apache/casbin/releases/tag/v3.10.0";
+}
diff --git a/src/pages/index.js b/src/pages/index.js
index 5c66b31e..b95671ad 100644
--- a/src/pages/index.js
+++ b/src/pages/index.js
@@ -1,4 +1,4 @@
-import React, {useEffect, useRef, useState} from "react";
+import React, {useEffect, useRef} from "react";
 import clsx from "clsx";
 import Layout from "@theme/Layout";
 import Link from "@docusaurus/Link";
@@ -12,26 +12,22 @@ import useDocusaurusContext from 
"@docusaurus/useDocusaurusContext";
 import AnimatedText from "../components/AnimatedText";
 import {Code, Zap} from "lucide-react";
 import LogoCarousel from "@site/src/components/LogoCarousel";
+import latestReleaseData from "@site/src/data/latest-release.json";
 
 function HomepageHeader() {
-  const [latestVersion, setLatestVersion] = useState("v3.4.1");
   const headerRef = useRef(null);
   const haloRef = useRef(null);
   const {siteConfig} = useDocusaurusContext();
   const {customFields} = siteConfig;
+  const latestVersion = latestReleaseData.tagName || "v3.4.1";
+  const latestReleaseLink =
+    latestReleaseData.url || 
`https://github.com/apache/casbin/releases/tag/${latestVersion}`;
 
   // Activate halo behavior for this header
   useHeroCursorHalo(headerRef, haloRef);
 
-  useEffect(() => {
-    fetch("https://api.github.com/repos/casbin/casbin/releases/latest";)
-      .then(res => res.json())
-      .then(data => setLatestVersion(data.tag_name || "v3.4.1"))
-      .catch(() => setLatestVersion("v3.4.1"));
-  }, []);
-
   const pillText = customFields?.customMessage || `${latestVersion} Released`;
-  const link = customFields?.customLink || 
`https://github.com/casbin/casbin/releases/tag/${latestVersion}`;
+  const link = customFields?.customLink || latestReleaseLink;
 
   return (
     <header ref={headerRef} className={clsx("hero hero--primary", 
styles.heroBanner)}>


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to