|
| 1 | +#!/usr/bin/env bash |
| 2 | +# GitHub Marketplace metadata guards for action.yml, runnable locally |
| 3 | +# (`bash tools/check-action-metadata.sh`) and as the `action-metadata` flake |
| 4 | +# check on every PR. |
| 5 | +# |
| 6 | +# Why this exists: GitHub validates this metadata only when a release is |
| 7 | +# published to the Marketplace, in the release UI. Nothing in `zola check`, the |
| 8 | +# formatter, the test suite, or the release guards looks at it. So an invalid |
| 9 | +# value merges green, releases green, and the only symptom is the Marketplace |
| 10 | +# listing going 404 — which is exactly how v1.7.2 delisted the action: the |
| 11 | +# `description` had grown to 133 characters against a 125-character cap, and the |
| 12 | +# failure surfaced only as "Your action.yml needs changes before it can be |
| 13 | +# published" in the UI, after the tag was already immutable. |
| 14 | +# |
| 15 | +# Limits below are GitHub's documented Marketplace requirements. `::error::` |
| 16 | +# lines are GitHub Actions annotations; harmless when run locally. |
| 17 | +set -euo pipefail |
| 18 | + |
| 19 | +file="${1:-action.yml}" |
| 20 | +status=0 |
| 21 | + |
| 22 | +# GitHub's cap. The message says "less than 125 characters", so 125 itself is |
| 23 | +# over the line and the check is >=. |
| 24 | +readonly DESCRIPTION_MAX=125 |
| 25 | + |
| 26 | +field() { yq -r ".$1 // \"\"" "$file"; } |
| 27 | + |
| 28 | +name="$(field name)" |
| 29 | +description="$(field description)" |
| 30 | +icon="$(field branding.icon)" |
| 31 | +color="$(field branding.color)" |
| 32 | + |
| 33 | +# A listing needs a name, and GitHub rejects one containing "GitHub". |
| 34 | +if [ -z "$name" ]; then |
| 35 | + echo "::error file=$file::action.yml has no 'name'; the Marketplace listing requires one." |
| 36 | + status=1 |
| 37 | +elif printf '%s' "$name" | grep -qi 'github'; then |
| 38 | + echo "::error file=$file::action name '$name' contains \"GitHub\", which the Marketplace rejects." |
| 39 | + status=1 |
| 40 | +else |
| 41 | + echo "name: '$name' ✓" |
| 42 | +fi |
| 43 | + |
| 44 | +# The limit that actually bit us. |
| 45 | +if [ -z "$description" ]; then |
| 46 | + echo "::error file=$file::action.yml has no 'description'; the Marketplace listing requires one." |
| 47 | + status=1 |
| 48 | +elif [ "${#description}" -ge "$DESCRIPTION_MAX" ]; then |
| 49 | + echo "::error file=$file::action.yml 'description' is ${#description} characters; the Marketplace requires fewer than ${DESCRIPTION_MAX}. Publishing a release to the Marketplace will fail and the existing listing will 404. Shorten it — put runner requirements and other detail in the README, which is the listing body." |
| 50 | + status=1 |
| 51 | +else |
| 52 | + echo "description: ${#description}/${DESCRIPTION_MAX} characters ✓" |
| 53 | +fi |
| 54 | + |
| 55 | +# branding drives the listing's icon and tile colour; absent, the listing cannot |
| 56 | +# be published. GitHub accepts a fixed colour set. |
| 57 | +if [ -z "$icon" ]; then |
| 58 | + echo "::error file=$file::action.yml has no 'branding.icon'; required to publish to the Marketplace." |
| 59 | + status=1 |
| 60 | +else |
| 61 | + echo "branding.icon: '$icon' ✓" |
| 62 | +fi |
| 63 | + |
| 64 | +case "$color" in |
| 65 | + white | yellow | blue | green | orange | red | purple | gray-dark) |
| 66 | + echo "branding.color: '$color' ✓" |
| 67 | + ;; |
| 68 | + "") |
| 69 | + echo "::error file=$file::action.yml has no 'branding.color'; required to publish to the Marketplace." |
| 70 | + status=1 |
| 71 | + ;; |
| 72 | + *) |
| 73 | + echo "::error file=$file::branding.color '$color' is not one of GitHub's accepted values (white, yellow, blue, green, orange, red, purple, gray-dark)." |
| 74 | + status=1 |
| 75 | + ;; |
| 76 | +esac |
| 77 | + |
| 78 | +if [ "$status" -ne 0 ]; then |
| 79 | + echo "action.yml would be rejected by the GitHub Marketplace." >&2 |
| 80 | +fi |
| 81 | +exit "$status" |
0 commit comments