#!/bin/sh # CI lint: flags a contiguous comment block longer than MAX lines. A giant # prose block in source is a signal it should live in docs/ as an # "implementation notes" section instead — short "why"/invariant/contract # comments stay, walls of text move out. See the hive convention in #2077. # # Emits a CI error annotation per offending block and exits 1 if any block # exceeds MAX. Runs as its own CI job and IS a required check on the forge # (branch protection) — a hit blocks merge. # # Scope: tracked *.rs *.nix *.sh *.js *.ts *.css *.html. Markdown is exempt # (it is prose by nature). Comment forms: `#` line comments (nix/sh), `//` # line comments (rs/js/ts), and `/* */` block comments (nix/rs/js/ts/css) + # `` (html). A blank line separates two line-comment blocks (does NOT # extend a run); a blank inside a `/* */` / `` block stays part of it. # # Escape hatch: a `lint:allow-long-comment` marker anywhere in a block # exempts it (reserve for a genuinely irreducible block; prefer relocating). set -eu MAX=30 # Collect annotations into a variable (not via xargs/awk exit codes) so a # hit in any xargs batch is preserved — mirrors scripts/check-issue-refs.sh. hits="$( git ls-files -z '*.rs' '*.nix' '*.sh' '*.js' '*.ts' '*.css' '*.html' \ | xargs -0 -r awk -v MAX="$MAX" ' function mode_of(fn) { if (fn ~ /\.nix$/) return "nix" if (fn ~ /\.sh$/) return "hash" if (fn ~ /\.(rs|js|ts)$/) return "slash" if (fn ~ /\.css$/) return "cstyle" if (fn ~ /\.html$/) return "html" return "" } function flush() { if (run > MAX && !allow) printf "::error file=%s,line=%d::comment block of %d lines exceeds the %d-line max — move long prose to docs/ as an implementation-notes section (or mark lint:allow-long-comment)\n", curfile, start, run, MAX run = 0; allow = 0 } FNR == 1 { flush(); curfile = FILENAME; mode = mode_of(FILENAME); inblock = 0 } { if (mode == "") next c = 0 if (inblock) { c = 1 if (index($0, blockend) > 0) inblock = 0 } else if (mode == "hash") { if ($0 ~ /^[ \t]*#/) c = 1 } else if (mode == "nix") { # nix: `#` line comments AND `/* */` block comments. if ($0 ~ /^[ \t]*#/) c = 1 else if ($0 ~ /^[ \t]*\/\*/) { c = 1; if (index($0, "*/") == 0) { inblock = 1; blockend = "*/" } } } else if (mode == "slash") { if ($0 ~ /^[ \t]*\/\//) c = 1 else if ($0 ~ /^[ \t]*\/\*/) { c = 1; if (index($0, "*/") == 0) { inblock = 1; blockend = "*/" } } } else if (mode == "cstyle") { if ($0 ~ /^[ \t]*\/\*/) { c = 1; if (index($0, "*/") == 0) { inblock = 1; blockend = "*/" } } } else if (mode == "html") { if ($0 ~ /^[ \t]*") == 0) { inblock = 1; blockend = "-->" } } } if (c) { if (run == 0) start = FNR run++ if ($0 ~ /lint:allow-long-comment/) allow = 1 } else { flush() } } END { flush() } ' )" if [ -n "$hits" ]; then printf '%s\n' "$hits" count="$(printf '%s\n' "$hits" | grep -c '::error' || true)" printf 'check-comment-blocks: %s comment block(s) over %d lines found\n' "$count" "$MAX" >&2 exit 1 fi exit 0