From a6b59015bd9ff18d65cc40dcc3fbead2391115a3 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 26 Jan 2026 14:50:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0=20Docker=20Pulls=20?= =?UTF-8?q?=E6=AD=B7=E5=8F=B2=E8=A8=98=E9=8C=84=E5=B7=A5=E4=BD=9C=E6=B5=81?= =?UTF-8?q?=E7=A8=8B=EF=BC=8C=E6=94=B9=E5=96=84=20CSV=20=E6=AA=94=E6=A1=88?= =?UTF-8?q?=E8=99=95=E7=90=86=E5=8F=8A=20SVG=20=E5=9C=96=E8=A1=A8=E7=94=9F?= =?UTF-8?q?=E6=88=90=E9=82=8F=E8=BC=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/docker-pulls-history.yml | 96 ++++++++++++++++------ 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/.github/workflows/docker-pulls-history.yml b/.github/workflows/docker-pulls-history.yml index 6e8e0bd..5da1cf4 100644 --- a/.github/workflows/docker-pulls-history.yml +++ b/.github/workflows/docker-pulls-history.yml @@ -5,6 +5,9 @@ on: - cron: "0 0 * * *" # 每天 UTC 00:00(台灣 08:00) workflow_dispatch: +permissions: + contents: write + jobs: track: runs-on: ubuntu-latest @@ -16,55 +19,100 @@ jobs: - name: Fetch Docker Hub pull count run: | set -e + + # 1️⃣ 確保 metrics 資料夾存在(runner 是全新的) + mkdir -p metrics + + # 2️⃣ 若 CSV 不存在,建立並寫入 header + if [ ! -f metrics/docker-pulls.csv ]; then + echo "date,pulls" > metrics/docker-pulls.csv + fi + + # 3️⃣ 抓 Docker Hub 總下載數(不限版本) DATE=$(date -u +"%Y-%m-%d") PULLS=$(curl -s https://hub.docker.com/v2/repositories/convertx/convertx-cn/ | jq '.pull_count') - echo "$DATE,$PULLS" >> metrics/docker-pulls.csv - - name: Generate SVG chart + # 4️⃣ 避免同一天重複寫入 + if ! grep -q "^$DATE," metrics/docker-pulls.csv; then + echo "$DATE,$PULLS" >> metrics/docker-pulls.csv + fi + + - name: Generate SVG chart (star-history style) run: | node <<'EOF' const fs = require('fs'); - const csv = fs.readFileSync('metrics/docker-pulls.csv', 'utf8') + const csvPath = 'metrics/docker-pulls.csv'; + const outPath = 'metrics/docker-pulls-history.svg'; + + const rows = fs.readFileSync(csvPath, 'utf8') .trim() .split('\n') - .slice(1); + .slice(1) + .map(r => { + const [d, p] = r.split(','); + return { date: d, pulls: Number(p) }; + }); - const dates = csv.map(r => r.split(',')[0]); - const pulls = csv.map(r => Number(r.split(',')[1])); + const dates = rows.map(r => r.date); + const pulls = rows.map(r => r.pulls); + + const max = Math.max(...pulls); + const w = 800, h = 400; + const padX = 60, padY = 60; + const plotW = w - padX * 2; + const plotH = h - padY * 2; + + const points = pulls.map((v, i) => { + const x = padX + (i / (pulls.length - 1)) * plotW; + const y = h - padY - (v / max) * plotH; + return `${x},${y}`; + }).join(' '); const svg = ` - - - Docker Pull History – convertx/convertx-cn + + Docker Pull History · convertx/convertx-cn - ${pulls.map((v, i) => { - if (i === 0) return ''; - const x1 = 60 + (i - 1) * (680 / (pulls.length - 1)); - const y1 = 340 - (pulls[i - 1] / Math.max(...pulls)) * 260; - const x2 = 60 + i * (680 / (pulls.length - 1)); - const y2 = 340 - (v / Math.max(...pulls)) * 260; - return ``; - }).join('')} + + + - ${dates[0]} - ${dates[dates.length - 1]} + + - ${Math.max(...pulls).toLocaleString()} + + + ${dates[0]} + + + ${dates[dates.length - 1]} + + + + ${max.toLocaleString()} + `; - fs.writeFileSync('metrics/docker-pulls-history.svg', svg); + fs.writeFileSync(outPath, svg.trim()); EOF - name: Commit and push