diff --git a/.github/workflows/clone.yml b/.github/workflows/clone.yml index 380320a..6d099a5 100644 --- a/.github/workflows/clone.yml +++ b/.github/workflows/clone.yml @@ -2,7 +2,7 @@ name: GitHub Clone Count Update Everyday on: schedule: - - cron: "0 */24 * * *" + - cron: "0 0 * * *" workflow_dispatch: jobs: @@ -13,69 +13,131 @@ jobs: steps: - uses: actions/checkout@v6 - + - name: gh login run: echo "${{ secrets.SECRET_TOKEN }}" | gh auth login --with-token - name: parse latest clone count run: | - curl --user "${{ github.actor }}:${{ secrets.SECRET_TOKEN }}" \ + set -euo pipefail + if ! curl -f --user "${{ github.actor }}:${{ secrets.SECRET_TOKEN }}" \ -H "Accept: application/vnd.github.v3+json" \ - https://api.github.com/repos/${{ github.repository }}/traffic/clones \ - > clone.json + "https://api.github.com/repos/${{ github.repository }}/traffic/clones" \ + > clone.json; then + echo "Error: Failed to fetch clone statistics" + exit 1 + fi - name: create gist and download previous count id: set_id run: | + set -euo pipefail if gh secret list | grep -q "GIST_ID" then echo "GIST_ID found" echo "GIST=${{ secrets.GIST_ID }}" >> $GITHUB_OUTPUT - curl https://gist.githubusercontent.com/${{ github.actor }}/${{ secrets.GIST_ID }}/raw/clone.json > clone_before.json - if cat clone_before.json | grep '404: Not Found'; then + curl -f "https://gist.githubusercontent.com/${{ github.actor }}/${{ secrets.GIST_ID }}/raw/clone.json" > clone_before.json || true + if grep -q '404: Not Found' clone_before.json 2>/dev/null || [ ! -s clone_before.json ]; then echo "GIST_ID not valid anymore. Creating another gist..." gist_id=$(gh gist create clone.json | awk -F / '{print $NF}') - echo $gist_id | gh secret set GIST_ID + if [ -z "$gist_id" ]; then + echo "Error: Failed to create gist" + exit 1 + fi + echo "$gist_id" | gh secret set GIST_ID echo "GIST=$gist_id" >> $GITHUB_OUTPUT cp clone.json clone_before.json - git rm --ignore-unmatch CLONE.md + git rm --ignore-unmatch CLONE.md || true fi else echo "GIST_ID not found. Creating a gist..." gist_id=$(gh gist create clone.json | awk -F / '{print $NF}') - echo $gist_id | gh secret set GIST_ID + if [ -z "$gist_id" ]; then + echo "Error: Failed to create gist" + exit 1 + fi + echo "$gist_id" | gh secret set GIST_ID echo "GIST=$gist_id" >> $GITHUB_OUTPUT cp clone.json clone_before.json fi - name: update clone.json run: | - curl https://raw.githubusercontent.com/nathanthaler/github-clone-count-badge/master/main.py > main.py - python3 main.py + set -euo pipefail + python3 << 'EOF' + import json + + # Read current clone data from GitHub API + with open('clone.json', 'r') as fh: + now = json.load(fh) + + # Read previous accumulated data + with open('clone_before.json', 'r') as fh: + before = json.load(fh) + + # Create timestamp index for efficient lookup + timestamps = {before['clones'][i]['timestamp']: i for i in range(len(before['clones']))} + + # Start with previous data and merge in new data + latest = dict(before) + for i in range(len(now['clones'])): + timestamp = now['clones'][i]['timestamp'] + if timestamp in timestamps: + # Update existing timestamp with new data + latest['clones'][timestamps[timestamp]] = now['clones'][i] + else: + # Add new timestamp + latest['clones'].append(now['clones'][i]) + + # Recalculate totals from all clones + latest['count'] = sum(map(lambda x: int(x['count']), latest['clones'])) + latest['uniques'] = sum(map(lambda x: int(x['uniques']), latest['clones'])) + + # Aggregate old data by month if we have more than 100 timestamps + if len(timestamps) > 100: + remove_this = [] + clones = latest['clones'] + for i in range(len(timestamps) - 35): + clones[i]['timestamp'] = clones[i]['timestamp'][:7] # Truncate to YYYY-MM + if clones[i]['timestamp'] == clones[i+1]['timestamp'][:7]: + clones[i+1]['count'] += clones[i]['count'] + clones[i+1]['uniques'] += clones[i]['uniques'] + remove_this.append(clones[i]) + + for item in remove_this: + clones.remove(item) + + # Write updated data back + with open('clone.json', 'w', encoding='utf-8') as fh: + json.dump(latest, fh, ensure_ascii=False, indent=4) + EOF - name: Update gist with latest count run: | + set -euo pipefail content=$(sed -e 's/\\/\\\\/g' -e 's/\t/\\t/g' -e 's/\"/\\"/g' -e 's/\r//g' "clone.json" | sed -E ':a;N;$!ba;s/\r{0,1}\n/\\n/g') echo '{"description": "${{ github.repository }} clone statistics", "files": {"clone.json": {"content": "'"$content"'"}}}' > post_clone.json - curl -s -X PATCH \ + if ! curl -f -s -X PATCH \ --user "${{ github.actor }}:${{ secrets.SECRET_TOKEN }}" \ -H "Content-Type: application/json" \ - -d @post_clone.json https://api.github.com/gists/${{ steps.set_id.outputs.GIST }} > /dev/null 2>&1 + -d @post_clone.json "https://api.github.com/gists/${{ steps.set_id.outputs.GIST }}" > /dev/null; then + echo "Error: Failed to update gist" + exit 1 + fi if [ ! -f CLONE.md ]; then shields="https://img.shields.io/badge/dynamic/json?color=success&label=Clone&query=count&url=" url="https://gist.githubusercontent.com/${{ github.actor }}/${{ steps.set_id.outputs.GIST }}/raw/clone.json" repo="https://gist.githubusercontent.com/${{ github.actor }}/${{ steps.set_id.outputs.GIST }}/raw/clone.json" - echo ''> CLONE.md - echo ' - **Markdown** - ```markdown' >> CLONE.md - echo "[![GitHub Clones]($shields$url&logo=github)]($repo)" >> CLONE.md - echo ```' >> CLONE.md - + echo '' > CLONE.md + echo '**Markdown**' >> CLONE.md + echo '```markdown' >> CLONE.md + echo "[![GitHub Clones](${shields}${url}&logo=github)](${repo})" >> CLONE.md + echo '```' >> CLONE.md + git add CLONE.md git config --global user.name "GitHub Action" git config --global user.email "action@github.com" git commit -m "create clone count badge" fi - name: Push - uses: ad-m/github-push-action@master + uses: ad-m/github-push-action@v0.8.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - +