diff --git a/.github/workflows/track-ghcr-downloads.yml b/.github/workflows/track-ghcr-downloads.yml new file mode 100644 index 0000000..972a2b0 --- /dev/null +++ b/.github/workflows/track-ghcr-downloads.yml @@ -0,0 +1,147 @@ +name: Track GHCR downloads + +on: + schedule: + - cron: "17 2 * * *" + workflow_dispatch: + +permissions: + contents: write + packages: read + +concurrency: + group: ghcr-download-tracker + cancel-in-progress: false + +jobs: + track: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Read package download count + id: downloads + env: + GH_TOKEN: ${{ github.token }} + shell: bash + run: | + set -euo pipefail + total="$( + gh api --paginate "/users/${{ github.repository_owner }}/packages/container/copilot_here/versions?per_page=100" --jq '.[].download_count // 0' | + awk '{ total += $1 } END { print total + 0 }' + )" + echo "total=$total" >> "$GITHUB_OUTPUT" + echo "Current GHCR downloads: $total" + + - name: Update history and chart + env: + TOTAL_DOWNLOADS: ${{ steps.downloads.outputs.total }} + run: | + python3 <<'PY' + import csv + import html + import os + from datetime import date, timedelta + from pathlib import Path + + data_path = Path("docs/ghcr-downloads.csv") + chart_path = Path("docs/ghcr-downloads.svg") + data_path.parent.mkdir(parents=True, exist_ok=True) + + today = date.today() + total = int(os.environ["TOTAL_DOWNLOADS"]) + rows = {} + + if data_path.exists(): + with data_path.open(newline="", encoding="utf-8") as f: + for row in csv.DictReader(f): + rows[date.fromisoformat(row["date"])] = int(row["downloads"]) + + rows[today] = total + cutoff = today - timedelta(days=89) + rows = {d: count for d, count in rows.items() if d >= cutoff} + points = sorted(rows.items()) + + with data_path.open("w", newline="", encoding="utf-8") as f: + writer = csv.writer(f) + writer.writerow(["date", "downloads"]) + for d, count in points: + writer.writerow([d.isoformat(), count]) + + width, height = 900, 360 + left, right, top, bottom = 72, 24, 54, 54 + plot_w = width - left - right + plot_h = height - top - bottom + counts = [count for _, count in points] + min_count = min(counts) + max_count = max(counts) + span = max(max_count - min_count, 1) + y_min = max(0, min_count - max(1, span // 10)) + y_span = max(max_count - y_min, 1) + + def x(i): + return left + (plot_w * i / max(len(points) - 1, 1)) + + def y(value): + return top + plot_h - ((value - y_min) / y_span * plot_h) + + polyline = " ".join(f"{x(i):.1f},{y(count):.1f}" for i, (_, count) in enumerate(points)) + start_date = points[0][0].isoformat() + end_date = points[-1][0].isoformat() + latest = points[-1][1] + + grid = [] + labels = [] + for i in range(5): + value = y_min + y_span * i / 4 + yy = y(value) + grid.append(f'') + labels.append(f'{int(value):,}') + + svg = f""" + GHCR downloads over the last 90 days + Cumulative downloads for ghcr.io/gordonbeeming/copilot_here. Latest total: {latest:,}. + + GHCR downloads — {latest:,} + Cumulative package downloads, rolling 90-day history + {"".join(grid)} + {"".join(labels)} + + + {html.escape(start_date)} + {html.escape(end_date)} + """ + chart_path.write_text(svg, encoding="utf-8") + PY + + - name: Commit updated statistics + shell: bash + run: | + set -euo pipefail + if git diff --quiet -- docs/ghcr-downloads.csv docs/ghcr-downloads.svg; then + echo "No statistics changes to commit." + exit 0 + fi + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git add docs/ghcr-downloads.csv docs/ghcr-downloads.svg + git commit -m "chore: update GHCR download statistics" + git push diff --git a/docs/ghcr-downloads.md b/docs/ghcr-downloads.md new file mode 100644 index 0000000..fafdb92 --- /dev/null +++ b/docs/ghcr-downloads.md @@ -0,0 +1,9 @@ +# GHCR download statistics + +![GHCR downloads over the last 90 days](ghcr-downloads.svg) + +This chart tracks the cumulative download count for `ghcr.io/gordonbeeming/copilot_here`. + +The [raw history](ghcr-downloads.csv) is updated daily by the [Track GHCR downloads workflow](../.github/workflows/track-ghcr-downloads.yml). The repository keeps a rolling 90-day history, so daily changes can also be derived from consecutive rows. + +> The history starts when the tracker first runs; GitHub does not provide historical daily pull data retroactively.