Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions .github/workflows/gallery-downloads.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
name: Gallery downloads badge

# The PowerShell Gallery's per-package total (DownloadCount, which the page and the shields.io
# powershellgallery/dt badge read) is broken: 0 for this module, and stuck or wrong for others
# (PowerShell/PowerShellGallery#347, #350). The per-version counts still move, so this sums them
# and publishes a shields.io endpoint badge on the badges branch. The README reads it through
# https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/<owner>/<repo>/badges/downloads.json
#
# Reusable: another module's repository calls this workflow with its package name and gets the
# same badge on its own badges branch. The schedule below is this repository's; a caller sets
# its own.

on:
schedule:
- cron: '17 6 * * *'
workflow_dispatch:
workflow_call:
inputs:
package:
description: The Gallery package id. Defaults to the repository name.
type: string
required: false
label:
description: The badge's left-hand text.
type: string
required: false
default: Gallery downloads

permissions:
contents: write

jobs:
badge:
name: Sum the version downloads
runs-on: ubuntu-latest
steps:
- name: Sum VersionDownloadCount over every version
id: sum
shell: pwsh
env:
PACKAGE: ${{ inputs.package || github.event.repository.name }}
LABEL: ${{ inputs.label || 'Gallery downloads' }}
run: |
$ErrorActionPreference = 'Stop'
# The v2 feed is OData Atom. Invoke-RestMethod hands back the entries but not the paging
# link, so the pages are read as XML; a package with many versions spans several.
$uri = "https://www.powershellgallery.com/api/v2/FindPackagesById()?id='$env:PACKAGE'"
$versions = 0
$total = 0L
$brokenTotal = $null
while ($uri) {
$xml = [xml](Invoke-WebRequest -Uri $uri -UseBasicParsing).Content
foreach ($entry in @($xml.feed.entry)) {
$versions++
$total += [long]$entry.properties.VersionDownloadCount.'#text'
$brokenTotal = $entry.properties.DownloadCount.'#text'
}
$uri = @($xml.feed.link | Where-Object rel -eq 'next' | Select-Object -First 1).href
}
if ($versions -eq 0) { throw "The Gallery returned no versions for '$env:PACKAGE'." }
Write-Host "$env:PACKAGE : $versions version(s), $total downloads summed (the Gallery's own total says $brokenTotal)"

$badge = [ordered]@{
schemaVersion = 1
label = $env:LABEL
message = $total.ToString('N0', [cultureinfo]::InvariantCulture)
color = 'blue'
cacheSeconds = 3600

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An hour of shields.io caching, because the number changes once a day at most and the raw.githubusercontent.com URL behind it is itself cached for a few minutes; without this shields re-fetches on every README view.

} | ConvertTo-Json -Compress
Set-Content -Path downloads.json -Value $badge -NoNewline
"total=$total" >> $env:GITHUB_OUTPUT
"versions=$versions" >> $env:GITHUB_OUTPUT

# The badges branch holds nothing but the badge files, with no history in common with main,
# and gains a commit only when a number changed.
- name: Publish to the badges branch
shell: bash
env:
TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TOTAL: ${{ steps.sum.outputs.total }}
VERSIONS: ${{ steps.sum.outputs.versions }}
run: |
set -euo pipefail
mkdir badges && cd badges
git init -q -b badges

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A fresh repository in a scratch folder rather than a checkout of main: the badges branch is an orphan with no history in common with main, and this keeps the badge commits out of the module's history and the module's files out of the badge branch. The fetch below picks up the existing branch when there is one, so the first run creates it and every later run appends.

git config user.name 'github-actions[bot]'
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
remote="https://x-access-token:${TOKEN}@github.com/${REPO}.git"
git remote add origin "$remote"
if git fetch -q origin badges 2>/dev/null; then
git reset -q --hard origin/badges
fi
if [ -f downloads.json ] && cmp -s downloads.json ../downloads.json; then

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Byte comparison of the JSON rather than of the number, so a change to the label or colour also publishes, and a run that moved nothing leaves the branch alone. Over a quiet month that is the difference between no commits and thirty.

echo "Badge unchanged at ${TOTAL} downloads; nothing to publish."
exit 0
fi
cp ../downloads.json downloads.json
git add downloads.json
git commit -q -m "badge: ${TOTAL} downloads across ${VERSIONS} versions"
git push -q origin badges
echo "Published ${TOTAL} downloads to the badges branch."
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ release notes.

## [Unreleased]

### Added

- A Gallery downloads badge in the README that sums the per-version download counts, because the
Gallery's own total is broken (PowerShell/PowerShellGallery#347 and #350: 0 for this module, stuck
or wrong for others). `.github/workflows/gallery-downloads.yml` reads every version from the v2
feed daily and publishes a shields.io endpoint file on the `badges` branch when the number moves;
it is reusable from another module's repository through `workflow_call` with the package name.

### Changed

- The `help` job in `quality-gates.yml`, the one that runs on Linux, also analyzes a folder of two
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# IntuneScriptLab

[![PowerShell Gallery](https://img.shields.io/powershellgallery/v/IntuneScriptLab?style=flat-square&logo=powershell&label=Gallery)](https://www.powershellgallery.com/packages/IntuneScriptLab)
[![Gallery downloads](https://img.shields.io/endpoint?url=https%3A%2F%2Fraw.githubusercontent.com%2Ffadwen%2FIntuneScriptLab%2Fbadges%2Fdownloads.json&style=flat-square)](https://www.powershellgallery.com/packages/IntuneScriptLab)
[![Quality Gates](https://img.shields.io/github/actions/workflow/status/fadwen/IntuneScriptLab/quality-gates.yml?branch=main&style=flat-square&label=quality%20gates)](https://github.com/fadwen/IntuneScriptLab/actions/workflows/quality-gates.yml)
[![PowerShell 5.1](https://img.shields.io/badge/PowerShell-5.1+-blue?style=flat-square&logo=powershell)](https://github.com/PowerShell/PowerShell)
[![Pester](https://img.shields.io/badge/Tested_with-Pester_6-green?style=flat-square)](https://pester.dev)
Expand Down
Loading