From 4b3978dcba3cdde44fe8e98170de35a36576103c Mon Sep 17 00:00:00 2001 From: Ettore Di Giacinto Date: Fri, 31 Jul 2026 21:23:35 +0000 Subject: [PATCH] chore(website): derive the counters from data, refresh them weekly The star, fork, contributor and release counts were typed into the templates by hand, so they only moved when somebody remembered. They had already drifted: stars read 48,042 against 48,067, forks 4,314 against 4,320, and contributors 224 against 225. They move to website/data/stats.yaml, which .github/ci/refresh-site-counters.sh rewrites from the GitHub API, run weekly by a new workflow. The contributors and releases endpoints never report a total, so the script asks for one item per page and reads the count out of the Link header. It refuses to write a zero or a non-number, which is what a rate-limited or failed call looks like, and the workflow commits only when a number actually moved. The Discord count has no API behind it, so the script reads the existing value back and carries it through. The engine count was wrong in a second way. The hero said 18, the section heading said "Eighteen engines", the timeline said "Nineteen engines of our own", and the /engines/ page derived 19 from the data file. All of them now derive from that same file, so they cannot disagree again. Signed-off-by: Ettore Di Giacinto Assisted-by: Claude Code:claude-opus-5[1m] --- .github/ci/refresh-site-counters.sh | 64 +++++++++++++++++++++ .github/workflows/refresh-site-counters.yml | 44 ++++++++++++++ website/data/stats.yaml | 13 +++++ website/hugo.toml | 5 +- website/layouts/index.html | 22 +++---- website/layouts/partials/nav.html | 2 +- 6 files changed, 135 insertions(+), 15 deletions(-) create mode 100755 .github/ci/refresh-site-counters.sh create mode 100644 .github/workflows/refresh-site-counters.yml create mode 100644 website/data/stats.yaml diff --git a/.github/ci/refresh-site-counters.sh b/.github/ci/refresh-site-counters.sh new file mode 100755 index 000000000..7b80434d4 --- /dev/null +++ b/.github/ci/refresh-site-counters.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +# Refreshes the counters shown on the landing page from the GitHub API. +# +# The numbers used to be typed into the templates by hand, which meant they +# only moved when somebody remembered, and a stale star count on the front +# page is worse than no star count. Everything the API can answer for lives +# in website/data/stats.yaml and is rewritten wholesale by this script. +# +# Anything the API cannot answer for (the Discord member count) is read back +# out of the existing file and carried through untouched. +set -euo pipefail + +REPO="${REPO:-mudler/LocalAI}" +OUT="${OUT:-website/data/stats.yaml}" + +# The contributors and releases endpoints are paginated and never report a +# total. Asking for one item per page makes the last page number equal to the +# item count, which the Link header hands over. +count_via_link_header() { + local path="$1" link last + link=$(gh api -i "${path}?per_page=1" 2>/dev/null | tr -d '\r' | grep -i '^link:' || true) + if [ -z "$link" ]; then + # No Link header means a single page, so count that page directly. + gh api "${path}?per_page=100" --jq 'length' + return + fi + last=$(sed -n 's/.*[?&]page=\([0-9]*\)>; rel="last".*/\1/p' <<<"$link") + [ -n "$last" ] || { gh api "${path}?per_page=100" --jq 'length'; return; } + printf '%s\n' "$last" +} + +read -r stars forks < <(gh api "repos/${REPO}" --jq '"\(.stargazers_count) \(.forks_count)"') +contributors=$(count_via_link_header "repos/${REPO}/contributors") +releases=$(count_via_link_header "repos/${REPO}/releases") + +# Not derivable from the GitHub API, so keep whatever is already on disk. +discord=$(sed -n 's/^discord: *\([0-9]*\).*/\1/p' "$OUT" 2>/dev/null | head -1) +discord="${discord:-0}" + +for n in stars forks contributors releases; do + v="${!n}" + [[ "$v" =~ ^[0-9]+$ ]] && [ "$v" -gt 0 ] || { + echo "refusing to write: ${n} came back as '${v}'" >&2 + exit 1 + } +done + +cat > "$OUT" <★ Star on GitHub
-
0GitHub stars
+
0GitHub stars
0Backends
-
0Engines we wrote
+
0Engines we wrote
0Models, one click
@@ -164,7 +164,7 @@

Engines we build

-

Eighteen engines, written from scratch.

+

{{ len .Site.Data.engines.engines }} engines, written from scratch.

Most backends wrap somebody else's engine. These do not. They exist because the thing we needed was a 9 GB Python install, or closed, or nobody had built it yet. Each one is a binary and a GGUF file, checked against the reference implementation in CI.

@@ -247,7 +247,7 @@
-

All eighteen engines →

+

All {{ len .Site.Data.engines.engines }} engines →

@@ -363,14 +363,14 @@

Forty-eight thousand stars, and still shipping every week.

LocalAI on Trendshift -

LocalAI has been trending on GitHub repeatedly since it launched, and it is one of the most starred self-hosted AI projects there is. 224 people have contributed code, 3,187 are in the Discord, and the README is kept translated into eight languages because the users are everywhere.

+

LocalAI has been trending on GitHub repeatedly since it launched, and it is one of the most starred self-hosted AI projects there is. {{ lang.FormatNumberCustom 0 .Site.Data.stats.contributors }} people have contributed code, {{ lang.FormatNumberCustom 0 .Site.Data.stats.discord }} are in the Discord, and the README is kept translated into eight languages because the users are everywhere.

-
0Stars
-
0Forks
-
0Contributors
-
0Releases
-
0In Discord
+
0Stars
+
0Forks
+
0Contributors
+
0Releases
+
0In Discord
0Shipping since
@@ -421,7 +421,7 @@ employers reads as a customer logo wall, which is a claim we are not making; a sentence keeps it about the people, which is the true one. */}}

Who shows up

-

{{ .Site.Params.contributors }} people have put code in this repository.

+

{{ lang.FormatNumberCustom 0 .Site.Data.stats.contributors }} people have put code in this repository.

{{- $co := slice }} {{- range .Site.Data.ecosystem.contributors.companies }}{{ $co = $co | append (printf "%s" .name) }}{{ end }} {{- $ac := slice }} diff --git a/website/layouts/partials/nav.html b/website/layouts/partials/nav.html index f8837b6ff..71fed539f 100644 --- a/website/layouts/partials/nav.html +++ b/website/layouts/partials/nav.html @@ -13,7 +13,7 @@ Docs