#!/bin/sh # CI lint: flags tracker tags (a hash followed by an issue number) in # source comments. The hive convention is prose, not tracker tags, in # code (see /knowledge/hive-rules.md) — tags rot, they point at moving # targets and leak tracker coupling into the source tree. # # Usage: check-issue-refs.sh [warn|deny] # warn (default): emit a CI warning annotation per hit, exit 0. # deny: same annotations, but exit 1 if any hit is found. # # Rollout: starts in `warn` while the legacy backlog is cleaned up, # then flips to `deny` for a hard gate (the clippy-stricter playbook). # # Scope: tracked *.rs *.nix *.js *.ts *.css *.html. Markdown is exempt # (prose docs may legitimately cite the tracker). The pattern matches a # hash, 2-5 digits, then a non-hex char or end-of-line: that trailing # class skips CSS hex colours (letter-bearing or 6/8-digit) while still # catching tracker tags. Residual: a pure-numeric short hex (e.g. three # identical digits) trips it — write the six-digit form to dodge. set -eu mode="${1:-warn}" case "$mode" in warn | deny) ;; *) echo "usage: $0 [warn|deny]" >&2 exit 2 ;; esac pattern='#[0-9]{2,5}([^0-9a-fA-F]|$)' # `/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. hits="$( git ls-files -z '*.rs' '*.nix' '*.js' '*.ts' '*.css' '*.html' \ | xargs -0 -r grep -nE "$pattern" /dev/null 2>/dev/null || true )" if [ -n "$hits" ]; then echo "$hits" | while IFS=: read -r file lineno _; do printf '::warning file=%s,line=%s::tracker tag in source — write prose, 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 in source\n' "$count" >&2 [ "$mode" = deny ] && exit 1 fi exit 0