Desktop Download Badges #14
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
| # Refreshes the desktop and combined download counters behind the README badges. | |
| # | |
| # shields.io cannot do this on its own: its asset wildcards silently report 0 | |
| # (a tag whose .dmg had 39,877 downloads returns 0 for `*.dmg`), exact asset | |
| # names embed the version so they break on every release, and `dynamic/json` | |
| # rejects every filter expression. So we sum the counts here and publish them | |
| # as shields `endpoint` documents. | |
| # | |
| # The documents land on the orphan `badges` branch, never on `main` — `main` is | |
| # protected and rejects pushes from everyone, CI included. | |
| name: Desktop Download Badges | |
| on: | |
| push: | |
| branches: [main] | |
| paths: | |
| - .github/workflows/desktop-download-badges.yml | |
| schedule: | |
| # Once a day is plenty; the counters move slowly and the API is rate-limited. | |
| - cron: '17 4 * * *' | |
| workflow_dispatch: {} | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: desktop-download-badges | |
| cancel-in-progress: true | |
| jobs: | |
| refresh: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout the badges branch | |
| uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # pinned from v4 | |
| with: | |
| ref: badges | |
| persist-credentials: true | |
| - name: Refresh download counts | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| # Desktop installers shipped from this repo up to v0.1.0 and move to | |
| # pythinker-desktop-releases from the next release on. Summing both | |
| # keeps the counters continuous across that move. | |
| RELEASES_REPOS: PyModel/pythinker-code PyModel/pythinker-desktop-releases | |
| run: | | |
| set -euo pipefail | |
| # --paginate walks every release, so the totals cover the whole | |
| # history rather than just the latest tag. jq -s concatenates the one | |
| # array each repo produces into a single list of releases. | |
| for repo in ${RELEASES_REPOS}; do | |
| gh api --paginate "repos/${repo}/releases" | |
| done | jq -s 'add' > releases.json | |
| # An empty suffix matches every asset, which is what the combined | |
| # counter wants; `endswith("")` is true for any string. | |
| write_endpoint() { | |
| local label="$1" message="$2" out="$3" | |
| jq -n --arg label "$label" --arg message "$message" '{ | |
| schemaVersion: 1, | |
| label: $label, | |
| message: $message, | |
| color: "4D6BFE" | |
| }' > "$out" | |
| echo "${label}: ${message}" | |
| } | |
| write_desktop_badge() { | |
| local suffix="$1" label="$2" out="$3" | |
| local total | |
| total="$(jq --arg s "$suffix" ' | |
| [ .[].assets[] | |
| | select(.name | ascii_downcase | endswith($s)) | |
| | .download_count | |
| ] | add // 0 | |
| ' releases.json)" | |
| write_endpoint "$label" "$total" "$out" | |
| } | |
| write_desktop_badge '.dmg' 'macOS .dmg' desktop-dmg.json | |
| write_desktop_badge '.exe' 'Windows .exe' desktop-exe.json | |
| # The shields `github/downloads/.../total` route reads one repository, | |
| # so the combined counter has to be summed here like the other two. | |
| write_desktop_badge '' 'desktop' desktop-total.json | |
| # Match the site: desktop and npm can represent the same users, so | |
| # publish the larger counter instead of adding them together. | |
| npm_downloads="$( | |
| curl --fail --silent --show-error --location \ | |
| --retry 3 --retry-all-errors --connect-timeout 10 --max-time 30 \ | |
| 'https://api.npmjs.org/downloads/point/last-year/%40pymodel%2Fpythinker-code' \ | |
| | jq -er '.downloads | numbers | select(. >= 0 and floor == .)' | |
| )" | |
| desktop_downloads="$(jq -er '.message | tonumber' desktop-total.json)" | |
| downloads="$( | |
| jq -n --argjson desktop "$desktop_downloads" --argjson npm "$npm_downloads" \ | |
| '[$desktop, $npm] | max' | |
| )" | |
| write_endpoint 'downloads' "$downloads" downloads-total.json | |
| rm -f releases.json | |
| - name: Publish if the counts moved | |
| run: | | |
| set -euo pipefail | |
| git config user.name 'github-actions[bot]' | |
| git config user.email 'github-actions[bot]@users.noreply.github.com' | |
| # Stage before comparing: a document added in this run is untracked, | |
| # and `git diff` on an untracked path reports no change at all. | |
| git add desktop-dmg.json desktop-exe.json desktop-total.json downloads-total.json | |
| if git diff --cached --quiet; then | |
| echo 'Counts unchanged; nothing to publish.' | |
| exit 0 | |
| fi | |
| git commit -m 'chore: refresh download counts' | |
| git push origin badges |