metrics #5
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: metrics | |
| # Weekly adoption snapshot. GitHub only retains traffic for 14 days and exposes | |
| # it to repo admins only — without this job that data is lost every day. Appends: | |
| # * metrics/traffic_log.csv — one row/run: stars, forks, downloads, 14d views/clones, Zenodo | |
| # * metrics/referrers_log.csv — top referrers/run (date, referrer, count, uniques) | |
| # * metrics/paths_log.csv — top viewed paths/run (date, path, title, count, uniques) | |
| # The referrer/path snapshots are the most strategically useful AND the most | |
| # ephemeral data GitHub exposes: they reveal which channel (Google / Reddit / | |
| # chatgpt.com / a directory) drove a wave, but are a top-10 point-in-time view | |
| # that vanishes after 14 days. Long-format rows let you pivot a channel's trend | |
| # over weeks (e.g. is AEO traffic from chatgpt.com / perplexity.ai compounding?). | |
| # | |
| # Traffic endpoints (/traffic/*) require push access. The default GITHUB_TOKEN may | |
| # return 403; set a repo secret METRICS_PAT (a fine-grained PAT with | |
| # "Administration: read" on this repo) to capture them. Public metrics | |
| # (stars/forks/release downloads/Zenodo) work without any PAT. | |
| on: | |
| schedule: | |
| - cron: "17 3 * * 1" # every Monday 03:17 UTC | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: write | |
| jobs: | |
| snapshot: | |
| # Do not run on forks — only the canonical repo should append to the log. | |
| if: github.repository_owner == 'Aperivue' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Capture metrics snapshot | |
| env: | |
| GH_TOKEN: ${{ secrets.METRICS_PAT || secrets.GITHUB_TOKEN }} | |
| REPO: ${{ github.repository }} | |
| # Zenodo concept record id (the number in the concept DOI). Verify it | |
| # resolves at https://zenodo.org/api/records/<id> before relying on the | |
| # Zenodo columns; left blank-tolerant if the API call fails. | |
| ZENODO_RECORD: "20155321" | |
| run: | | |
| set -uo pipefail | |
| DATE=$(date -u +%Y-%m-%d) | |
| # --- Public repo metrics (always available) --- | |
| REPO_JSON=$(gh api "repos/$REPO" 2>/dev/null || echo '{}') | |
| STARS=$(echo "$REPO_JSON" | jq -r '.stargazers_count // ""') | |
| FORKS=$(echo "$REPO_JSON" | jq -r '.forks_count // ""') | |
| WATCH=$(echo "$REPO_JSON" | jq -r '.subscribers_count // ""') | |
| DL=$(gh api "repos/$REPO/releases" --paginate 2>/dev/null \ | |
| | jq -s 'add // [] | [.[].assets[]?.download_count] | add // 0' || echo "") | |
| # --- Traffic (needs push access; blank-tolerant on 403) --- | |
| VJSON=$(gh api "repos/$REPO/traffic/views" 2>/dev/null || echo '{}') | |
| CJSON=$(gh api "repos/$REPO/traffic/clones" 2>/dev/null || echo '{}') | |
| VIEWS=$(echo "$VJSON" | jq -r '.count // ""') | |
| UVIEWS=$(echo "$VJSON" | jq -r '.uniques // ""') | |
| CLONES=$(echo "$CJSON" | jq -r '.count // ""') | |
| UCLONES=$(echo "$CJSON"| jq -r '.uniques // ""') | |
| # --- Top referrers + paths (top-10 point-in-time; default [] so a 403/error | |
| # yields zero rows rather than a malformed append) --- | |
| RJSON=$(gh api "repos/$REPO/traffic/popular/referrers" 2>/dev/null || echo '[]') | |
| PJSON=$(gh api "repos/$REPO/traffic/popular/paths" 2>/dev/null || echo '[]') | |
| # --- Zenodo (public API, blank-tolerant) --- | |
| ZJSON=$(curl -fsSL "https://zenodo.org/api/records/$ZENODO_RECORD" 2>/dev/null || echo '{}') | |
| ZVIEWS=$(echo "$ZJSON" | jq -r '(.stats.version_views // .stats.views) // ""' 2>/dev/null || echo "") | |
| ZDL=$(echo "$ZJSON" | jq -r '(.stats.version_downloads // .stats.downloads) // ""' 2>/dev/null || echo "") | |
| mkdir -p metrics | |
| if [ ! -f metrics/traffic_log.csv ]; then | |
| echo "date,stars,forks,watchers,release_downloads,views_14d,unique_views_14d,clones_14d,unique_clones_14d,zenodo_views,zenodo_downloads" > metrics/traffic_log.csv | |
| fi | |
| echo "$DATE,$STARS,$FORKS,$WATCH,$DL,$VIEWS,$UVIEWS,$CLONES,$UCLONES,$ZVIEWS,$ZDL" >> metrics/traffic_log.csv | |
| echo "Appended: $DATE stars=$STARS forks=$FORKS downloads=$DL views=$VIEWS clones=$CLONES" | |
| # Referrers (date,referrer,count,uniques). @csv quotes fields safely; `if type==array` | |
| # tolerates a 403/{} fallback by emitting nothing. Header written once. | |
| if [ ! -f metrics/referrers_log.csv ]; then | |
| echo "date,referrer,count,uniques" > metrics/referrers_log.csv | |
| fi | |
| echo "$RJSON" | jq -r --arg d "$DATE" \ | |
| 'if type=="array" then .[] else empty end | [$d, .referrer, .count, .uniques] | @csv' \ | |
| >> metrics/referrers_log.csv | |
| NREF=$(echo "$RJSON" | jq -r 'if type=="array" then length else 0 end') | |
| # Popular paths (date,path,title,count,uniques). | |
| if [ ! -f metrics/paths_log.csv ]; then | |
| echo "date,path,title,count,uniques" > metrics/paths_log.csv | |
| fi | |
| echo "$PJSON" | jq -r --arg d "$DATE" \ | |
| 'if type=="array" then .[] else empty end | [$d, .path, .title, .count, .uniques] | @csv' \ | |
| >> metrics/paths_log.csv | |
| NPATH=$(echo "$PJSON" | jq -r 'if type=="array" then length else 0 end') | |
| echo "Referrers captured: $NREF; paths captured: $NPATH (0 = no PAT / no traffic data)" | |
| - name: Commit snapshot | |
| run: | | |
| git config user.name "github-actions[bot]" | |
| git config user.email "github-actions[bot]@users.noreply.github.com" | |
| git add metrics/traffic_log.csv metrics/referrers_log.csv metrics/paths_log.csv | |
| git diff --staged --quiet || git commit -m "chore(metrics): weekly adoption snapshot [skip ci]" | |
| git push |