Skip to content

metrics

metrics #5

Workflow file for this run

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