forked from Gitlink/gitlink-cli
402 lines
19 KiB
Bash
402 lines
19 KiB
Bash
#!/usr/bin/env bash
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
# Scenario 5: Contributor Growth System
|
|
# Flow: Collect data → Calculate scores → Generate HTML → Publish Wiki
|
|
#
|
|
# Scoring (based on available shortcuts):
|
|
# - Issue created: 15% weight (issue +list)
|
|
# - PR merged: 25% weight (pr +list state=merged)
|
|
# - Code changes: 30% weight (pr +files)
|
|
# - Issue comments: 15% weight (issue +view)
|
|
# - Team member: 15% weight (repo +members)
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
source "$SCRIPT_DIR/lib/common.sh"
|
|
|
|
usage() {
|
|
echo "Usage: $0 --owner OWNER --repo REPO [--sample N] [--dry-run]"
|
|
echo ""
|
|
echo " --owner OWNER Repository owner"
|
|
echo " --repo REPO Repository name"
|
|
echo " --sample N Sample N PRs for code stats (default: 10)"
|
|
echo " --dry-run Preview actions without executing"
|
|
exit 1
|
|
}
|
|
|
|
DRY_RUN=false
|
|
OWNER=""
|
|
REPO=""
|
|
SAMPLE_SIZE=10
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--owner) OWNER="$2"; shift 2 ;;
|
|
--repo) REPO="$2"; shift 2 ;;
|
|
--sample) SAMPLE_SIZE="$2"; shift 2 ;;
|
|
--dry-run) DRY_RUN="true"; shift ;;
|
|
--help|-h) usage ;;
|
|
*) log_err "Unknown arg: $1"; usage ;;
|
|
esac
|
|
done
|
|
|
|
check_auth
|
|
require_owner_repo
|
|
|
|
REPORT_FILE="contrib-report-$OWNER-$REPO.html"
|
|
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
log_title "Contributor Growth System: $OWNER/$REPO"
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
|
|
# ── Step 1: Collect Data ─────────────────────────────────────────────
|
|
log_step "Collecting data..."
|
|
|
|
ISSUES_OPEN=$(gl_check issue +list --owner "$OWNER" --repo "$REPO" --state open --limit 100)
|
|
ISSUES_CLOSED=$(gl_run issue +list --owner "$OWNER" --repo "$REPO" --state closed --limit 100)
|
|
OPEN_COUNT=$(echo "$ISSUES_OPEN" | jq '.data.issues | length' 2>/dev/null || echo "0")
|
|
CLOSED_COUNT=$(echo "$ISSUES_CLOSED" | jq '.data.issues | length' 2>/dev/null || echo "0")
|
|
|
|
PRS_MERGED=$(gl_run pr +list --owner "$OWNER" --repo "$REPO" --state merged --limit 100)
|
|
PR_MERGED_COUNT=$(echo "$PRS_MERGED" | jq '(.data.issues // .data.pulls // .data | if type == "array" then . else [] end) | length' 2>/dev/null || echo "0")
|
|
|
|
MEMBERS=$(gl_run repo +members --owner "$OWNER" --repo "$REPO" --limit 100)
|
|
# Members may be in .data.members[] or .data[]
|
|
MEMBER_COUNT=$(echo "$MEMBERS" | jq '(.data.members // .data | if type == "array" then . else [] end) | length' 2>/dev/null || echo "0")
|
|
|
|
log_ok "Issues(open:$OPEN_COUNT closed:$CLOSED_COUNT) PRs(merged:$PR_MERGED_COUNT) Members:$MEMBER_COUNT"
|
|
|
|
# ── Step 2: Build Contributor Data ───────────────────────────────────
|
|
log_step "Building contributor profiles..."
|
|
|
|
declare -A C_ISSUES C_MERGED C_ADDITIONS C_DELETIONS C_COMMENTS C_IS_MEMBER
|
|
|
|
# Issues
|
|
for i in $(seq 0 $((OPEN_COUNT - 1))); do
|
|
A=$(echo "$ISSUES_OPEN" | jq -r ".data.issues[$i].author.login // empty")
|
|
[[ -n "$A" ]] && C_ISSUES["$A"]=$(( ${C_ISSUES["$A"]:-0} + 1 ))
|
|
done
|
|
for i in $(seq 0 $((CLOSED_COUNT - 1))); do
|
|
A=$(echo "$ISSUES_CLOSED" | jq -r ".data.issues[$i].author.login // empty")
|
|
[[ -n "$A" ]] && C_ISSUES["$A"]=$(( ${C_ISSUES["$A"]:-0} + 1 ))
|
|
done
|
|
|
|
# Merged PRs + code stats
|
|
log_step "Analyzing PR code changes (sampling $SAMPLE_SIZE)..."
|
|
PR_SAMPLE=$((PR_MERGED_COUNT > SAMPLE_SIZE ? SAMPLE_SIZE : PR_MERGED_COUNT))
|
|
PR_DATA_PATH='(.data.issues // .data.pulls // .data | if type == "array" then . else [] end)'
|
|
for i in $(seq 0 $((PR_MERGED_COUNT - 1))); do
|
|
A=$(echo "$PRS_MERGED" | jq -r "$PR_DATA_PATH[$i].author_login // $PR_DATA_PATH[$i].author.login // empty")
|
|
ID=$(echo "$PRS_MERGED" | jq -r "$PR_DATA_PATH[$i].pull_request_number // $PR_DATA_PATH[$i].number // $PR_DATA_PATH[$i].id // empty")
|
|
[[ -n "$A" ]] && C_MERGED["$A"]=$(( ${C_MERGED["$A"]:-0} + 1 ))
|
|
if [[ $i -lt $PR_SAMPLE ]] && [[ -n "$ID" ]]; then
|
|
FILES=$(gl_run pr +files --owner "$OWNER" --repo "$REPO" --id "$ID" 2>&1)
|
|
ADD=$(echo "$FILES" | jq -r '[.data.files[]? | (.additions // .addition // 0)] | add // 0' 2>/dev/null || echo "0")
|
|
DEL=$(echo "$FILES" | jq -r '[.data.files[]? | (.deletions // .deletion // 0)] | add // 0' 2>/dev/null || echo "0")
|
|
[[ -n "$A" ]] && C_ADDITIONS["$A"]=$(( ${C_ADDITIONS["$A"]:-0} + ADD ))
|
|
[[ -n "$A" ]] && C_DELETIONS["$A"]=$(( ${C_DELETIONS["$A"]:-0} + DEL ))
|
|
fi
|
|
done
|
|
|
|
# Members
|
|
MEMBERS_DATA_PATH='(.data.members // .data | if type == "array" then . else [] end)'
|
|
for i in $(seq 0 $((MEMBER_COUNT - 1))); do
|
|
L=$(echo "$MEMBERS" | jq -r "$MEMBERS_DATA_PATH[$i].login // $MEMBERS_DATA_PATH[$i].username // empty")
|
|
[[ -n "$L" ]] && C_IS_MEMBER["$L"]="yes"
|
|
done
|
|
|
|
# Comments (sample)
|
|
log_step "Sampling issue comments..."
|
|
for i in $(seq 0 $((OPEN_COUNT > 10 ? 9 : OPEN_COUNT - 1))); do
|
|
ID=$(echo "$ISSUES_OPEN" | jq -r ".data.issues[$i].id // empty")
|
|
[[ -z "$ID" ]] && continue
|
|
DETAIL=$(gl_run issue +view --owner "$OWNER" --repo "$REPO" --number "$ID" 2>&1)
|
|
C=$(echo "$DETAIL" | jq -r '.data.comment_journals_count // 0' 2>/dev/null || echo "0")
|
|
if [[ "$C" -gt 0 ]]; then
|
|
A=$(echo "$ISSUES_OPEN" | jq -r ".data.issues[$i].author.login // empty")
|
|
[[ -n "$A" ]] && C_COMMENTS["$A"]=$(( ${C_COMMENTS["$A"]:-0} + C ))
|
|
fi
|
|
done
|
|
|
|
# ── Step 3: Calculate Scores ─────────────────────────────────────────
|
|
log_step "Calculating scores..."
|
|
|
|
declare -A SCORES
|
|
ALL_USERS=()
|
|
for user in "${!C_ISSUES[@]}" "${!C_MERGED[@]}" "${!C_COMMENTS[@]}"; do
|
|
[[ -n "$user" ]] && ALL_USERS+=("$user")
|
|
done
|
|
ALL_USERS=($(printf '%s\n' "${ALL_USERS[@]}" | sort -u))
|
|
|
|
MAX_ISSUES=0; MAX_MERGED=0; MAX_LINES=0; MAX_COMMENTS=0
|
|
for user in "${ALL_USERS[@]}"; do
|
|
[[ ${C_ISSUES[$user]:-0} -gt $MAX_ISSUES ]] && MAX_ISSUES=${C_ISSUES[$user]}
|
|
[[ ${C_MERGED[$user]:-0} -gt $MAX_MERGED ]] && MAX_MERGED=${C_MERGED[$user]}
|
|
LINES=$(( ${C_ADDITIONS[$user]:-0} + ${C_DELETIONS[$user]:-0} ))
|
|
[[ $LINES -gt $MAX_LINES ]] && MAX_LINES=$LINES
|
|
[[ ${C_COMMENTS[$user]:-0} -gt $MAX_COMMENTS ]] && MAX_COMMENTS=${C_COMMENTS[$user]}
|
|
done
|
|
|
|
for user in "${ALL_USERS[@]}"; do
|
|
SCORE=$(awk -v iss="${C_ISSUES[$user]:-0}" -v mi="$MAX_ISSUES" \
|
|
-v mer="${C_MERGED[$user]:-0}" -v mm="$MAX_MERGED" \
|
|
-v lin="$(( ${C_ADDITIONS[$user]:-0} + ${C_DELETIONS[$user]:-0} ))" -v ml="$MAX_LINES" \
|
|
-v com="${C_COMMENTS[$user]:-0}" -v mc="$MAX_COMMENTS" \
|
|
-v mem="${C_IS_MEMBER[$user]:-no}" \
|
|
'BEGIN {
|
|
ni=(mi>0)?iss/mi:0; nm=(mm>0)?mer/mm:0; nl=(ml>0)?lin/ml:0; nc=(mc>0)?com/mc:0; ms=(mem=="yes")?1:0;
|
|
printf "%.1f", (ni*15+nm*25+nl*30+nc*15+ms*15)
|
|
}')
|
|
SCORES["$user"]="$SCORE"
|
|
done
|
|
|
|
# ── Step 4: Display Rankings ─────────────────────────────────────────
|
|
log_title "Contributor Rankings"
|
|
echo ""
|
|
printf " ${BOLD}%-4s %-18s %-8s %-8s %-12s %-10s %-8s %s${NC}\n" "Rank" "Contributor" "Issues" "Merged" "+/- Lines" "Comments" "Score" "Badge"
|
|
echo " ──── ─────────────────── ──────── ──────── ──────────── ────────── ──────── ─────────────"
|
|
|
|
TEMP=$(mktemp)
|
|
for u in "${!SCORES[@]}"; do echo "${SCORES[$u]} $u" >> "$TEMP"; done
|
|
|
|
RANK=1
|
|
sort -rn "$TEMP" | while read -r score user; do
|
|
iss=${C_ISSUES[$user]:-0}; mer=${C_MERGED[$user]:-0}
|
|
add=${C_ADDITIONS[$user]:-0}; del=${C_DELETIONS[$user]:-0}
|
|
com=${C_COMMENTS[$user]:-0}; si=${score%.*}
|
|
if [[ $si -ge 80 ]]; then B="Champion"
|
|
elif [[ $si -ge 60 ]]; then B="Core Contributor"
|
|
elif [[ $si -ge 40 ]]; then B="Active Contributor"
|
|
elif [[ $si -ge 20 ]]; then B="Contributor"
|
|
else B="Newcomer"
|
|
fi
|
|
printf " %-4d %-18s %-8d %-8d +%-6d/-%-4d %-10d %-8s %s\n" "$RANK" "$user" "$iss" "$mer" "$add" "$del" "$com" "$score" "$B"
|
|
RANK=$((RANK + 1))
|
|
done
|
|
rm -f "$TEMP"
|
|
|
|
# ── Step 5: Generate HTML Report ─────────────────────────────────────
|
|
log_title "Generating HTML Report"
|
|
|
|
# Build JSON data for charts
|
|
PIE_DATA=""
|
|
TABLE_ROWS=""
|
|
RANK=1
|
|
|
|
TEMP2=$(mktemp)
|
|
for u in "${!SCORES[@]}"; do echo "${SCORES[$u]} $u" >> "$TEMP2"; done
|
|
|
|
sort -rn "$TEMP2" | while read -r score user; do
|
|
iss=${C_ISSUES[$user]:-0}; mer=${C_MERGED[$user]:-0}
|
|
add=${C_ADDITIONS[$user]:-0}; del=${C_DELETIONS[$user]:-0}
|
|
lin=$((add + del)); com=${C_COMMENTS[$user]:-0}
|
|
si=${score%.*}
|
|
if [[ $si -ge 80 ]]; then B="Champion"
|
|
elif [[ $si -ge 60 ]]; then B="Core Contributor"
|
|
elif [[ $si -ge 40 ]]; then B="Active Contributor"
|
|
elif [[ $si -ge 20 ]]; then B="Contributor"
|
|
else B="Newcomer"
|
|
fi
|
|
# Output as CSV for processing
|
|
echo "$RANK|$user|$iss|$mer|$lin|$com|$score|$B|$add|$del"
|
|
RANK=$((RANK + 1))
|
|
done > "$TEMP2.csv"
|
|
rm -f "$TEMP2"
|
|
|
|
# Generate HTML
|
|
cat > "$REPORT_FILE" << 'HTMLHEAD'
|
|
<!DOCTYPE html>
|
|
<html lang="zh-CN">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Contributor Report</title>
|
|
<script src="https://cdn.jsdelivr.net/npm/echarts@5.4.3/dist/echarts.min.js"></script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); min-height: 100vh; padding: 40px 20px; }
|
|
.container { max-width: 1200px; margin: 0 auto; }
|
|
.header { text-align: center; color: white; margin-bottom: 40px; }
|
|
.header h1 { font-size: 2.5rem; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); }
|
|
.header p { font-size: 1.1rem; opacity: 0.9; }
|
|
.card { background: white; border-radius: 16px; box-shadow: 0 20px 60px rgba(0,0,0,0.3); padding: 30px; margin-bottom: 30px; }
|
|
.card h2 { color: #333; margin-bottom: 20px; font-size: 1.5rem; border-bottom: 3px solid #667eea; padding-bottom: 10px; }
|
|
.stats-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 30px; }
|
|
.stat-card { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 12px; padding: 20px; color: white; text-align: center; }
|
|
.stat-value { font-size: 2rem; font-weight: bold; margin-bottom: 5px; }
|
|
.stat-label { font-size: 0.9rem; opacity: 0.9; }
|
|
table { width: 100%; border-collapse: collapse; margin-top: 20px; }
|
|
th { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 15px 12px; text-align: left; font-size: 0.85rem; text-transform: uppercase; letter-spacing: 1px; }
|
|
td { padding: 12px; border-bottom: 1px solid #eee; }
|
|
tr:hover { background: #f8f9ff; }
|
|
.rank { font-weight: bold; color: #667eea; font-size: 1.2rem; }
|
|
.rank-1 { color: #FFD700; }
|
|
.rank-2 { color: #C0C0C0; }
|
|
.rank-3 { color: #CD7F32; }
|
|
.badge { padding: 4px 12px; border-radius: 20px; font-size: 0.8rem; font-weight: 600; }
|
|
.badge-champion { background: #FFD700; color: #333; }
|
|
.badge-core { background: #C0C0C0; color: #333; }
|
|
.badge-active { background: #CD7F32; color: white; }
|
|
.badge-contributor { background: #4CAF50; color: white; }
|
|
.badge-newcomer { background: #9E9E9E; color: white; }
|
|
.chart-container { width: 100%; height: 400px; }
|
|
.weight-info { background: #f8f9ff; border-radius: 12px; padding: 20px; margin-top: 20px; }
|
|
.weight-info h3 { color: #667eea; margin-bottom: 15px; }
|
|
.weight-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; }
|
|
.weight-item { display: flex; justify-content: space-between; padding: 8px 12px; background: white; border-radius: 8px; border-left: 4px solid #667eea; }
|
|
.weight-label { color: #666; }
|
|
.weight-value { font-weight: 600; color: #667eea; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="header">
|
|
<h1>Contributor Report</h1>
|
|
HTMLHEAD
|
|
|
|
echo " <p>$OWNER/$REPO - Team Contribution Analysis</p>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
|
|
# Stats cards
|
|
TOTAL_ISSUES=$((OPEN_COUNT + CLOSED_COUNT))
|
|
echo " <div class=\"stats-grid\">" >> "$REPORT_FILE"
|
|
echo " <div class=\"stat-card\"><div class=\"stat-value\">${#SCORES[@]}</div><div class=\"stat-label\">Contributors</div></div>" >> "$REPORT_FILE"
|
|
echo " <div class=\"stat-card\"><div class=\"stat-value\">$TOTAL_ISSUES</div><div class=\"stat-label\">Total Issues</div></div>" >> "$REPORT_FILE"
|
|
echo " <div class=\"stat-card\"><div class=\"stat-value\">$PR_MERGED_COUNT</div><div class=\"stat-label\">Merged PRs</div></div>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
|
|
# Pie chart
|
|
echo " <div class=\"card\">" >> "$REPORT_FILE"
|
|
echo " <h2>Score Distribution</h2>" >> "$REPORT_FILE"
|
|
echo " <div id=\"pieChart\" class=\"chart-container\"></div>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
|
|
# Rankings table
|
|
echo " <div class=\"card\">" >> "$REPORT_FILE"
|
|
echo " <h2>Detailed Rankings</h2>" >> "$REPORT_FILE"
|
|
echo " <table><thead><tr><th>Rank</th><th>Contributor</th><th>Issues</th><th>Merged PRs</th><th>Code Lines</th><th>Comments</th><th>Score</th><th>Badge</th></tr></thead><tbody>" >> "$REPORT_FILE"
|
|
|
|
PIE_JSON=""
|
|
while IFS='|' read -r rank user iss mer lin com score badge add del; do
|
|
cls=""; [[ $rank -eq 1 ]] && cls=" rank-1"
|
|
[[ $rank -eq 2 ]] && cls=" rank-2"
|
|
[[ $rank -eq 3 ]] && cls=" rank-3"
|
|
|
|
badge_cls="newcomer"
|
|
[[ "$badge" == "Champion" ]] && badge_cls="champion"
|
|
[[ "$badge" == "Core Contributor" ]] && badge_cls="core"
|
|
[[ "$badge" == "Active Contributor" ]] && badge_cls="active"
|
|
[[ "$badge" == "Contributor" ]] && badge_cls="contributor"
|
|
|
|
echo " <tr><td class=\"rank$cls\">$rank</td><td>@$user</td><td>$iss</td><td>$mer</td><td>$lin</td><td>$com</td><td>$score</td><td><span class=\"badge badge-$badge_cls\">$badge</span></td></tr>" >> "$REPORT_FILE"
|
|
PIE_JSON+="{value: $score, name: '$user'},"
|
|
done < "$TEMP2.csv"
|
|
|
|
echo " </tbody></table>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
|
|
# Weight info
|
|
echo " <div class=\"card\">" >> "$REPORT_FILE"
|
|
echo " <h2>Scoring System (AHP Weights)</h2>" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-info\">" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-grid\">" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-item\"><span class=\"weight-label\">Issues Created</span><span class=\"weight-value\">15%</span></div>" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-item\"><span class=\"weight-label\">PRs Merged</span><span class=\"weight-value\">25%</span></div>" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-item\"><span class=\"weight-label\">Code Changes</span><span class=\"weight-value\">30%</span></div>" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-item\"><span class=\"weight-label\">Issue Comments</span><span class=\"weight-value\">15%</span></div>" >> "$REPORT_FILE"
|
|
echo " <div class=\"weight-item\"><span class=\"weight-label\">Team Member</span><span class=\"weight-value\">15%</span></div>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
echo " </div>" >> "$REPORT_FILE"
|
|
|
|
# JavaScript
|
|
cat >> "$REPORT_FILE" << HTMLFOOT
|
|
</div>
|
|
<script>
|
|
var chart = echarts.init(document.getElementById('pieChart'));
|
|
chart.setOption({
|
|
tooltip: { trigger: 'item', formatter: '{a} <br/>{b}: {c} ({d}%)' },
|
|
legend: { orient: 'vertical', left: 'left', top: 'middle' },
|
|
series: [{
|
|
name: 'Score',
|
|
type: 'pie',
|
|
radius: ['40%', '70%'],
|
|
center: ['60%', '50%'],
|
|
itemStyle: { borderRadius: 10, borderColor: '#fff', borderWidth: 2 },
|
|
label: { show: true, formatter: '{b}\\n{d}%' },
|
|
data: [$PIE_JSON]
|
|
}]
|
|
});
|
|
window.addEventListener('resize', () => chart.resize());
|
|
</script>
|
|
</body>
|
|
</html>
|
|
HTMLFOOT
|
|
|
|
rm -f "$TEMP2.csv"
|
|
log_ok "HTML report: $REPORT_FILE"
|
|
|
|
# ── Step 6: Publish to Wiki ──────────────────────────────────────────
|
|
log_step "Publishing to Wiki..."
|
|
|
|
WIKI_CONTENT="# Contributor Leaderboard - $OWNER/$REPO
|
|
|
|
*Generated: $(date '+%Y-%m-%d %H:%M')*
|
|
|
|
## Scoring System
|
|
|
|
| Dimension | Weight | Source |
|
|
|-----------|--------|--------|
|
|
| Issues Created | 15% | \`issue +list\` |
|
|
| PRs Merged | 25% | \`pr +list state=merged\` |
|
|
| Code Changes | 30% | \`pr +files\` |
|
|
| Issue Comments | 15% | \`issue +view\` |
|
|
| Team Member | 15% | \`repo +members\` |
|
|
|
|
## Rankings
|
|
|
|
| Rank | Contributor | Issues | Merged | Lines | Comments | Score | Badge |
|
|
|------|-------------|--------|--------|-------|----------|-------|-------|
|
|
"
|
|
|
|
TEMP3=$(mktemp)
|
|
for u in "${!SCORES[@]}"; do echo "${SCORES[$u]} $u" >> "$TEMP3"; done
|
|
RANK=1
|
|
sort -rn "$TEMP3" | while read -r score user; do
|
|
iss=${C_ISSUES[$user]:-0}; mer=${C_MERGED[$user]:-0}
|
|
lin=$(( ${C_ADDITIONS[$user]:-0} + ${C_DELETIONS[$user]:-0} ))
|
|
com=${C_COMMENTS[$user]:-0}; si=${score%.*}
|
|
if [[ $si -ge 80 ]]; then B="Champion"
|
|
elif [[ $si -ge 60 ]]; then B="Core"
|
|
elif [[ $si -ge 40 ]]; then B="Active"
|
|
elif [[ $si -ge 20 ]]; then B="Contributor"
|
|
else B="Newcomer"
|
|
fi
|
|
echo "| $RANK | @$user | $iss | $mer | $lin | $com | $score | $B |"
|
|
RANK=$((RANK + 1))
|
|
done > "$TEMP3.rows"
|
|
WIKI_CONTENT+=$(cat "$TEMP3.rows")
|
|
rm -f "$TEMP3" "$TEMP3.rows"
|
|
|
|
WIKI_CONTENT+="
|
|
|
|
---
|
|
*Auto-generated by gitlink-cli*"
|
|
|
|
# Use timestamp to avoid title conflicts with cached deletions
|
|
WIKI_TITLE="Contributor Leaderboard $(date '+%Y-%m-%d')"
|
|
WIKI_RESULT=$(gl_run wiki +create --owner "$OWNER" --repo "$REPO" \
|
|
--title "$WIKI_TITLE" --content "$WIKI_CONTENT" 2>&1) || true
|
|
|
|
if [[ "$(json_ok "$WIKI_RESULT")" == "true" ]]; then
|
|
log_ok "Published to Wiki: $WIKI_TITLE"
|
|
else
|
|
log_warn "Wiki publish failed"
|
|
fi
|
|
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
log_title "Complete"
|
|
echo " Contributors: ${#SCORES[@]}"
|
|
echo " HTML Report: $REPORT_FILE"
|
|
echo ""
|