#!/bin/sh # CI lint: flags tracker tags (a hash followed by an issue number) anywhere # in tracked text, source or docs. The hive convention is prose, not # tracker tags — in code because tags rot (they point at moving targets and # leak tracker coupling into the source tree); in markdown because the # forge's public mirror carries no issue/PR data at all, so *any* `#N` # form — bare, qualified `owner/repo#N`, or an ambiguous glued `owner#N` — # is equally dead weight for a public reader. No exemption for markdown: # that used to exist ("prose docs may cite the tracker with a bare `#N`") # and got dropped once that read as still allowing exactly the kind of # reference the public-mirror problem rules out. # # Emits a CI error annotation per hit and exits 1 if any tag is found, 0 # otherwise. It runs as its own CI job and IS a required check on the forge # (branch protection) — a hit blocks merge. # # Scope: every tracked `*.rs *.nix *.js *.ts *.tsx *.css *.html *.md`. The # pattern matches a hash, 2-5 digits, then a non-alphanumeric char or # end-of-line. A real tracker tag is never glued to a letter, so the # trailing class skips both letter-bearing / 6-8-digit hex colours (the # digit run breaks or overruns) and digit-runs followed by a letter — e.g. # hash-route fragments like #24h. Residual: a pure-numeric short hex (e.g. # three identical digits) trips it — write the six-digit form to dodge. # # Escape hatch: a line containing the marker `lint:allow` is exempt. # Reserve it for genuine `#` that aren't tracker tags — e.g. a # `#123` markdown-heading example or hash-prefixed test-input data — and # keep a short reason next to the marker. Don't use it to keep a real # tracker tag; rewrite those to prose (or a full issue URL) instead. set -eu pattern='#[0-9]{2,5}([^0-9a-zA-Z]|$)' # `/dev/null` forces grep to always print a filename prefix, even when # xargs hands it a single file. `-r`/`-0` keep it robust to odd paths and # an empty file list. Lines carrying the `lint:allow` marker are dropped # (legitimate non-tracker `#`; see the header). hits="$( git ls-files -z '*.rs' '*.nix' '*.js' '*.ts' '*.tsx' '*.css' '*.html' '*.md' \ | xargs -0 -r grep -nE "$pattern" /dev/null 2>/dev/null \ | grep -v 'lint:allow' || true )" if [ -n "$hits" ]; then echo "$hits" | while IFS=: read -r file lineno _; do printf '::error file=%s,line=%s::tracker tag — write prose or a full issue URL, not a hash-number tag (see /knowledge/hive-rules.md)\n' "$file" "$lineno" done count="$(printf '%s\n' "$hits" | wc -l | tr -d ' ')" printf 'check-issue-refs: %s tracker tag(s) found\n' "$count" >&2 exit 1 fi exit 0