From 567578f08d24cb85460423439d3b6258c0a83735 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Fri, 4 Sep 2026 23:45:50 -0700 Subject: [PATCH] docs(readme): replace star-history.com with self-generated chart (#11171) * docs(readme): replace star-history.com with self-generated chart The star-history.com SVG is a third-party dependency that can rate limit or go down. Replace it with a GitHub Action that fetches stargazers via the REST API and renders an SVG with matplotlib, committing note/star_history.svg weekly. The README references the committed file directly, so the chart has no runtime dependency on any external service. * ci(star-history): run daily instead of weekly --- .github/scripts/star_history.py | 87 ++ .github/workflows/star_history.yml | 46 + README.md | 2 +- note/star_history.svg | 1693 ++++++++++++++++++++++++++++ 4 files changed, 1827 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/star_history.py create mode 100644 .github/workflows/star_history.yml create mode 100644 note/star_history.svg diff --git a/.github/scripts/star_history.py b/.github/scripts/star_history.py new file mode 100644 index 000000000..7f1e6be6a --- /dev/null +++ b/.github/scripts/star_history.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +"""Render the repository's star history to note/star_history.svg. + +Uses the GitHub REST stargazers endpoint with the starred-at accept header, +which caps at 40,000 entries (400 pages of 100). The chart is regenerated on +a schedule; if the repo grows past that cap the script stops at 40,000 and +logs a warning rather than under-reporting. +""" +import json +import os +import sys +import urllib.request +from datetime import datetime + +import matplotlib + +matplotlib.use("Agg") +import matplotlib.pyplot as plt # noqa: E402 +from matplotlib.dates import AutoDateLocator, DateFormatter # noqa: E402 + +REPO = "seaweedfs/seaweedfs" +TOKEN = os.environ["GITHUB_TOKEN"] +OUT = os.environ.get("OUT", "note/star_history.svg") +PAGE_CAP = 400 # GitHub's hard limit on stargazer pagination + + +def fetch_stargazers(): + stars = [] + page = 1 + while page <= PAGE_CAP: + url = f"https://api.github.com/repos/{REPO}/stargazers?per_page=100&page={page}" + req = urllib.request.Request( + url, + headers={ + "Accept": "application/vnd.github.star+json", + "Authorization": f"Bearer {TOKEN}", + "X-GitHub-Api-Version": "2022-11-28", + "User-Agent": "seaweedfs-star-history", + }, + ) + with urllib.request.urlopen(req) as resp: + batch = json.load(resp) + if not batch: + break + for u in batch: + sa = u.get("starred_at") + if sa: + stars.append(datetime.fromisoformat(sa.replace("Z", "+00:00"))) + if len(batch) < 100: + break + page += 1 + if page > PAGE_CAP: + print( + f"::warning::Hit the {PAGE_CAP}-page stargazer pagination cap; " + "chart reflects the first 40,000 stars only." + ) + return stars + + +def render(stars, out): + stars.sort() + counts = list(range(1, len(stars) + 1)) + fig, ax = plt.subplots(figsize=(10, 4), dpi=130) + ax.plot(stars, counts, color="#0969da", linewidth=1.6) + ax.set_xlabel("Date") + ax.set_ylabel("Stars") + ax.set_title(f"{REPO} star history") + ax.grid(True, linestyle="--", alpha=0.3) + ax.xaxis.set_major_locator(AutoDateLocator()) + ax.xaxis.set_major_formatter(DateFormatter("%Y-%m")) + fig.autofmt_xdate() + fig.tight_layout() + fig.savefig(out, format="svg", transparent=False) + plt.close(fig) + + +def main(): + stars = fetch_stargazers() + if not stars: + print("::error::No stargazers fetched; not updating the chart.") + sys.exit(1) + render(stars, OUT) + print(f"Rendered {len(stars)} stars to {OUT}") + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/star_history.yml b/.github/workflows/star_history.yml new file mode 100644 index 000000000..87905fa60 --- /dev/null +++ b/.github/workflows/star_history.yml @@ -0,0 +1,46 @@ +--- +name: Star History + +on: + schedule: + - cron: "0 0 * * *" # daily, 00:00 UTC + workflow_dispatch: + +permissions: + contents: write + +jobs: + render: + name: Regenerate star history chart + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v7 + + - name: Set up Python + uses: actions/setup-python@v8 + with: + python-version: "3.x" + cache: pip + cache-dependency-path: .github/scripts/star_history.py + + - name: Install matplotlib + run: pip install matplotlib + + - name: Render chart + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: python .github/scripts/star_history.py + + - name: Commit if changed + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + if git diff --quiet -- note/star_history.svg; then + echo "No changes to the chart." + exit 0 + fi + git add note/star_history.svg + git commit -m "docs: regenerate star history chart" + git push diff --git a/README.md b/README.md index 012d94d11..82ae132a1 100644 --- a/README.md +++ b/README.md @@ -435,7 +435,7 @@ Your support will be really appreciated by me and other supporters! ## Star History -[![Star History Chart](https://api.star-history.com/svg?repos=seaweedfs/seaweedfs&type=Date)](https://star-history.com/#seaweedfs/seaweedfs&Date) +![Star History](note/star_history.svg) [WeedMini]: https://github.com/seaweedfs/seaweedfs/wiki/Quick-Start-with-weed-mini [DockerComposeS3]: https://github.com/seaweedfs/seaweedfs/wiki/Docker-Compose-for-S3 diff --git a/note/star_history.svg b/note/star_history.svg new file mode 100644 index 000000000..561226953 --- /dev/null +++ b/note/star_history.svg @@ -0,0 +1,1693 @@ + + + + + + + + 2026-09-04T23:41:32.574322 + image/svg+xml + + + Matplotlib v3.9.4, https://matplotlib.org/ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +