diff --git a/config.sh b/config.sh index 1badea1..caa5443 100644 --- a/config.sh +++ b/config.sh @@ -27,4 +27,8 @@ SKIP_REPOS=${SKIP_REPOS:-"archive,AutoApiSecret, \ backup-openbilibili-go-common, \ carrot,ChatGLM-6B,dokploy,hub-mirror, \ Download-macOS, \ - songtianlun,songtianlun.github.io"} \ No newline at end of file + songtianlun,songtianlun.github.io"} + +# 系统配置 +LOG_FILE="$LOG_DIR/mirror-$(date '+%Y%m%d-%H%M%S').log" +STATS_FILE="$LOG_DIR/sync_stats-$(date '+%Y%m%d-%H%M%S').json" diff --git a/main.sh b/main.sh index 29a2d73..7687f85 100644 --- a/main.sh +++ b/main.sh @@ -15,7 +15,6 @@ NC='\033[0m' # 初始化日志 init_logging() { mkdir -p "$LOG_DIR" - LOG_FILE="$LOG_DIR/mirror-$(date '+%Y%m%d-%H%M%S').log" exec 1> >(tee -a "$LOG_FILE") exec 2> >(tee -a "$LOG_FILE" >&2) } @@ -49,7 +48,8 @@ main() { "$GITEA_USER" \ "$GITEA_TOKEN" \ "$WORK_DIR" \ - "$SKIP_REPOS" + "$SKIP_REPOS" \ + "$STATS_FILE" mirror_exit_code=$? @@ -57,11 +57,37 @@ main() { summary="GitHub to Gitea 同步报告 运行时间: $(date '+%Y-%m-%d %H:%M:%S') -同步状态: $([ $mirror_exit_code -eq 0 ] && echo "成功" || echo "失败") 详细日志: $(cat "${LOG_FILE}")" + if [ -f "$STATS_FILE" ]; then + stats=$(cat "$STATS_FILE") + summary="GitHub to Gitea 同步报告 + + 开始时间: $(echo "$stats" | jq -r '.start_time') + 结束时间: $(echo "$stats" | jq -r '.end_time') + 同步状态: $([ $mirror_exit_code -eq 0 ] && echo "成功" || echo "失败") + + 统计信息: + - 总仓库数: $(echo "$stats" | jq -r '.total_repos') + - 处理数量: $(echo "$stats" | jq -r '.processed') + - 成功数量: $(echo "$stats" | jq -r '.success') + - 失败数量: $(echo "$stats" | jq -r '.failed') + - 跳过数量: $(echo "$stats" | jq -r '.skipped') + + 跳过的仓库: + $(echo "$stats" | jq -r '.details.skipped_repos[]' | sed 's/^/- /') + + 失败的仓库: + $(echo "$stats" | jq -r '.details.failed_repos[]' | sed 's/^/- /') + + 详细日志 (最后 50 行): + $(tail -n 50 "$LOG_FILE")" + else + summary="无法获取同步统计信息" + fi + # 如果启用了邮件通知,调用 mail.sh if [ "$ENABLE_MAIL" = "true" ]; then subject="GitHub 同步$([ $mirror_exit_code -eq 0 ] && echo "成功" || echo "失败") - $(date '+%Y-%m-%d')" diff --git a/mirror.sh b/mirror.sh index 2f6a0c3..a99eeeb 100644 --- a/mirror.sh +++ b/mirror.sh @@ -8,45 +8,101 @@ GITEA_USER="$4" GITEA_TOKEN="$5" WORK_DIR="$6" SKIP_REPOS="$7" +STATS_FILE="$8" + +# 初始化统计 +init_stats() { + cat > "$STATS_FILE" << EOF +{ + "total_repos": 0, + "processed": 0, + "skipped": 0, + "success": 0, + "failed": 0, + "start_time": "$(date '+%Y-%m-%d %H:%M:%S')", + "end_time": "", + "details": { + "skipped_repos": [], + "success_repos": [], + "failed_repos": [] + } +} +EOF +} + +# 更新统计信息 +update_stats() { + local key="$1" + local value="$2" + local type="$3" # 可以是 number 或 array + + if [ "$type" = "array" ]; then + # 添加到数组 + jq --arg key "$key" --arg value "$value" \ + ".details[$key] += [\$value]" "$STATS_FILE" > "$STATS_FILE.tmp" + else + # 更新数值 + jq --arg key "$key" --arg value "$value" \ + ".[$key] = ($value | tonumber)" "$STATS_FILE" > "$STATS_FILE.tmp" + fi + mv "$STATS_FILE.tmp" "$STATS_FILE" +} # 同步单个仓库 sync_repository() { local repo="$1" + local success=true # 检查 Gitea 仓库是否存在 if ! curl -s -o /dev/null -f -H "Authorization: token $GITEA_TOKEN" \ "$GITEA_URL/api/v1/repos/$GITEA_USER/$repo"; then - echo "在 Gitea 上创建仓库 $repo" - curl -X POST -H "Authorization: token $GITEA_TOKEN" \ + if ! curl -X POST -H "Authorization: token $GITEA_TOKEN" \ -H "Content-Type: application/json" \ -d "{\"name\":\"$repo\",\"private\":false}" \ - "$GITEA_URL/api/v1/user/repos" + "$GITEA_URL/api/v1/user/repos"; then + success=false + fi fi # 克隆和推送 [ -d "$repo" ] && rm -rf "$repo" - git clone --mirror "https://${GITHUB_TOKEN:+$GITHUB_TOKEN@}github.com/$GITHUB_USER/$repo.git" "$repo" - cd "$repo" + if ! git clone --mirror "https://${GITHUB_TOKEN:+$GITHUB_TOKEN@}github.com/$GITHUB_USER/$repo.git" "$repo"; then + success=false + else + cd "$repo" - # 尝试 mirror 推送 - if ! git push --mirror "https://$GITEA_USER:$GITEA_TOKEN@${GITEA_URL#https://}/$GITEA_USER/$repo.git"; then - echo "mirror 推送失败,尝试逐个分支推送..." + # 尝试 mirror 推送 + if ! git push --mirror "https://$GITEA_USER:$GITEA_TOKEN@${GITEA_URL#https://}/$GITEA_USER/$repo.git"; then + echo "mirror 推送失败,尝试逐个分支推送..." - # 获取所有分支 - git fetch --all + # 获取所有分支 + git fetch --all - # 推送每个分支 - git for-each-ref --format='%(refname:short)' refs/heads/ | while read branch; do - echo "推送分支: $branch" - git push "https://$GITEA_USER:$GITEA_TOKEN@${GITEA_URL#https://}/$GITEA_USER/$repo.git" "$branch:$branch" - done + # 推送每个分支 + git for-each-ref --format='%(refname:short)' refs/heads/ | while read branch; do + echo "推送分支: $branch" + if ! git push "https://$GITEA_USER:$GITEA_TOKEN@${GITEA_URL#https://}/$GITEA_USER/$repo.git" "$branch:$branch"; then + success=false + fi + done - # 推送所有标签 - git push "https://$GITEA_USER:$GITEA_TOKEN@${GITEA_URL#https://}/$GITEA_USER/$repo.git" --tags + # 推送所有标签 + if ! git push "https://$GITEA_USER:$GITEA_TOKEN@${GITEA_URL#https://}/$GITEA_USER/$repo.git" --tags; then + success=false + fi + fi + cd .. fi - cd .. + # 更新统计 + if [ "$success" = true ]; then + update_stats "success" "$(( $(jq '.success' "$STATS_FILE") + 1 ))" "number" + update_stats "success_repos" "$repo" "array" + else + update_stats "failed" "$(( $(jq '.failed' "$STATS_FILE") + 1 ))" "number" + update_stats "failed_repos" "$repo" "array" + fi } # 主同步逻辑 @@ -54,22 +110,37 @@ main() { mkdir -p "$WORK_DIR" cd "$WORK_DIR" + # 初始化统计 + init_stats + # 获取仓库列表 repos=$(curl -s -H "Authorization: token $GITHUB_TOKEN" \ "https://api.github.com/user/repos?per_page=100&type=all" | \ jq -r '.[].name') + # 更新总仓库数 + total_repos=$(echo "$repos" | wc -l) + update_stats "total_repos" "$total_repos" "number" + # 同步每个仓库 for repo in $repos; do # 检查是否跳过 if echo "$SKIP_REPOS" | grep -q "$repo"; then echo "跳过仓库: $repo" + update_stats "skipped" "$(( $(jq '.skipped' "$STATS_FILE") + 1 ))" "number" + update_stats "skipped_repos" "$repo" "array" continue fi echo "处理仓库: $repo" + update_stats "processed" "$(( $(jq '.processed' "$STATS_FILE") + 1 ))" "number" sync_repository "$repo" done + + # 更新结束时间 + jq --arg time "$(date '+%Y-%m-%d %H:%M:%S')" \ + '.end_time = $time' "$STATS_FILE" > "$STATS_FILE.tmp" + mv "$STATS_FILE.tmp" "$STATS_FILE" } main "$@" \ No newline at end of file