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
54 changes: 50 additions & 4 deletions .github/scripts/build-publish-summary.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
# "target_version": "0.2.2",
# "status": "published" # published | up-to-date | failed
# }
#
# For "published"/"up-to-date" cells, this renders a shields.io "Dynamic
# Regex Badge" that live-scrapes GitHub's public package-versions page for
# that pack + version's download count on every image load - no scraping
# infra or stored data of our own. NOTE: shields.io's dynamic/regex badge is
# documented as "experimental: may change or be removed at any time" - see
# CONTRIBUTING.md's "Releases & publishing" section.
set -euo pipefail

RESULTS_DIR="${1:?usage: build-publish-summary.sh <results-dir>}"
Expand Down Expand Up @@ -52,6 +59,46 @@ is_ext_language() {
return 1
}

# Escapes a string for safe embedding in an RE2 regex pattern (used to match
# a specific version tag literally in download_badge's search regex below).
regex_escape() {
printf '%s' "$1" | sed -E 's/[.[\*^$()+?{}|]/\\&/g'
}

# Percent-encodes a string for use in a URL query string. jq's @uri (like
# several other encoders, e.g. .NET's HttpUtility.UrlEncode) follows RFC
# 2396's "mark" characters and leaves * ( ) ' ! unescaped. A bare "*" or
# unbalanced "(" / ")" surviving into the final markdown can be misread as
# emphasis syntax or prematurely close a markdown link's destination parens
# (this bit us once already - see PR discussion), so percent-encode those
# explicitly on top of jq's output.
url_encode() {
jq -rn --arg s "$1" '$s|@uri' | sed -e 's/\*/%2A/g' -e 's/(/%28/g' -e 's/)/%29/g' -e "s/'/%27/g" -e 's/!/%21/g'
}

# Renders a shields.io Dynamic Regex Badge (see file header) for one
# package+version's live download count, labeled with the version and
# linked to that version's package page.
download_badge() {
local package="$1" version="$2"
local versions_url="https://github.com/${REPO}/pkgs/container/${package}/versions"
local tag_escaped
tag_escaped=$(regex_escape "$version")
# Finds this specific tag's row, then advances (non-greedily, since the
# page lists many tags) to the download icon/count that follows it.
local search="tag=${tag_escaped}\"[\\s\\S]*?octicon-download[\\s\\S]*?</svg>\\s*([\\d,]+)\\s*<span class=\"sr-only\">Version downloads"

local encoded_url encoded_search encoded_replace encoded_label
encoded_url=$(url_encode "$versions_url")
encoded_search=$(url_encode "$search")
encoded_replace=$(url_encode '$1 downloads')
encoded_label=$(url_encode "$version")

local badge_url="https://img.shields.io/badge/dynamic/regex?url=${encoded_url}&search=${encoded_search}&replace=${encoded_replace}&label=${encoded_label}&color=blue"
local pkg_url="https://github.com/${REPO}/pkgs/container/${package}?tag=${version}"
echo "[![${package} ${version} downloads](${badge_url})](${pkg_url})"
}

cell() {
local lang="$1" type="$2"
local entry
Expand All @@ -70,11 +117,10 @@ cell() {
version=$(echo "$entry" | jq -r '.target_version')
status=$(echo "$entry" | jq -r '.status')
previous=$(echo "$entry" | jq -r '.previous_version')
local url="https://github.com/${REPO}/pkgs/container/${package}?tag=${version}"

case "$status" in
published) echo "[${version}](${url}) πŸ†•" ;;
up-to-date) echo "[${version}](${url})" ;;
published) echo "$(download_badge "$package" "$version") πŸ†•" ;;
up-to-date) echo "$(download_badge "$package" "$version")" ;;
failed)
if [ -n "$previous" ] && [ "$previous" != "null" ]; then
echo "⚠️ publish failed (still \`${previous}\`)"
Expand All @@ -88,7 +134,7 @@ cell() {

echo "## Publish summary"
echo
echo "_Generated by [publish.yml run #${GITHUB_RUN_NUMBER:-}](${RUN_URL}) β€” πŸ†• marks a package republished by this run._"
echo "_Generated by [publish.yml run #${GITHUB_RUN_NUMBER:-}](${RUN_URL}) β€” πŸ†• marks a package republished by this run. Download counts are live (fetched by shields.io on each view, not stored here) and may take a little while to reflect the newest activity._"
echo
echo "| Language | Queries (\`src\`) | Library (\`lib\`) | Extensions (\`ext\`) | Library sources (\`ext-library-sources\`) |"
echo "| --- | --- | --- | --- | --- |"
Expand Down
15 changes: 15 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,20 @@ fails), aggregates their per-pack results into the publish-summary and CodeQL li
version tables, and (on the release-cut `push` trigger only) upserts both tables into the GitHub
Release's notes.

Each published/up-to-date cell in the publish-summary table is a shields.io **Dynamic Regex
Badge**, labeled with the version and showing that pack version's download count (e.g. `0.6.0 |
2,559 downloads`), linked through to that version's GHCR package page. The badge's `url`/`search`
params point shields at GitHub's public `.../pkgs/container/<package>/versions` page and scrape the
count live on every image load - there's no scraping infra, cron, or stored data of our own; the
number simply reflects whatever GitHub reports at view time (shields.io caches responses briefly,
so it can lag live activity by a bit). **This relies on shields.io's `dynamic/regex` badge type,
which is explicitly documented upstream as "experimental: may change or be removed at any time"**
β€” if it ever breaks or is removed, the affected cells will render as a broken image/`invalid`
badge rather than failing the workflow; see `download_badge()` in
[`build-publish-summary.sh`][build-publish-summary-script] for the implementation and its comments
on why the URL/regex must be percent-encoded a specific way (jq's `@uri`, plus manual `%2A`/`%28`/
`%29` fixups) to survive round-tripping through markdown without corruption.

**Each `<language>` Γ— `<pack type>` combination is checked and published completely
independently.** For every matrix entry, the job compares the `version:` in that one pack's
`qlpack.yml` on `main` to the version currently published on [GHCR][ghcr-packages], and only
Expand Down Expand Up @@ -414,6 +428,7 @@ Please do get in touch (privacy@github.com) if you have any questions about this
[detect-codeql-release-workflow]: ./.github/workflows/detect-codeql-release.yml
[copilot-setup-steps-workflow]: ./.github/workflows/copilot-setup-steps.yml
[pin-codeql-library-versions-script]: ./.github/scripts/pin-codeql-library-versions.sh
[build-publish-summary-script]: ./.github/scripts/build-publish-summary.sh
[codeql-cli-binaries]: https://github.com/github/codeql-cli-binaries/releases
[release-config]: ./.release.yml
[patch-release-me]: https://github.com/42ByteLabs/patch-release-me
Expand Down
Loading