- 数据收集:定期(例如每天)获取你所有仓库的 Star 和 Fork 总数。
- 数据存储:将收集到的数据(时间戳、总 Star 数、总 Fork 数)存储起来。
- 图表生成:根据存储的数据生成变化曲线图(通常是 SVG 或 PNG 格式)。
- 自动更新:使用 GitHub Actions 自动化上述过程,并将生成的图表提交到你的主页仓库。
- README 展示 :在
用户名/用户名/README.md
中引用生成的图表。
下面是一个比较详细的方案,以 Python 和 Matplotlib 为例,并结合 GitHub Actions:
方案:使用 GitHub Actions + Python + Matplotlib
1. 项目结构 (在你的 用户名/用户名
仓库中)
.
├── .github/
│ └── workflows/
│ └── update_stats.yml # GitHub Actions 配置文件
├── scripts/
│ └── generate_chart.py # Python 脚本
├── data/
│ └── stats_history.csv # 存储历史数据
├── images/ # 存储生成的图表
│ └── stars_chart.svg
│ └── forks_chart.svg
└── README.md
2. Python 脚本 (scripts/generate_chart.py
)
这个脚本会:
- 从 GitHub API 获取当前所有仓库的 Star 和 Fork 总数。
- 读取
data/stats_history.csv
中的历史数据。 - 将新数据追加到 CSV 文件。
- 使用 Matplotlib 生成 Star 和 Fork 变化曲线图,并保存为 SVG。
python
import os
import requests
import pandas as pd
from datetime import datetime
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# --- 配置 ---
GITHUB_USERNAME = os.environ.get("GITHUB_ACTOR") # 从 GitHub Actions 环境变量获取用户名
GITHUB_TOKEN = os.environ.get("GITHUB_TOKEN") # GitHub Actions 会自动提供
STATS_FILE = "data/stats_history.csv"
STARS_CHART_FILE = "images/stars_chart.svg"
FORKS_CHART_FILE = "images/forks_chart.svg"
# --- 1. 获取当前 Star 和 Fork 总数 ---
def get_total_stats():
total_stars = 0
total_forks = 0
page = 1
while True:
url = f"https://api.github.com/users/{GITHUB_USERNAME}/repos?per_page=100&page={page}"
headers = {"Authorization": f"token {GITHUB_TOKEN}"}
response = requests.get(url, headers=headers)
response.raise_for_status() # 如果请求失败则抛出异常
repos = response.json()
if not repos:
break
for repo in repos:
total_stars += repo["stargazers_count"]
total_forks += repo["forks_count"]
page += 1
return total_stars, total_forks
# --- 2. 加载/更新历史数据 ---
def update_stats_history(stars, forks):
today = datetime.now().strftime("%Y-%m-%d")
new_data = {"date": [today], "total_stars": [stars], "total_forks": [forks]}
new_df = pd.DataFrame(new_data)
if os.path.exists(STATS_FILE):
history_df = pd.read_csv(STATS_FILE)
# 如果今天的数据已存在,则更新它,否则追加
if today in history_df["date"].values:
history_df.loc[history_df["date"] == today, ["total_stars", "total_forks"]] = [stars, forks]
else:
history_df = pd.concat([history_df, new_df], ignore_index=True)
else:
os.makedirs(os.path.dirname(STATS_FILE), exist_ok=True)
history_df = new_df
history_df["date"] = pd.to_datetime(history_df["date"]) # 确保日期是 datetime 类型
history_df = history_df.sort_values(by="date") # 按日期排序
history_df["date"] = history_df["date"].dt.strftime("%Y-%m-%d") # 转回字符串保存
history_df.to_csv(STATS_FILE, index=False)
return pd.read_csv(STATS_FILE, parse_dates=["date"]) # 返回解析日期后的 DataFrame 用于绘图
# --- 3. 生成图表 ---
def generate_plot(df, column, title, filename):
plt.style.use('seaborn-v0_8-darkgrid') # 使用一个好看的样式
fig, ax = plt.subplots(figsize=(10, 5)) # 调整图像大小
ax.plot(df["date"], df[column], marker='o', linestyle='-', color='skyblue', linewidth=2, markersize=5)
# 设置标题和标签
ax.set_title(f"{GITHUB_USERNAME}'s {title} Over Time", fontsize=16, color='white')
ax.set_xlabel("Date", fontsize=12, color='lightgray')
ax.set_ylabel(title, fontsize=12, color='lightgray')
# 美化日期格式
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m-%d'))
ax.xaxis.set_major_locator(mdates.AutoDateLocator(minticks=5, maxticks=10)) # 自动选择合适的日期间隔
plt.xticks(rotation=30, ha='right', color='lightgray')
plt.yticks(color='lightgray')
# 设置背景和边框颜色
fig.patch.set_facecolor('#24292e') # GitHub 暗色背景
ax.set_facecolor('#2d333b')
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.spines['bottom'].set_color('gray')
ax.spines['left'].set_color('gray')
ax.tick_params(axis='x', colors='lightgray')
ax.tick_params(axis='y', colors='lightgray')
plt.grid(True, linestyle='--', alpha=0.5, color='gray') # 网格线
plt.tight_layout() # 自动调整子图参数,使其填充整个图像区域
os.makedirs(os.path.dirname(filename), exist_ok=True)
plt.savefig(filename, facecolor=fig.get_facecolor(), transparent=False) # 保存 SVG
plt.close()
print(f"Chart saved to {filename}")
# --- Main ---
if __name__ == "__main__":
if not GITHUB_USERNAME or not GITHUB_TOKEN:
print("Error: GITHUB_ACTOR and GITHUB_TOKEN environment variables must be set.")
exit(1)
current_stars, current_forks = get_total_stats()
print(f"Current total stars: {current_stars}, total forks: {current_forks}")
history_df = update_stats_history(current_stars, current_forks)
generate_plot(history_df, "total_stars", "Total Stars", STARS_CHART_FILE)
generate_plot(history_df, "total_forks", "Total Forks", FORKS_CHART_FILE)
print("Stats updated and charts generated successfully!")
3. GitHub Actions Workflow (.github/workflows/update_stats.yml
)
这个 workflow 会:
- 每天定时运行(或手动触发)。
- Checkout 代码。
- 设置 Python 环境并安装依赖。
- 运行
generate_chart.py
脚本。 - 提交并推送更新后的
stats_history.csv
和图表文件。
yaml
name: Update Profile Stats Charts
on:
schedule:
- cron: '0 0 * * *' # 每天午夜 UTC 时间运行
workflow_dispatch: # 允许手动触发
jobs:
update_charts:
runs-on: ubuntu-latest
permissions:
contents: write # 需要写入权限来提交文件
actions: read # GITHUB_TOKEN 默认有 actions:read
# GITHUB_TOKEN 默认也有 contents:read
# 显式声明可以确保权限
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install requests pandas matplotlib
- name: Run script to generate charts
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # 由 GitHub Actions 自动提供
# GITHUB_ACTOR 会自动设置为触发 workflow 的用户,即你自己
run: python scripts/generate_chart.py
- name: Commit and push changes
run: |
git config --local user.email "[email protected]"
git config --local user.name "GitHub Action"
# 检查是否有文件变动
if ! git diff --quiet data/stats_history.csv images/stars_chart.svg images/forks_chart.svg; then
git add data/stats_history.csv images/stars_chart.svg images/forks_chart.svg
git commit -m "📊 Update profile stats charts and data"
git push
else
echo "No changes to commit."
fi
4. 在 README.md 中展示图表
在你的 用户名/用户名/README.md
文件中添加以下内容:
markdown
### 🚀 My GitHub Stats Over Time
**Total Stars ⭐**
<p align="center">
<img src="./images/stars_chart.svg" alt="My Total Stars History" />
</p>
**Total Forks 🍴**
<p align="center">
<img src="./images/forks_chart.svg" alt="My Total Forks History" />
</p>
<!-- 你也可以使用 GitHub 的缓存来避免浏览器缓存问题,但通常直接引用仓库内路径更好 -->
<!-- <img src="https://raw.githubusercontent.com/你的用户名/你的用户名/你的分支名/images/stars_chart.svg" /> -->
确保分支名正确,通常是 main
或 master
。
设置步骤:
- 创建文件和目录 :在你的
用户名/用户名
仓库中,按照上面的项目结构创建scripts/generate_chart.py
,.github/workflows/update_stats.yml
。data/
和images/
目录会在脚本第一次运行时自动创建(如果它们不存在且脚本有创建权限)。 - 修改
GITHUB_USERNAME
(可选) : 脚本中的GITHUB_USERNAME = os.environ.get("GITHUB_ACTOR")
会自动获取你的用户名。如果想硬编码,可以修改。 - 提交并推送这些新文件 到你的 GitHub 仓库。
- 触发 Workflow :
- 你可以等待
cron
计划的下一次运行。 - 或者,进入你的仓库 -> Actions 标签页 -> 找到 "Update Profile Stats Charts" workflow -> 点击 "Run workflow" 手动触发一次。
- 你可以等待
- 检查结果 :
- Workflow 运行成功后,
data/stats_history.csv
文件应该会被创建/更新。 images/stars_chart.svg
和images/forks_chart.svg
应该会被生成。- 这些文件会被自动 commit 和 push 到你的仓库。
- 你的 README.md 应该会显示这些图表。
- Workflow 运行成功后,
"Fancy" 提示:
- Matplotlib 样式 :
plt.style.use()
可以使用很多内置样式 (ggplot
,seaborn-v0_8-darkgrid
,fivethirtyeight
等)。你也可以自定义颜色、字体、线条粗细等。 - SVG 优势: SVG 是矢量图,放大不失真,而且可以直接用文本编辑器修改(如果你懂 SVG 语法)。
- 颜色主题: 可以根据你的 GitHub 主页主题(亮色/暗色)调整图表的背景色、文字颜色等。上面脚本中的颜色是为暗色主题优化的。
- 平滑曲线 : 可以使用
scipy.interpolate.make_interp_spline
等库来生成更平滑的曲线,但这会增加脚本的复杂性。 - 渐变填充: Matplotlib 也支持在曲线下方进行渐变颜色填充,可以让图表更美观。
注意事项:
- API Rate Limits : GitHub API 有速率限制。对于个人用户,通常是每小时 5000 次请求(使用
GITHUB_TOKEN
)。获取所有仓库信息通常只需要几次请求(取决于你有多少仓库,per_page=100
)。每天运行一次脚本是完全没问题的。 - 首次运行 : 第一次运行时,
stats_history.csv
不存在,图表上只有一个数据点。随着时间推移,曲线才会显现。 - 安全性 :
GITHUB_TOKEN
是由 GitHub Actions 自动提供的,并且具有适当的权限,无需手动创建。 - 分支保护: 如果你的主分支受保护,你可能需要调整 GitHub Actions 的权限或分支保护规则,以允许 Action 推送更改。