From bd86a3c1ddf33a7e5f342ec4e8dfde715c18e664 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 29 Jun 2026 00:48:06 +0200 Subject: [PATCH] feat(#2081): CI lint for comment blocks over 30 lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Flags any contiguous comment block longer than 30 lines — the threshold above which a why/impl-notes block should move to docs/ rather than live in-code (the #2077 rubric, made self-enforcing). - scripts/check-comment-blocks.sh: git+awk, mirrors check-issue-refs.sh's capture-output shape (robust to xargs batching). Handles # line comments (nix/sh), // line comments (rs/js/ts), and /* */ (rs/js/ts/css) + (html) block comments. Blank separates line-comment blocks; a blank inside a /* */ / block stays part of it. lint:allow-long-comment escape hatch. Threshold is a tunable constant. - ci.yml: own 'comment-block lint' job, kept OUT of required checks while the tree settles (red signal, not a merge gate), like the tracker-tag lint. Current tree has 6 blocks > 30 (all in the frontend + hive-c0re-core #2077 slices, none in nix/infra): matrix-accounts.js, permissions.js, stream-worker.js, terminal.js, hivectl.rs:1036, assets.rs. Non-required, so non-blocking — they're the remaining #2077 targets for those area owners. --- .forgejo/workflows/ci.yml | 15 +++++++ scripts/check-comment-blocks.sh | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) create mode 100755 scripts/check-comment-blocks.sh diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml index 985a80fc..dc76b499 100644 --- a/.forgejo/workflows/ci.yml +++ b/.forgejo/workflows/ci.yml @@ -40,3 +40,18 @@ jobs: # merge. Promote to a required check once the tree is clean. # See scripts/check-issue-refs.sh. run: sh scripts/check-issue-refs.sh + + comment-blocks: + name: comment-block lint + runs-on: [hive-ci] + # Pure git+awk — seconds. + timeout-minutes: 5 + steps: + - uses: actions/checkout@v3 + - name: lint + # Flags contiguous comment blocks over 30 lines (a giant prose + # block belongs in docs/ as implementation notes, not in source). + # Own job, kept out of required checks while the tree settles: a + # hit reds this check without blocking merge. Promote to required + # once clean. See scripts/check-comment-blocks.sh. + run: sh scripts/check-comment-blocks.sh diff --git a/scripts/check-comment-blocks.sh b/scripts/check-comment-blocks.sh new file mode 100755 index 00000000..c3b90fe6 --- /dev/null +++ b/scripts/check-comment-blocks.sh @@ -0,0 +1,76 @@ +#!/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, kept OUT of the required checks while +# the tree settles: a hit turns the job red (visible, non-blocking) without +# blocking merge. Promote to required once the tree is clean. +# +# 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 `/* */` (rs/js/ts/css) + `` (html) +# block comments. 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|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 == "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