@@ -2,79 +2,207 @@ name: Collect GitHub Traffic
22
33on :
44 schedule :
5- - cron : " 0 2 * * *"
5+ - cron : " 0 4 * * *" # 04:00 UTC
66 workflow_dispatch :
77 inputs :
8- date :
9- description : " Date (YYYY-MM-DD), default today (UTC)"
10- required : false
118 dry_run :
129 description : " Only show result (w/o commit)"
10+ required : true
11+ default : " false"
12+ type : choice
13+ options :
14+ - true
15+ - false
16+ init_pages :
17+ description : " Create gh-pages branch"
18+ required : true
19+ default : " false"
20+ type : choice
21+ options :
22+ - true
23+ - false
24+ reset :
25+ description : " Reset stat files (full clean)"
1326 required : false
1427 default : " false"
28+ type : choice
29+ options :
30+ - true
31+ - false
1532
1633permissions :
1734 contents : write
1835
36+ env :
37+ GH_TOKEN : ${{ secrets.GH_PAT }}
38+ API_CLONES : https://api.github.com/repos/${{ github.repository }}/traffic/clones
39+ API_VIEWS : https://api.github.com/repos/${{ github.repository }}/traffic/views
40+ WORK_DIR : ./traffic
41+
1942jobs :
2043 traffic :
2144 runs-on : ubuntu-latest
2245
2346 steps :
47+ - name : Create gh-pages branch if missing
48+ if : github.event.inputs.init_pages == 'true'
49+ env :
50+ GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
51+ REPO : ${{ github.repository }}
52+ run : |
53+ set -e
54+ git clone https://x-access-token:${GH_TOKEN}@github.com/${REPO}.git repo
55+ cd repo
56+ git config user.name "github-actions"
57+ git config user.email "github-actions@github.com"
58+ if git show-ref --verify --quiet refs/heads/gh-pages; then
59+ echo "gh-pages already exists"
60+ exit 0
61+ fi
62+ echo "Creating empty gh-pages branch"
63+ git checkout --orphan gh-pages
64+ git rm -rf .
65+ mkdir -p ${{ env.WORK_DIR }}
66+ touch ${{ env.WORK_DIR }}/clones.svg
67+ echo "# gh-pages" > README.md
68+ git add README.md ${{ env.WORK_DIR }}/*
69+ git commit -m "init gh-pages"
70+ git push origin gh-pages
71+
2472 - name : Checkout gh-pages
2573 uses : actions/checkout@v4
2674 with :
2775 ref : gh-pages
28- path : gh-pages
76+ fetch-depth : 0
2977
30- - name : Fetch traffic data
78+ - name : Configure git
79+ run : |
80+ git config user.name "github-actions"
81+ git config user.email "github-actions@github.com"
82+
83+ - name : Reset traffic data (on demand)
84+ if : github.event.inputs.reset == 'true'
85+ env :
86+ WORK_DIR : ${{ env.WORK_DIR }}
87+ run : |
88+ echo "Reset requested — clean traffic dir"
89+ if [ -d $WORK_DIR ]; then
90+ cd $WORK_DIR
91+ rm -f ./*
92+ git add -A
93+ git commit -m "Reset traffic stats" || true
94+ git push
95+ fi
96+
97+ - name : Ensure traffic JSON files exist
98+ env :
99+ WORK_DIR : ${{ env.WORK_DIR }}
100+ run : |
101+ mkdir -p $WORK_DIR
102+ cd $WORK_DIR
103+ for FILE in clones.json clones_uniq.json views.json views_uniq.json; do
104+ if [ ! -f "$FILE" ]; then
105+ echo '{"total":0,"last_update":"","daily":{}}' > "$FILE"
106+ fi
107+ done
108+
109+ - name : Fetch traffic data (clones + views)
110+ working-directory : ${{ env.WORK_DIR }}
31111 env :
32- GH_TOKEN : ${{ secrets.GITHUB_TOKEN }}
33112 INPUT_DATE : ${{ github.event.inputs.date }}
113+ run : |
114+ set -e
115+ fetch_data () {
116+ API="$1"
117+ OUT="$2"
118+ echo "API response ($OUT)"
119+ HTTP=$( curl -s -w "%{http_code}" -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API" -o "$OUT" )
120+ if [ "$HTTP" != "200" ]; then
121+ echo "API error ($HTTP):"
122+ cat "$OUT"
123+ exit 11
124+ fi
125+ echo "::group::GitHub Traffic API raw response ($OUT)"
126+ cat "$OUT" | jq .
127+ echo "::endgroup::"
128+ }
129+ fetch_data "$API_CLONES" clones_api.json
130+ fetch_data "$API_VIEWS" views_api.json
131+ UPDATED_AT=$( date -u +"%Y-%m-%dT%H:%M:%SZ" )
132+ echo "UPDATED_AT=$UPDATED_AT" >> $GITHUB_ENV
133+
134+ - name : Update traffic JSON files
135+ working-directory : ${{ env.WORK_DIR }}
136+ env :
34137 DRY_RUN : ${{ github.event.inputs.dry_run }}
35138 run : |
36139 set -e
37- OWNER="${{ github.repository_owner }}"
38- REPO="$(basename ${{ github.repository }})"
39- if [ -n "$INPUT_DATE" ]; then
40- TODAY="$INPUT_DATE"
41- else
42- TODAY=$(date -u +%F)
43- fi
44- API="https://api.github.com/repos/$OWNER/$REPO/traffic/clones"
45- DATA=$( curl -s -H "Authorization: Bearer $GH_TOKEN" -H "Accept: application/vnd.github+json" "$API" )
46- COUNT=$( echo "$DATA" | jq '.clones[-1].count // 0' )
47- mkdir -p gh-pages/traffic
48- FILE="gh-pages/traffic/clones.json"
49- if [ ! -f "$FILE" ]; then
50- echo '{"total":0,"daily":{}}' > "$FILE"
51- fi
52- TOTAL=$( jq '.total' "$FILE" )
53- TOTAL_NEW=$((TOTAL + COUNT))
54- jq \
55- --arg date "$TODAY" \
56- --argjson count "$COUNT" \
57- --argjson total "$TOTAL_NEW" \
58- '
59- .total = $total
60- | .last_update = $date
61- | .daily[$date] = $count
62- ' "$FILE" > "$FILE.tmp"
63- mv "$FILE.tmp" "$FILE"
64- echo "Date: $TODAY"
65- echo "Clones per day: $COUNT"
66- echo "TOTAL_NEW: $TOTAL_NEW"
140+ update_json() {
141+ local UPDATES=$1
142+ local KEY=$2
143+ local FIELD=$3
144+ local FILE=$4
145+ echo "[$FILE] : TOTAL = $( jq -r '.total' $FILE ) (old)"
146+ DAYS=$( jq ".${KEY} | length" "$UPDATES" )
147+ echo "Updating $FILE ($DAYS days)"
148+ jq \
149+ --arg key "$KEY" \
150+ --arg field "$FIELD" \
151+ --arg updated_at "$UPDATED_AT" \
152+ --argjson updates "$(cat $UPDATES)" '
153+ . // { daily: {} }
154+ | .daily as $old
155+ | .daily = (
156+ reduce $updates[$key][] as $date ($old;
157+ .[ ($date.timestamp | split("T")[0]) ] = $date[$field]
158+ )
159+ )
160+ | .total = ( .daily | to_entries | map(.value) | add // 0 )
161+ | .last_update = $updated_at
162+ ' "$FILE" > "$FILE.tmp"
163+ mv "$FILE.tmp" "$FILE"
164+ echo "[$FILE] : TOTAL = $( jq -r '.total' $FILE ) (new)"
165+ git add "$FILE"
166+ }
167+ update_json clones_api.json clones count clones.json
168+ update_json clones_api.json clones uniques clones_uniq.json
169+ update_json views_api.json views count views.json
170+ update_json views_api.json views uniques views_uniq.json
67171 if [ "$DRY_RUN" = "true" ]; then
68172 echo "DRY-RUN: changes not be saved!"
69- exit 0
70173 fi
71174
175+ - name : Generate badge_clones.svg
176+ if : github.event.inputs.dry_run != 'true'
177+ working-directory : ${{ env.WORK_DIR }}
178+ run : |
179+ if git diff --cached --quiet; then
180+ echo "No changes for update badges"
181+ exit 0
182+ fi
183+ create_badge () {
184+ FILE="$1"
185+ FNAME="$2"
186+ LABEL="$3"
187+ COLOR="${4:-green}"
188+ BTYPE="${5:-downloads}"
189+ TOTAL=$( jq -r '.total' "$FILE" )
190+ UPDATED_AT=$( jq -r '.last_update' "$FILE" )
191+ curl -s "https://img.shields.io/badge/${BTYPE}-${TOTAL}-${COLOR}?label=${LABEL}&style=flat" -o "$FNAME"
192+ git add "$FNAME"
193+ }
194+ create_badge clones.json clones.svg downloads green
195+ create_badge clones_uniq.json clones_uniq.svg downloads blue
196+ create_badge views.json views.svg views green
197+ create_badge views_uniq.json views_uniq.svg unique%20views blue
198+
72199 - name : Commit & push
73200 if : github.event.inputs.dry_run != 'true'
201+ working-directory : ${{ env.WORK_DIR }}
74202 run : |
75- cd gh-pages
76- git config user.name "github-actions "
77- git config user.email "github-actions@github.com"
78- git add traffic/clones.json
79- git commit -m "traffic: update clones stats" || exit 0
203+ if git diff --cached --quiet; then
204+ echo "No changes to commit "
205+ exit 0
206+ fi
207+ git commit -m "traffic: update clones stats for $(date -u +%F)"
80208 git push
0 commit comments