Add live star history tracking

This commit is contained in:
cporter202
2026-07-22 00:19:41 -04:00
parent 5575f2d84b
commit e96cff200d
5 changed files with 225 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
name: Update star history
on:
schedule:
- cron: "17 3 * * *"
workflow_dispatch:
permissions:
contents: write
concurrency:
group: update-star-history
cancel-in-progress: true
jobs:
update:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Record stars and render chart
run: python scripts/update_star_history.py
- name: Commit updated history
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add assets/star-history.svg data/star-history.json
git diff --cached --quiet || git commit -m "Update star history"
git push
+11
View File
@@ -16,6 +16,7 @@
<p align="center">
<a href="https://github.com/cporter202/coreclaw-api-directory/actions/workflows/validate-catalog.yml"><img alt="Validate Catalog" src="https://github.com/cporter202/coreclaw-api-directory/actions/workflows/validate-catalog.yml/badge.svg"></a>
<a href="https://github.com/cporter202/coreclaw-api-directory/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/cporter202/coreclaw-api-directory?style=for-the-badge&logo=github&label=Stars&color=f59e0b"></a>
<img alt="118 public Workers" src="https://img.shields.io/badge/Public_Workers-118-6366f1?style=for-the-badge">
<img alt="11 use-case categories" src="https://img.shields.io/badge/Categories-11-06b6d4?style=for-the-badge">
<img alt="29 data sources" src="https://img.shields.io/badge/Data_Sources-29-8b5cf6?style=for-the-badge">
@@ -44,6 +45,16 @@ A polished, community-maintained directory of **118 public CoreClaw Workers** fo
</tr>
</table>
## Star history
<p align="center">
<a href="https://github.com/cporter202/coreclaw-api-directory/stargazers">
<img src="assets/star-history.svg" alt="CoreClaw API Directory star history chart" width="720">
</a>
</p>
<p align="center"><sub>Updated daily from GitHub. Like the directory? Star the repository to help more developers discover it.</sub></p>
CoreClaw calls these integrations **Workers**. Each Worker is a runnable web-data workflow with its own input schema and structured output, exposed through CoreClaws shared REST and MCP integration layers.
## Explore by use case
+33
View File
@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="430" viewBox="0 0 1200 430" role="img" aria-labelledby="title desc">
<title id="title">CoreClaw API Directory star history</title>
<desc id="desc">Daily GitHub star count, currently 1 stars.</desc>
<defs>
<linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#070B1D"/><stop offset=".55" stop-color="#111B46"/><stop offset="1" stop-color="#0B3A4A"/>
</linearGradient>
<linearGradient id="line" x1="0" y1="0" x2="1" y2="0">
<stop stop-color="#8B5CF6"/><stop offset=".5" stop-color="#6366F1"/><stop offset="1" stop-color="#22D3EE"/>
</linearGradient>
<linearGradient id="area" x1="0" y1="0" x2="0" y2="1">
<stop stop-color="#22D3EE" stop-opacity=".3"/><stop offset="1" stop-color="#6366F1" stop-opacity=".02"/>
</linearGradient>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M40 0H0V40" fill="none" stroke="#A5B4FC" stroke-opacity=".035"/>
</pattern>
</defs>
<rect width="1200" height="430" rx="26" fill="url(#background)"/>
<rect width="1200" height="430" rx="26" fill="url(#grid)"/>
<g font-family="Inter,Segoe UI,Arial,sans-serif">
<text x="60" y="61" fill="#C7D2FE" font-size="16" font-weight="700" letter-spacing="2.4">STAR GROWTH</text>
<text x="60" y="101" fill="#FFFFFF" font-size="30" font-weight="800">CoreClaw API Directory</text>
<text x="1138" y="65" text-anchor="end" fill="#FBBF24" font-size="34" font-weight="800">★ 1</text>
<text x="1138" y="94" text-anchor="end" fill="#A5B4FC" font-size="14">CURRENT STARS</text>
<line x1="92" y1="132.0" x2="1135" y2="132.0" stroke="#A5B4FC" stroke-opacity=".12"/><text x="74" y="137.0" text-anchor="end" fill="#818CF8" font-size="14">4</text><line x1="92" y1="184.0" x2="1135" y2="184.0" stroke="#A5B4FC" stroke-opacity=".12"/><text x="74" y="189.0" text-anchor="end" fill="#818CF8" font-size="14">3</text><line x1="92" y1="236.0" x2="1135" y2="236.0" stroke="#A5B4FC" stroke-opacity=".12"/><text x="74" y="241.0" text-anchor="end" fill="#818CF8" font-size="14">2</text><line x1="92" y1="288.0" x2="1135" y2="288.0" stroke="#A5B4FC" stroke-opacity=".12"/><text x="74" y="293.0" text-anchor="end" fill="#818CF8" font-size="14">1</text><line x1="92" y1="340.0" x2="1135" y2="340.0" stroke="#A5B4FC" stroke-opacity=".12"/><text x="74" y="345.0" text-anchor="end" fill="#818CF8" font-size="14">0</text>
<path d="M 613.5 340 L 613.5 288.0 L 613.5 340 Z" fill="url(#area)"/>
<polyline points="613.5,288.0" fill="none" stroke="url(#line)" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
<circle cx="613.5" cy="288.0" r="4" fill="#22D3EE" stroke="#07101F" stroke-width="2"/>
<text x="92" y="380" fill="#A5B4FC" font-size="14">2026-07-22</text>
<text x="1135" y="380" text-anchor="end" fill="#A5B4FC" font-size="14">Tracking started</text>
<text x="600" y="409" text-anchor="middle" fill="#818CF8" font-size="13">Daily snapshots recorded by this repository</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

+9
View File
@@ -0,0 +1,9 @@
{
"repository": "cporter202/coreclaw-api-directory",
"points": [
{
"date": "2026-07-22",
"stars": 1
}
]
}
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""Record the repository's daily star count and render its README chart."""
from __future__ import annotations
import json
import math
from datetime import datetime, timezone
from pathlib import Path
from urllib.request import Request, urlopen
ROOT = Path(__file__).resolve().parents[1]
DATA_PATH = ROOT / "data" / "star-history.json"
SVG_PATH = ROOT / "assets" / "star-history.svg"
REPOSITORY = "cporter202/coreclaw-api-directory"
def fetch_star_count() -> int:
request = Request(
f"https://api.github.com/repos/{REPOSITORY}",
headers={
"Accept": "application/vnd.github+json",
"User-Agent": "coreclaw-api-directory-star-history",
"X-GitHub-Api-Version": "2022-11-28",
},
)
with urlopen(request, timeout=20) as response:
return int(json.load(response)["stargazers_count"])
def update_history(stars: int) -> dict:
history = json.loads(DATA_PATH.read_text(encoding="utf-8"))
today = datetime.now(timezone.utc).date().isoformat()
points = history.setdefault("points", [])
if points and points[-1]["date"] == today:
points[-1]["stars"] = stars
else:
points.append({"date": today, "stars": stars})
points.sort(key=lambda point: point["date"])
DATA_PATH.write_text(json.dumps(history, indent=2) + "\n", encoding="utf-8")
return history
def render_svg(history: dict) -> str:
points = history["points"]
width, height = 1200, 430
left, right, top, bottom = 92, 1135, 132, 340
current = int(points[-1]["stars"])
max_value = max(int(point["stars"]) for point in points)
max_y = max(4, int(math.ceil(max_value / 4.0) * 4))
def x_at(index: int) -> float:
if len(points) == 1:
return (left + right) / 2
return left + (right - left) * index / (len(points) - 1)
def y_at(value: int) -> float:
return bottom - (bottom - top) * value / max_y
coordinates = [
(x_at(index), y_at(int(point["stars"])))
for index, point in enumerate(points)
]
polyline = " ".join(f"{x:.1f},{y:.1f}" for x, y in coordinates)
area = (
f"M {coordinates[0][0]:.1f} {bottom} "
+ " ".join(f"L {x:.1f} {y:.1f}" for x, y in coordinates)
+ f" L {coordinates[-1][0]:.1f} {bottom} Z"
)
grid = []
for step in range(5):
y = top + (bottom - top) * step / 4
label = round(max_y * (4 - step) / 4)
grid.append(
f'<line x1="{left}" y1="{y:.1f}" x2="{right}" y2="{y:.1f}" '
'stroke="#A5B4FC" stroke-opacity=".12"/>'
f'<text x="{left - 18}" y="{y + 5:.1f}" text-anchor="end" '
f'fill="#818CF8" font-size="14">{label}</text>'
)
dots = "".join(
f'<circle cx="{x:.1f}" cy="{y:.1f}" r="4" fill="#22D3EE" '
'stroke="#07101F" stroke-width="2"/>'
for x, y in coordinates[-30:]
)
end_label = points[-1]["date"] if len(points) > 1 else "Tracking started"
return f'''<svg xmlns="http://www.w3.org/2000/svg" width="{width}" height="{height}" viewBox="0 0 {width} {height}" role="img" aria-labelledby="title desc">
<title id="title">CoreClaw API Directory star history</title>
<desc id="desc">Daily GitHub star count, currently {current} stars.</desc>
<defs>
<linearGradient id="background" x1="0" y1="0" x2="1" y2="1">
<stop offset="0" stop-color="#070B1D"/><stop offset=".55" stop-color="#111B46"/><stop offset="1" stop-color="#0B3A4A"/>
</linearGradient>
<linearGradient id="line" x1="0" y1="0" x2="1" y2="0">
<stop stop-color="#8B5CF6"/><stop offset=".5" stop-color="#6366F1"/><stop offset="1" stop-color="#22D3EE"/>
</linearGradient>
<linearGradient id="area" x1="0" y1="0" x2="0" y2="1">
<stop stop-color="#22D3EE" stop-opacity=".3"/><stop offset="1" stop-color="#6366F1" stop-opacity=".02"/>
</linearGradient>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M40 0H0V40" fill="none" stroke="#A5B4FC" stroke-opacity=".035"/>
</pattern>
</defs>
<rect width="1200" height="430" rx="26" fill="url(#background)"/>
<rect width="1200" height="430" rx="26" fill="url(#grid)"/>
<g font-family="Inter,Segoe UI,Arial,sans-serif">
<text x="60" y="61" fill="#C7D2FE" font-size="16" font-weight="700" letter-spacing="2.4">STAR GROWTH</text>
<text x="60" y="101" fill="#FFFFFF" font-size="30" font-weight="800">CoreClaw API Directory</text>
<text x="1138" y="65" text-anchor="end" fill="#FBBF24" font-size="34" font-weight="800"> {current}</text>
<text x="1138" y="94" text-anchor="end" fill="#A5B4FC" font-size="14">CURRENT STARS</text>
{''.join(grid)}
<path d="{area}" fill="url(#area)"/>
<polyline points="{polyline}" fill="none" stroke="url(#line)" stroke-width="5" stroke-linejoin="round" stroke-linecap="round"/>
{dots}
<text x="{left}" y="380" fill="#A5B4FC" font-size="14">{points[0]['date']}</text>
<text x="{right}" y="380" text-anchor="end" fill="#A5B4FC" font-size="14">{end_label}</text>
<text x="600" y="409" text-anchor="middle" fill="#818CF8" font-size="13">Daily snapshots recorded by this repository</text>
</g>
</svg>
'''
def main() -> None:
history = update_history(fetch_star_count())
SVG_PATH.write_text(render_svg(history), encoding="utf-8")
print(
f"Recorded {history['points'][-1]['stars']} stars for "
f"{history['points'][-1]['date']}"
)
if __name__ == "__main__":
main()