diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 0ed83313..e87d724a 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -57,6 +57,17 @@ jobs: - name: lint run: sh scripts/check-attribution-trailers.sh + dashboard-descriptions: + name: dashboard-description lint + runs-on: [hive-ci] + timeout-minutes: 5 + steps: + - uses: actions/checkout@v3 + - name: lint + # jq isn't a project dependency elsewhere in this repo — same + # nix-shell-for-one-tool shape the vale/shellcheck jobs above use. + run: nix shell nixpkgs#jq --command sh scripts/check-dashboard-descriptions.sh + shellcheck: name: shellcheck runs-on: [hive-ci] diff --git a/scripts/check-dashboard-descriptions.sh b/scripts/check-dashboard-descriptions.sh new file mode 100755 index 00000000..9dd1e7af --- /dev/null +++ b/scripts/check-dashboard-descriptions.sh @@ -0,0 +1,90 @@ +#!/bin/sh +# CI lint: keeps grafana dashboard panel/board descriptions short and free of +# meta-commentary — a tooltip answers "what is this number", not "how did we +# arrive at this panel" or "what did someone say about it". +# +# Two checks: length (over MAX_LEN means it became prose, not a short +# answer — fix by deleting the editorializing, not trimming words), and +# keywords (the operator's name, hardcoded rather than read from config — +# the class of mistake is what's caught, not one specific name; and a +# tracker tag `#1234`, reusing check-issue-refs.sh's own pattern and its +# same known false-positive class, e.g. `PKCS#11` — no `lint:allow` escape +# hatch here though, since JSON has no comment syntax to carry the marker; +# rephrase to drop the `#digits` shape instead). +# +# Deliberately NOT a blanket ban on the word "issue": forge/gitea panels +# legitimately show issue-tracker counts ("Currently open issues, across +# every repo." is exactly the short description this check wants to keep). +# The tracker-tag pattern is the part machine-checkable; a prose +# meta-reference with no `#N` needs a human reviewer, same as elsewhere. +# +# Uses `jq` — the right tool for JSON, unlike its zero-dependency siblings +# in this directory. This script does NOT nix-shell for it itself: the +# caller declares that dependency (see `pre-push` and +# `.forgejo/workflows/ci.yml`, the same `nix shell nixpkgs#jq --command sh +# ` shape the vale/shellcheck jobs already use for their own +# tools), so a leaf script never hides a nix invocation inside itself. +# +# Emits a CI error annotation per hit, exits 1 if any hit is found. +set -eu + +dashboards_dir="nix/host-modules/swarm-grafana/dashboards" +max_len=100 +tracker_pattern='#[0-9]{2,5}([^0-9a-zA-Z]|$)' + +files="$(find "$dashboards_dir" -maxdepth 1 -name '*.json' 2>/dev/null | sort)" +if [ -z "$files" ]; then + echo 'check-dashboard-descriptions: no dashboard json files found — path moved?' >&2 + exit 1 +fi + +violations=0 +checked=0 + +for f in $files; do + # One JSON object per description hit: {"title": "...", "description": "..."} + # -- recursive `..` walk catches both schema shapes uniformly, since in + # both a description always sits beside a sibling title in the same object. + while IFS= read -r row; do + [ -n "$row" ] || continue + title="$(printf '%s' "$row" | jq -r '.title')" + desc="$(printf '%s' "$row" | jq -r '.description')" + [ -n "$desc" ] || continue + checked=$((checked + 1)) + + len="$(printf '%s' "$desc" | wc -m | tr -d ' ')" + if [ "$len" -gt "$max_len" ]; then + violations=$((violations + 1)) + printf '::error file=%s::panel "%s" description is %s chars (max %s) — shorten it to what the number is, drop the reasoning\n' \ + "$f" "$title" "$len" "$max_len" + fi + + if printf '%s' "$desc" | grep -qiE 'mara'; then + violations=$((violations + 1)) + printf '::error file=%s::panel "%s" description names the operator — describe the metric, not who asked for the panel\n' \ + "$f" "$title" + fi + + if printf '%s' "$desc" | grep -qE "$tracker_pattern"; then + violations=$((violations + 1)) + printf '::error file=%s::panel "%s" description references a tracker issue — write prose that stands on its own\n' \ + "$f" "$title" + fi + done <&2 + exit 1 +fi + +if [ "$violations" -gt 0 ]; then + printf 'check-dashboard-descriptions: %s violation(s) across %s description(s) checked\n' \ + "$violations" "$checked" >&2 + exit 1 +fi + +echo "check-dashboard-descriptions: $checked description(s) checked, clean" >&2 +exit 0 diff --git a/scripts/pre-push b/scripts/pre-push index 0f6bf3c2..9cc2f5b2 100755 --- a/scripts/pre-push +++ b/scripts/pre-push @@ -31,5 +31,13 @@ if ! sh "$repo_root/scripts/check-attribution-trailers.sh"; then exit 1 fi +echo "pre-push: running dashboard-description lint..." >&2 +# jq isn't a project dependency elsewhere in this repo, so the check script +# assumes nothing about the caller's PATH — nix-shell it here instead. +if ! nix shell nixpkgs#jq --command sh "$repo_root/scripts/check-dashboard-descriptions.sh"; then + echo "pre-push: dashboard-description lint FAILED — fix before pushing" >&2 + exit 1 +fi + echo "pre-push: lints passed ✓" >&2 exit 0