977 lines
35 KiB
YAML
977 lines
35 KiB
YAML
name: 📊 代码统计徽章
|
||
|
||
on:
|
||
push:
|
||
branches: [main, master]
|
||
workflow_dispatch:
|
||
|
||
# ==========================================
|
||
# 🔧 配置区域 - 根据你的项目修改
|
||
# ==========================================
|
||
env:
|
||
# ===== Token 配置 =====
|
||
# 建议在 Settings -> Secrets 中配置 STATS_TOKEN 以获得更好的权限控制
|
||
ACCESS_TOKEN: ${{ secrets.WORKFLOW }}
|
||
|
||
# ===== 工作区配置 =====
|
||
# 完整克隆的工作目录
|
||
WORKSPACE_DIR: "/home/workspace"
|
||
|
||
# ===== 分支配置 =====
|
||
# 徽章数据存储分支(可配置)
|
||
BADGE_BRANCH: "stats"
|
||
# 徽章文件存储目录
|
||
BADGE_DIR: "badges"
|
||
|
||
# ===== 排除配置 =====
|
||
# 全局排除的目录(这些目录会被完全忽略)
|
||
EXCLUDE_DIRS: "node_modules,dist,build,out,target,vendor,.venv,venv,__pycache__,.git,.github"
|
||
|
||
# 特殊包含规则(即使在排除目录中,也统计这些扩展名的文件)
|
||
# 格式: 目录:扩展名列表
|
||
# 例如: 'dist:js,css' 表示即使 dist 被排除,也统计其中的 .js 和 .css 文件
|
||
SPECIAL_INCLUDES: ""
|
||
# 示例: 'dist:js,css|vendor:go,mod'
|
||
|
||
# ===== 徽章颜色配置 =====
|
||
COLOR_TOTAL: "blue"
|
||
COLOR_FILES: "green"
|
||
COLOR_DEFAULT: "brightgreen"
|
||
|
||
# ===== 徽章样式配置 =====
|
||
BADGE_STYLE: "flat" # 可选: flat, flat-square, plastic, for-the-badge, social
|
||
|
||
# ===== 输出配置 =====
|
||
# 是否生成详细报告
|
||
GENERATE_DETAILED_REPORT: "true"
|
||
# 是否输出到 workflow summary
|
||
OUTPUT_TO_SUMMARY: "true"
|
||
# 最小代码行数阈值(低于此值的语言不生成徽章)
|
||
MIN_LINES_THRESHOLD: "10"
|
||
|
||
# ===== Git 配置 =====
|
||
GIT_USER_NAME: "ci[bot]"
|
||
GIT_USER_EMAIL: "ci[bot]@tinysoft.com.cn"
|
||
|
||
# ===== 平台配置 =====
|
||
# 平台类型: github 或 gitea
|
||
PLATFORM: "gitea"
|
||
# Git 服务器 URL(Gitea 示例: https://gitea.example.com)
|
||
GIT_SERVER_URL: "https://git.mytsl.cn"
|
||
# 仓库路径(格式: owner/repo)
|
||
REPO_PATH: "${{ github.repository }}"
|
||
# Raw 文件基础 URL
|
||
# GitHub: https://raw.githubusercontent.com/{owner}/{repo}/{branch}/{path}
|
||
# Gitea: https://gitea.example.com/{owner}/{repo}/raw/branch/{branch}/{path}
|
||
RAW_URL_BASE: 'https://git.mytsl.cn/${{ github.repository }}/raw/branch'
|
||
|
||
# ==========================================
|
||
# 🎨 语言分组配置
|
||
# 格式: 组名:后缀列表:显示名称:颜色:图标(可选)
|
||
# 图标使用 simple-icons 的名称,如 cplusplus, typescript 等
|
||
# ==========================================
|
||
LANGUAGE_GROUPS: |
|
||
cpp:hpp,cpp,cxx,cc,h,c:C/C++:00599C:cplusplus
|
||
python:py:Python:3572A5:python
|
||
typescript:ts,tsx:TypeScript:3178c6:typescript
|
||
javascript:js,jsx:JavaScript:F7DF1E:javascript
|
||
java:java:Java:007396:java
|
||
go:go:Go:00ADD8:go
|
||
rust:rs:Rust:CE412B:rust
|
||
shell:sh,bash:Shell:4EAA25:gnubash
|
||
yaml:yml,yaml:YAML:CB171E
|
||
tsf:tsl,tsf:TSF:9945FF
|
||
|
||
jobs:
|
||
update-stats:
|
||
runs-on: ubuntu-22.04
|
||
permissions:
|
||
contents: write
|
||
|
||
steps:
|
||
- name: 🔍 验证 Token 配置
|
||
id: validate_token
|
||
run: |
|
||
echo "🔐 验证访问令牌..."
|
||
|
||
if [ -z "${{ env.ACCESS_TOKEN }}" ]; then
|
||
echo "❌ 错误: 未配置访问令牌"
|
||
echo "请在 Settings -> Secrets 中配置 STATS_TOKEN 或确保 GITHUB_TOKEN 可用"
|
||
exit 1
|
||
fi
|
||
|
||
# 检测使用的是哪个 token
|
||
if [ -n "${{ secrets.STATS_TOKEN }}" ]; then
|
||
echo "✅ 使用自定义 STATS_TOKEN"
|
||
echo "token_type=STATS_TOKEN" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "✅ 使用默认 GITHUB_TOKEN"
|
||
echo "token_type=GITHUB_TOKEN" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
echo "🔗 仓库: ${{ github.repository }}"
|
||
echo "🌿 分支: ${{ github.ref_name }}"
|
||
echo "📝 提交: ${GITHUB_SHA:0:7}"
|
||
|
||
- name: 🔧 检查必需工具
|
||
id: check_deps
|
||
run: |
|
||
echo "======================================"
|
||
echo "🔍 检查依赖工具"
|
||
echo "======================================"
|
||
|
||
MISSING_TOOLS=()
|
||
|
||
# 检查 bc(用于数字计算)
|
||
if ! command -v bc &> /dev/null; then
|
||
echo "⚠️ bc 未安装"
|
||
MISSING_TOOLS+=("bc")
|
||
else
|
||
echo "✓ bc 已安装"
|
||
fi
|
||
|
||
# 检查 jq(用于 JSON 处理)
|
||
if ! command -v jq &> /dev/null; then
|
||
echo "⚠️ jq 未安装"
|
||
MISSING_TOOLS+=("jq")
|
||
else
|
||
echo "✓ jq 已安装"
|
||
fi
|
||
|
||
# 如果有缺失的工具,安装它们
|
||
if [ ${#MISSING_TOOLS[@]} -gt 0 ]; then
|
||
echo ""
|
||
echo "📦 安装缺失的工具: ${MISSING_TOOLS[*]}"
|
||
|
||
# 🔧 智能判断是否需要 sudo
|
||
# Docker 环境通常以 root 运行,不需要 sudo
|
||
if [ "$EUID" -eq 0 ] || [ "$(id -u)" -eq 0 ]; then
|
||
# 当前是 root 用户,直接执行
|
||
echo "🔑 检测到 root 权限,直接安装"
|
||
apt-get update -qq
|
||
apt-get install -y -qq "${MISSING_TOOLS[@]}"
|
||
elif command -v sudo &> /dev/null; then
|
||
# 有 sudo 命令,使用 sudo
|
||
echo "🔑 使用 sudo 安装"
|
||
sudo apt-get update -qq
|
||
sudo apt-get install -y -qq "${MISSING_TOOLS[@]}"
|
||
else
|
||
# 既不是 root 也没有 sudo
|
||
echo "❌ 错误:没有足够权限安装工具"
|
||
echo "请在 Docker 镜像中预装以下工具:"
|
||
echo " ${MISSING_TOOLS[*]}"
|
||
echo ""
|
||
echo "方案1: 使用包含这些工具的镜像"
|
||
echo "方案2: 在 Dockerfile 中添加:"
|
||
echo " RUN apt-get update && apt-get install -y bc jq"
|
||
exit 1
|
||
fi
|
||
|
||
echo "installed=true" >> $GITHUB_OUTPUT
|
||
echo "✅ 工具安装完成"
|
||
else
|
||
echo "installed=false" >> $GITHUB_OUTPUT
|
||
echo "✅ 所有工具已就绪"
|
||
fi
|
||
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 📥 克隆主仓库
|
||
id: clone_main
|
||
run: |
|
||
echo "======================================"
|
||
echo "🚀 开始准备主仓库"
|
||
echo "======================================"
|
||
|
||
REPO_NAME="${{ github.event.repository.name }}"
|
||
REPO_DIR="${{ env.WORKSPACE_DIR }}/$REPO_NAME"
|
||
|
||
echo "📁 仓库名称: $REPO_NAME"
|
||
echo "📍 目标目录: $REPO_DIR"
|
||
echo "🌐 服务器: ${GITHUB_SERVER_URL}"
|
||
echo "🌿 分支: ${{ github.ref_name }}"
|
||
echo ""
|
||
|
||
# 检查仓库状态
|
||
if [ -d "$REPO_DIR" ]; then
|
||
echo "📂 目录已存在,检查 Git 仓库状态..."
|
||
|
||
if [ -d "$REPO_DIR/.git" ]; then
|
||
# 目录存在且是有效的 Git 仓库
|
||
echo "✓ 发现有效的 Git 仓库,执行增量更新..."
|
||
cd "$REPO_DIR"
|
||
|
||
# 清理工作区
|
||
git clean -fdx
|
||
git reset --hard
|
||
|
||
# 获取最新代码
|
||
echo "📥 拉取最新代码..."
|
||
git fetch --all --tags --force
|
||
|
||
echo "✓ 仓库已更新"
|
||
else
|
||
# 目录存在但不是 Git 仓库(可能之前运行失败)
|
||
echo "⚠️ 目录存在但不是有效的 Git 仓库"
|
||
echo "🧹 清理损坏的目录..."
|
||
rm -rf "$REPO_DIR"
|
||
echo "✓ 已清理"
|
||
|
||
# 重新克隆
|
||
echo "📥 克隆仓库..."
|
||
mkdir -p "${{ env.WORKSPACE_DIR }}"
|
||
|
||
git clone \
|
||
https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \
|
||
"$REPO_DIR"
|
||
|
||
if [ $? -ne 0 ]; then
|
||
echo "❌ 克隆失败"
|
||
exit 1
|
||
fi
|
||
|
||
cd "$REPO_DIR"
|
||
echo "✓ 仓库已克隆"
|
||
fi
|
||
else
|
||
# 目录不存在,首次克隆
|
||
echo "📥 克隆仓库(首次)..."
|
||
mkdir -p "${{ env.WORKSPACE_DIR }}"
|
||
|
||
git clone \
|
||
https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git \
|
||
"$REPO_DIR"
|
||
|
||
if [ $? -ne 0 ]; then
|
||
echo "❌ 克隆失败"
|
||
exit 1
|
||
fi
|
||
|
||
cd "$REPO_DIR"
|
||
echo "✓ 仓库已克隆"
|
||
fi
|
||
|
||
# 切换到目标分支
|
||
echo "🏷️ 切换到分支: ${{ github.ref_name }}"
|
||
git checkout -f ${{ github.ref_name }}
|
||
|
||
echo "✅ 仓库准备成功"
|
||
echo ""
|
||
|
||
# 配置 Git 用户信息
|
||
echo "⚙️ 配置 Git 用户信息..."
|
||
git config user.name "${{ env.GIT_USER_NAME }}"
|
||
git config user.email "${{ env.GIT_USER_EMAIL }}"
|
||
|
||
# 显示仓库信息
|
||
echo "📊 仓库信息:"
|
||
echo " - 当前分支: $(git branch --show-current)"
|
||
echo " - 最新提交: $(git log -1 --oneline)"
|
||
echo " - 远程地址: $(git remote get-url origin | sed 's/oauth2:.*@/oauth2:***@/')"
|
||
echo ""
|
||
|
||
# 导出环境变量
|
||
echo "REPO_DIR=$REPO_DIR" >> $GITHUB_ENV
|
||
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV
|
||
|
||
echo "✅ 主仓库准备完成"
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: ⚙️ 准备统计分支
|
||
id: prepare_stats_branch
|
||
run: |
|
||
echo "======================================"
|
||
echo "🔧 准备统计分支"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 获取所有远程分支
|
||
git fetch origin --prune
|
||
|
||
# 检查统计分支是否存在
|
||
if git ls-remote --heads origin ${{ env.BADGE_BRANCH }} | grep -q ${{ env.BADGE_BRANCH }}; then
|
||
echo "✅ 统计分支 '${{ env.BADGE_BRANCH }}' 已存在"
|
||
|
||
# 检出统计分支
|
||
git fetch origin ${{ env.BADGE_BRANCH }}:${{ env.BADGE_BRANCH }}
|
||
git checkout ${{ env.BADGE_BRANCH }}
|
||
|
||
echo "📂 当前分支内容:"
|
||
ls -la
|
||
|
||
# 返回主分支
|
||
git checkout ${{ github.ref_name }}
|
||
|
||
echo "branch_exists=true" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "🆕 统计分支 '${{ env.BADGE_BRANCH }}' 不存在,将创建"
|
||
echo "branch_exists=false" >> $GITHUB_OUTPUT
|
||
fi
|
||
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 🌱 创建统计分支
|
||
if: steps.prepare_stats_branch.outputs.branch_exists == 'false'
|
||
run: |
|
||
echo "======================================"
|
||
echo "🆕 创建新的统计分支"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 🔴 安全检查:确认在正确的目录
|
||
echo "🔍 当前目录: $(pwd)"
|
||
echo "🔍 .git 状态: $([ -d .git ] && echo '✓ 存在' || echo '✗ 不存在')"
|
||
|
||
if [ ! -d .git ]; then
|
||
echo "❌ 错误:.git 目录不存在,无法创建分支"
|
||
exit 1
|
||
fi
|
||
|
||
# 创建孤立分支(orphan branch)
|
||
# 📝 说明:孤立分支与主分支没有共同的提交历史,适合存储独立的数据
|
||
echo "🌱 创建孤立分支..."
|
||
git checkout --orphan ${{ env.BADGE_BRANCH }}
|
||
|
||
# 🔴 再次确认 .git 仍然存在
|
||
if [ ! -d .git ]; then
|
||
echo "❌ 严重错误:.git 目录丢失!"
|
||
exit 1
|
||
fi
|
||
|
||
# 🧹 清空所有文件(但保留 .git 目录)
|
||
# ❓ 为什么要删除?
|
||
# - stats 分支是独立的数据存储分支,不需要主分支的代码文件
|
||
# - 只需要存储徽章 JSON 文件和统计报告
|
||
# - 保持分支干净,避免混淆
|
||
#
|
||
# ✅ 能否复用仓库?
|
||
# - 可以!.git 目录包含所有分支的完整信息
|
||
# - 切换到主分支时,主分支的文件会恢复
|
||
# - 各分支独立,互不影响
|
||
|
||
echo "🧹 清理文件(保护 .git 和 .gitea)..."
|
||
|
||
# 步骤 1: 使用 git rm 清理已跟踪的文件
|
||
git rm -rf . 2>/dev/null || true
|
||
|
||
# 步骤 2: 安全地删除剩余文件
|
||
# 使用更严格的保护,确保不会误删 .git
|
||
echo "🔍 查找需要删除的文件..."
|
||
find . -maxdepth 1 -type f ! -name '.gitignore' -delete 2>/dev/null || true
|
||
find . -maxdepth 1 -type d ! -name '.' ! -name '..' ! -name '.git' ! -name '.gitea' -exec rm -rf {} + 2>/dev/null || true
|
||
|
||
# 🔴 最终检查:确认 .git 仍然完好
|
||
echo "🔍 最终检查..."
|
||
if [ ! -d .git ]; then
|
||
echo "❌ 致命错误:.git 目录在清理过程中被删除!"
|
||
echo "这不应该发生,请检查 find 命令"
|
||
exit 1
|
||
fi
|
||
echo "✓ .git 目录完好"
|
||
echo "✓ 文件清理完成"
|
||
echo ""
|
||
|
||
# 创建初始 README
|
||
cat > README.md << 'EOF'
|
||
# 📊 代码统计徽章数据
|
||
|
||
> 此分支由 GitHub Actions 自动生成和维护
|
||
>
|
||
> ⚠️ **请勿手动修改此分支的内容!**
|
||
|
||
## 📁 目录结构
|
||
|
||
```
|
||
badges/
|
||
├── total-lines.json # 总代码行数徽章
|
||
├── total-files.json # 总文件数徽章
|
||
├── {language}-lines.json # 各语言代码行数徽章
|
||
├── {language}-files.json # 各语言文件数徽章
|
||
└── README.md # 详细统计报告
|
||
```
|
||
|
||
## 🔄 更新机制
|
||
|
||
- ✅ 每次 push 到主分支时自动更新
|
||
- ✅ 可通过 workflow_dispatch 手动触发
|
||
|
||
## 📊 数据来源
|
||
|
||
所有统计数据基于主分支的最新代码自动生成。
|
||
|
||
---
|
||
|
||
*🤖 由 GitHub Actions 自动维护*
|
||
EOF
|
||
|
||
# 创建徽章目录
|
||
mkdir -p ${{ env.BADGE_DIR }}
|
||
|
||
# 添加并提交
|
||
git add .
|
||
git commit -m "chore: 初始化统计分支"
|
||
|
||
# 推送到远程
|
||
git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.BADGE_BRANCH }}
|
||
|
||
echo "✅ 统计分支创建完成"
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
# 🔴 重要:返回主分支,后续统计需要在主分支进行
|
||
echo "🔄 切换回主分支: ${{ github.ref_name }}"
|
||
git checkout ${{ github.ref_name }}
|
||
echo "✓ 已切换回主分支"
|
||
echo ""
|
||
|
||
- name: 📊 统计总代码量
|
||
id: total
|
||
run: |
|
||
echo "======================================"
|
||
echo "📊 统计总代码量"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 🔴 重要:确保在主分支进行统计(不要统计 stats 分支)
|
||
echo "🔍 确认当前分支..."
|
||
CURRENT_BRANCH=$(git branch --show-current)
|
||
if [ "$CURRENT_BRANCH" != "${{ github.ref_name }}" ]; then
|
||
echo "⚠️ 当前在分支: $CURRENT_BRANCH,切换到主分支: ${{ github.ref_name }}"
|
||
git checkout ${{ github.ref_name }}
|
||
else
|
||
echo "✓ 已在主分支: $CURRENT_BRANCH"
|
||
fi
|
||
echo ""
|
||
|
||
# 构建排除参数
|
||
EXCLUDE_PARAMS=""
|
||
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
|
||
for dir in "${EXCLUDES[@]}"; do
|
||
EXCLUDE_PARAMS="$EXCLUDE_PARAMS -not -path '*/$dir/*'"
|
||
done
|
||
|
||
# 获取所有文件
|
||
echo "🔍 扫描文件..."
|
||
|
||
# 统计总文件数
|
||
TOTAL_FILES=$(eval "find . -type f $EXCLUDE_PARAMS" | wc -l)
|
||
|
||
# 统计总代码行数(排除空行)
|
||
TOTAL_CODE=$(eval "find . -type f $EXCLUDE_PARAMS -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null" | awk -F: '{sum+=$2} END {print sum+0}')
|
||
|
||
echo "📊 统计结果:"
|
||
echo " - 总文件数: $TOTAL_FILES"
|
||
echo " - 总代码行: $TOTAL_CODE"
|
||
|
||
# 格式化数字(添加千分位)
|
||
FORMATTED_FILES=$(printf "%'d" $TOTAL_FILES)
|
||
FORMATTED_CODE=$(printf "%'d" $TOTAL_CODE)
|
||
|
||
# 输出到环境变量
|
||
echo "total_files=$TOTAL_FILES" >> $GITHUB_OUTPUT
|
||
echo "total_code=$TOTAL_CODE" >> $GITHUB_OUTPUT
|
||
echo "formatted_files=$FORMATTED_FILES" >> $GITHUB_OUTPUT
|
||
echo "formatted_code=$FORMATTED_CODE" >> $GITHUB_OUTPUT
|
||
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 📋 统计各语言代码量
|
||
id: languages
|
||
run: |
|
||
echo "======================================"
|
||
echo "📋 统计各语言代码量"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 🔴 重要:确保在主分支进行统计
|
||
echo "🔍 确认当前分支..."
|
||
CURRENT_BRANCH=$(git branch --show-current)
|
||
if [ "$CURRENT_BRANCH" != "${{ github.ref_name }}" ]; then
|
||
echo "⚠️ 当前在分支: $CURRENT_BRANCH,切换到主分支: ${{ github.ref_name }}"
|
||
git checkout ${{ github.ref_name }}
|
||
else
|
||
echo "✓ 已在主分支: $CURRENT_BRANCH"
|
||
fi
|
||
echo ""
|
||
|
||
# 创建临时目录
|
||
mkdir -p /tmp/lang_stats
|
||
|
||
# 构建排除参数
|
||
EXCLUDE_PARAMS=""
|
||
IFS=',' read -ra EXCLUDES <<< "${{ env.EXCLUDE_DIRS }}"
|
||
for dir in "${EXCLUDES[@]}"; do
|
||
EXCLUDE_PARAMS="$EXCLUDE_PARAMS -not -path '*/$dir/*'"
|
||
done
|
||
|
||
LANGUAGE_COUNT=0
|
||
|
||
# 读取语言配置并统计
|
||
echo "${{ env.LANGUAGE_GROUPS }}" | while IFS= read -r line; do
|
||
[ -z "$line" ] && continue
|
||
|
||
# 解析配置行: 组名:后缀列表:显示名称:颜色:图标
|
||
IFS=':' read -r lang_id extensions display_name color icon <<< "$line"
|
||
|
||
echo "🔍 统计 $display_name..."
|
||
|
||
# 构建扩展名查找条件
|
||
FIND_CONDITIONS=""
|
||
IFS=',' read -ra EXTS <<< "$extensions"
|
||
for ext in "${EXTS[@]}"; do
|
||
if [ -z "$FIND_CONDITIONS" ]; then
|
||
FIND_CONDITIONS="-name '*.$ext'"
|
||
else
|
||
FIND_CONDITIONS="$FIND_CONDITIONS -o -name '*.$ext'"
|
||
fi
|
||
done
|
||
|
||
# 统计文件数
|
||
FILE_COUNT=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS" | wc -l)
|
||
|
||
if [ "$FILE_COUNT" -gt 0 ]; then
|
||
# 统计代码行数
|
||
CODE_LINES=$(eval "find . -type f \( $FIND_CONDITIONS \) $EXCLUDE_PARAMS -exec grep -cHv '^[[:space:]]*$' {} + 2>/dev/null" | awk -F: '{sum+=$2} END {print sum+0}')
|
||
|
||
# 只保存超过阈值的语言
|
||
if [ "$CODE_LINES" -ge "${{ env.MIN_LINES_THRESHOLD }}" ]; then
|
||
FORMATTED_LINES=$(printf "%'d" $CODE_LINES)
|
||
|
||
echo " - 文件数: $FILE_COUNT"
|
||
echo " - 代码行: $FORMATTED_LINES"
|
||
|
||
# 保存到临时文件
|
||
echo "$lang_id|$display_name|$CODE_LINES|$FORMATTED_LINES|$FILE_COUNT|$color|$icon" >> /tmp/lang_stats/${lang_id}.txt
|
||
LANGUAGE_COUNT=$((LANGUAGE_COUNT + 1))
|
||
else
|
||
echo " ⏩ 跳过(少于 ${{ env.MIN_LINES_THRESHOLD }} 行)"
|
||
fi
|
||
else
|
||
echo " ⏩ 未找到文件"
|
||
fi
|
||
echo ""
|
||
done
|
||
|
||
# 汇总所有语言数据
|
||
cat /tmp/lang_stats/*.txt 2>/dev/null | sort -t'|' -k3 -nr > /tmp/lang_summary.txt || touch /tmp/lang_summary.txt
|
||
|
||
# 计算实际找到的语言数
|
||
ACTUAL_COUNT=$(wc -l < /tmp/lang_summary.txt)
|
||
|
||
echo "language_count=$ACTUAL_COUNT" >> $GITHUB_OUTPUT
|
||
|
||
echo "✅ 找到 $ACTUAL_COUNT 种语言"
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 🎨 生成徽章数据
|
||
id: generate_badges
|
||
run: |
|
||
echo "======================================"
|
||
echo "🎨 生成徽章数据"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 确保在统计分支
|
||
git checkout ${{ env.BADGE_BRANCH }}
|
||
|
||
# 确保徽章目录存在
|
||
mkdir -p ${{ env.BADGE_DIR }}
|
||
|
||
GENERATED_COUNT=0
|
||
|
||
# 生成总代码行数徽章
|
||
echo "📊 生成总代码行数徽章..."
|
||
cat > ${{ env.BADGE_DIR }}/total-lines.json << EOF
|
||
{
|
||
"schemaVersion": 1,
|
||
"label": "代码",
|
||
"message": "${{ steps.total.outputs.formatted_code }} 行",
|
||
"color": "${{ env.COLOR_TOTAL }}",
|
||
"style": "${{ env.BADGE_STYLE }}"
|
||
}
|
||
EOF
|
||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
||
|
||
# 生成总文件数徽章
|
||
echo "📁 生成总文件数徽章..."
|
||
cat > ${{ env.BADGE_DIR }}/total-files.json << EOF
|
||
{
|
||
"schemaVersion": 1,
|
||
"label": "文件",
|
||
"message": "${{ steps.total.outputs.formatted_files }} 个",
|
||
"color": "${{ env.COLOR_FILES }}",
|
||
"style": "${{ env.BADGE_STYLE }}"
|
||
}
|
||
EOF
|
||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
||
|
||
# 生成各语言徽章
|
||
if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then
|
||
echo ""
|
||
echo "🌐 生成语言徽章..."
|
||
|
||
while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do
|
||
[ -z "$lang_id" ] && continue
|
||
|
||
echo " - $display_name"
|
||
|
||
# 生成代码行数徽章(使用 heredoc)
|
||
if [ -n "$icon" ]; then
|
||
# 带图标的徽章
|
||
cat > ${{ env.BADGE_DIR }}/${lang_id}-lines.json << EOFJSON
|
||
{
|
||
"schemaVersion": 1,
|
||
"label": "$display_name",
|
||
"message": "$formatted_lines 行",
|
||
"color": "$color",
|
||
"style": "${{ env.BADGE_STYLE }}",
|
||
"namedLogo": "$icon"
|
||
}
|
||
EOFJSON
|
||
else
|
||
# 不带图标的徽章
|
||
cat > ${{ env.BADGE_DIR }}/${lang_id}-lines.json << EOFJSON
|
||
{
|
||
"schemaVersion": 1,
|
||
"label": "$display_name",
|
||
"message": "$formatted_lines 行",
|
||
"color": "$color",
|
||
"style": "${{ env.BADGE_STYLE }}"
|
||
}
|
||
EOFJSON
|
||
fi
|
||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
||
|
||
# 生成文件数徽章
|
||
cat > ${{ env.BADGE_DIR }}/${lang_id}-files.json << EOFJSON
|
||
{
|
||
"schemaVersion": 1,
|
||
"label": "$display_name 文件",
|
||
"message": "$file_count 个",
|
||
"color": "$color",
|
||
"style": "${{ env.BADGE_STYLE }}"
|
||
}
|
||
EOFJSON
|
||
GENERATED_COUNT=$((GENERATED_COUNT + 1))
|
||
done < /tmp/lang_summary.txt
|
||
fi
|
||
|
||
echo ""
|
||
echo "generated_count=$GENERATED_COUNT" >> $GITHUB_OUTPUT
|
||
echo "✅ 已生成 $GENERATED_COUNT 个徽章"
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 📝 生成详细统计报告
|
||
if: env.GENERATE_DETAILED_REPORT == 'true'
|
||
run: |
|
||
echo "======================================"
|
||
echo "📝 生成详细统计报告"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 确保在统计分支
|
||
git checkout ${{ env.BADGE_BRANCH }}
|
||
|
||
# 生成 README
|
||
cat > ${{ env.BADGE_DIR }}/README.md << 'EOFMD'
|
||
# 📊 代码统计详细报告
|
||
|
||
> 🤖 由 GitHub Actions 自动生成
|
||
>
|
||
> 📅 更新时间: TIMESTAMP_PLACEHOLDER
|
||
|
||
## 📈 总体统计
|
||
|
||
| 统计项 | 数值 | 徽章 |
|
||
|--------|------|------|
|
||
| 💻 总代码行数 | **TOTAL_CODE_PLACEHOLDER** 行 |  |
|
||
| 📁 总文件数 | **TOTAL_FILES_PLACEHOLDER** 个 |  |
|
||
| 🌐 语言种类 | **LANG_COUNT_PLACEHOLDER** 种 | - |
|
||
|
||
## 🌈 语言分布
|
||
|
||
EOFMD
|
||
|
||
# 替换占位符
|
||
sed -i "s|TIMESTAMP_PLACEHOLDER|$(date -u '+%Y-%m-%d %H:%M:%S UTC')|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|TOTAL_CODE_PLACEHOLDER|${{ steps.total.outputs.formatted_code }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|TOTAL_FILES_PLACEHOLDER|${{ steps.total.outputs.formatted_files }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|LANG_COUNT_PLACEHOLDER|${{ steps.languages.outputs.language_count }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|REPO_PLACEHOLDER|${{ github.repository }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|BRANCH_PLACEHOLDER|${{ env.BADGE_BRANCH }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|BADGE_DIR_PLACEHOLDER|${{ env.BADGE_DIR }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
sed -i "s|RAW_URL_PLACEHOLDER|${{ env.RAW_URL_BASE }}|g" ${{ env.BADGE_DIR }}/README.md
|
||
|
||
# 添加语言统计表格
|
||
if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then
|
||
cat >> ${{ env.BADGE_DIR }}/README.md << 'EOFTABLE'
|
||
| 语言 | 代码行数 | 文件数 | 占比 | 徽章 |
|
||
|------|----------|--------|------|------|
|
||
EOFTABLE
|
||
|
||
TOTAL_CODE=${{ steps.total.outputs.total_code }}
|
||
while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do
|
||
[ -z "$lang_id" ] && continue
|
||
|
||
if [ "$TOTAL_CODE" -gt 0 ]; then
|
||
PERCENT=$(echo "scale=1; $code_lines * 100 / $TOTAL_CODE" | bc)
|
||
else
|
||
PERCENT="0.0"
|
||
fi
|
||
|
||
cat >> ${{ env.BADGE_DIR }}/README.md << EOFLANG
|
||
| **${display_name}** | ${formatted_lines} | ${file_count} | ${PERCENT}% |  |
|
||
EOFLANG
|
||
done < /tmp/lang_summary.txt
|
||
fi
|
||
|
||
# 添加配置说明
|
||
cat >> ${{ env.BADGE_DIR }}/README.md << EOFMD
|
||
|
||
---
|
||
|
||
## ⚙️ 配置说明
|
||
|
||
### Token 配置
|
||
|
||
- 当前使用: **${{ steps.validate_token.outputs.token_type }}**
|
||
- 推荐配置自定义 `STATS_TOKEN` 以获得更好的权限控制
|
||
|
||
### 排除规则
|
||
|
||
当前排除的目录:
|
||
```
|
||
${{ env.EXCLUDE_DIRS }}
|
||
```
|
||
|
||
### 阈值设置
|
||
|
||
- 最小代码行数阈值: **${{ env.MIN_LINES_THRESHOLD }}** 行
|
||
- 低于此阈值的语言将不会生成徽章
|
||
|
||
### 徽章样式
|
||
|
||
- 当前样式: **${{ env.BADGE_STYLE }}**
|
||
- 可选样式: flat, flat-square, plastic, for-the-badge, social
|
||
|
||
---
|
||
|
||
<div align="center">
|
||
|
||
*📊 由 [GitHub Actions](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions) 自动生成和更新*
|
||
|
||
*🤖 生成时间: $(date -u '+%Y-%m-%d %H:%M:%S UTC')*
|
||
|
||
</div>
|
||
EOFMD
|
||
|
||
echo "✅ 详细统计报告已生成"
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 📤 提交并推送到统计分支
|
||
id: commit
|
||
run: |
|
||
echo "======================================"
|
||
echo "📤 提交并推送更新"
|
||
echo "======================================"
|
||
|
||
cd "${{ env.REPO_DIR }}"
|
||
|
||
# 确保在统计分支
|
||
git checkout ${{ env.BADGE_BRANCH }}
|
||
|
||
# 配置 Git 用户信息
|
||
git config user.name "${{ env.GIT_USER_NAME }}"
|
||
git config user.email "${{ env.GIT_USER_EMAIL }}"
|
||
|
||
# 添加所有更改
|
||
git add ${{ env.BADGE_DIR }}/
|
||
|
||
# 检查是否有变更
|
||
if git diff --staged --quiet; then
|
||
echo "📌 没有变更,跳过提交"
|
||
echo "changed=false" >> $GITHUB_OUTPUT
|
||
echo ""
|
||
else
|
||
echo "📝 准备提交..."
|
||
|
||
# 生成提交信息
|
||
COMMIT_MSG="chore: 更新代码统计 [$(date '+%Y-%m-%d %H:%M')]
|
||
|
||
📊 统计摘要:
|
||
- 总代码: ${{ steps.total.outputs.formatted_code }} 行
|
||
- 总文件: ${{ steps.total.outputs.formatted_files }} 个
|
||
- 语言数: ${{ steps.languages.outputs.language_count }} 种
|
||
- 徽章数: ${{ steps.generate_badges.outputs.generated_count }} 个
|
||
|
||
🔗 触发提交: ${GITHUB_SHA:0:7}
|
||
🤖 由 GitHub Actions 自动生成"
|
||
|
||
git commit -m "$COMMIT_MSG"
|
||
|
||
echo "📤 推送到远程 ${{ env.BADGE_BRANCH }} 分支..."
|
||
|
||
# 推送到远程(使用 TOKEN)
|
||
git push https://oauth2:${{ env.ACCESS_TOKEN }}@${GITHUB_SERVER_URL#https://}/${{ github.repository }}.git ${{ env.BADGE_BRANCH }}
|
||
|
||
if [ $? -eq 0 ]; then
|
||
echo "✅ 推送成功"
|
||
echo "changed=true" >> $GITHUB_OUTPUT
|
||
else
|
||
echo "❌ 推送失败"
|
||
exit 1
|
||
fi
|
||
fi
|
||
|
||
echo "======================================"
|
||
echo ""
|
||
|
||
- name: 📈 生成 Workflow Summary
|
||
if: env.OUTPUT_TO_SUMMARY == 'true' && success()
|
||
run: |
|
||
cat >> $GITHUB_STEP_SUMMARY << 'EOFSUMMARY'
|
||
# 📊 代码统计完成
|
||
|
||
## ✅ 执行结果
|
||
|
||
| 项目 | 结果 |
|
||
|------|------|
|
||
| 🔐 Token 类型 | **${{ steps.validate_token.outputs.token_type }}** |
|
||
| 🔧 依赖检查 | ${{ steps.check_deps.outputs.installed == 'true' && '✅ 已安装缺失工具' || '✅ 所有工具就绪' }} |
|
||
| 📊 总代码行数 | **${{ steps.total.outputs.formatted_code }}** 行 |
|
||
| 📁 总文件数 | **${{ steps.total.outputs.formatted_files }}** 个 |
|
||
| 🌐 语言种类 | **${{ steps.languages.outputs.language_count }}** 种 |
|
||
| 🎨 生成徽章 | **${{ steps.generate_badges.outputs.generated_count }}** 个 |
|
||
| 📤 提交状态 | ${{ steps.commit.outputs.changed == 'true' && '✅ 已更新' || '📌 无变更' }} |
|
||
|
||
## 🔗 快速链接
|
||
|
||
- 📊 [查看详细统计报告](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/README.md)
|
||
- 🎨 [浏览徽章文件](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }})
|
||
- 🔧 [查看 Workflow 配置](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/blob/${{ github.ref_name }}/.github/workflows/update_stats_badge.yaml)
|
||
|
||
## 📝 语言分布
|
||
|
||
EOFSUMMARY
|
||
|
||
# 添加语言分布表格
|
||
if [ -f /tmp/lang_summary.txt ] && [ -s /tmp/lang_summary.txt ]; then
|
||
cat >> $GITHUB_STEP_SUMMARY << 'EOFTABLE'
|
||
| 语言 | 代码行数 | 文件数 | 占比 |
|
||
|------|----------|--------|------|
|
||
EOFTABLE
|
||
|
||
TOTAL_CODE=${{ steps.total.outputs.total_code }}
|
||
sort -t'|' -k3 -nr /tmp/lang_summary.txt | head -10 | while IFS='|' read -r lang_id display_name code_lines formatted_lines file_count color icon; do
|
||
[ -z "$lang_id" ] && continue
|
||
|
||
if [ "$TOTAL_CODE" -gt 0 ]; then
|
||
PERCENT=$(echo "scale=1; $code_lines * 100 / $TOTAL_CODE" | bc)
|
||
else
|
||
PERCENT="0.0"
|
||
fi
|
||
|
||
echo "| **${display_name}** | ${formatted_lines} | ${file_count} | ${PERCENT}% |" >> $GITHUB_STEP_SUMMARY
|
||
done
|
||
else
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "> ⚠️ 未找到符合条件的语言文件" >> $GITHUB_STEP_SUMMARY
|
||
fi
|
||
|
||
# 添加工作区信息
|
||
cat >> $GITHUB_STEP_SUMMARY << 'EOFWS'
|
||
|
||
## 📂 工作区信息
|
||
|
||
| 项目 | 值 |
|
||
|------|-----|
|
||
| 📁 仓库名称 | `${{ env.REPO_NAME }}` |
|
||
| 📍 工作目录 | `${{ env.REPO_DIR }}` |
|
||
| 🌿 统计分支 | `${{ env.BADGE_BRANCH }}` |
|
||
| 📂 徽章目录 | `${{ env.BADGE_DIR }}` |
|
||
|
||
EOFWS
|
||
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "---" >> $GITHUB_STEP_SUMMARY
|
||
echo "" >> $GITHUB_STEP_SUMMARY
|
||
echo "*⏱️ 执行时间: $(date '+%Y-%m-%d %H:%M:%S %Z')*" >> $GITHUB_STEP_SUMMARY
|
||
|
||
- name: 💡 输出使用说明
|
||
if: success()
|
||
run: |
|
||
echo "================================================"
|
||
echo "✅ 代码统计任务执行完成!"
|
||
echo "================================================"
|
||
echo ""
|
||
|
||
echo "🔐 Token 信息:"
|
||
echo " - 使用: ${{ steps.validate_token.outputs.token_type }}"
|
||
echo ""
|
||
|
||
# 根据实际结果输出
|
||
if [ "${{ steps.commit.outputs.changed }}" == "true" ]; then
|
||
echo "📊 统计结果:"
|
||
echo " - 总代码: ${{ steps.total.outputs.formatted_code }} 行"
|
||
echo " - 总文件: ${{ steps.total.outputs.formatted_files }} 个"
|
||
echo " - 语言数: ${{ steps.languages.outputs.language_count }} 种"
|
||
echo " - 徽章数: ${{ steps.generate_badges.outputs.generated_count }} 个"
|
||
echo ""
|
||
echo "📤 已推送更新到分支: ${{ env.BADGE_BRANCH }}"
|
||
else
|
||
echo "📌 统计数据无变更,未产生新的提交"
|
||
fi
|
||
|
||
echo ""
|
||
echo "📂 工作区信息:"
|
||
echo " - 仓库目录: ${{ env.REPO_DIR }}"
|
||
echo " - 统计分支: ${{ env.BADGE_BRANCH }}"
|
||
echo ""
|
||
echo "🔗 查看详细报告:"
|
||
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}/README.md"
|
||
echo ""
|
||
echo "🎨 徽章目录:"
|
||
echo " ${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/src/branch/${{ env.BADGE_BRANCH }}/${{ env.BADGE_DIR }}"
|
||
echo ""
|
||
echo "================================================"
|
||
|
||
# 如果有警告或特殊情况,也输出
|
||
if [ "${{ steps.check_deps.outputs.installed }}" == "true" ]; then
|
||
echo ""
|
||
echo "ℹ️ 本次运行安装了缺失的依赖工具"
|
||
fi
|
||
|
||
if [ "${{ steps.languages.outputs.language_count }}" == "0" ]; then
|
||
echo ""
|
||
echo "⚠️ 警告: 未找到符合条件的语言文件"
|
||
echo " 请检查 LANGUAGE_GROUPS 配置和 MIN_LINES_THRESHOLD 设置"
|
||
fi
|
||
|
||
# 清理工作区(可选)
|
||
if [ "${{ env.CLEANUP_WORKSPACE }}" == "true" ]; then
|
||
echo ""
|
||
echo "🧹 清理工作区..."
|
||
rm -rf "${{ env.WORKSPACE_DIR }}"
|
||
echo "✅ 清理完成"
|
||
fi
|
||
|
||
- name: 🧹 清理工作区
|
||
if: always()
|
||
run: |
|
||
echo "🧹 清理临时文件..."
|
||
rm -rf /tmp/lang_stats /tmp/lang_summary.txt /tmp/total_stats*.json
|
||
echo "✅ 清理完成"
|