#!/usr/bin/env bash # UI drift ratchet — a one-way gate that prevents UI legacy patterns from # growing while UI5–UI8 migrations are still in flight. # # How it works: # - Reads tracked baseline counts from scripts/ui-drift-baseline.json # - Counts current matches per category by re-using the same patterns # as scripts/ui-drift-audit.sh # - FAILS if any category COUNT is GREATER than its baseline # - PASSES (and reports the delta) if counts equal or fall below baseline # # Usage: # ./scripts/ui-drift-ratchet.sh # check against baseline (CI mode) # ./scripts/ui-drift-ratchet.sh --update # rewrite baseline to current counts # # (commit the result with a UI # # migration that lowered them) set -euo pipefail ROOT="$(git rev-parse --show-toplevel)" cd "$ROOT" BASELINE_FILE="scripts/ui-drift-baseline.json" if [[ ! -f "$BASELINE_FILE" ]]; then echo "FAIL: missing $BASELINE_FILE" >&2 exit 2 fi MODE="check" if [[ "${1:-}" == "--update" ]]; then MODE="update" fi # Detect ripgrep availability with a grep fallback (same logic as ui-drift-audit.sh). if command -v rg >/dev/null 2>&1; then SEARCH_BACKEND="rg" else SEARCH_BACKEND="grep" fi count_matches() { local pattern="$1" shift local n if [[ "$SEARCH_BACKEND" == "rg" ]]; then n=$(rg -n "$pattern" "$@" --glob '!**/*.test.*' 2>/dev/null | wc -l | tr -d ' ') else n=$(grep -rnE "$pattern" "$@" \ --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" \ --exclude="*.test.*" --exclude="*.spec.*" \ --exclude-dir=node_modules --exclude-dir=.next 2>/dev/null | wc -l | tr -d ' ') fi echo "$n" } # Read baseline values. Avoid jq dependency by using grep + sed. read_baseline() { local key="$1" grep -E "\"$key\"" "$BASELINE_FILE" | head -1 | sed -E 's/.*: *([0-9]+).*/\1/' } BASE_RAW=$(read_baseline raw_interactive_controls) BASE_LEGACY=$(read_baseline legacy_global_surface_classes) BASE_COLORS=$(read_baseline hardcoded_color_literals) BASE_IMPORTS=$(read_baseline direct_bytelyst_ui_imports_outside_adapter) CUR_RAW=$(count_matches '/dev/null || echo 0) CUR_IMPORTS=$((CUR_IMPORTS_RAW - ADAPTER_LINES)) if [[ "$CUR_IMPORTS" -lt 0 ]]; then CUR_IMPORTS=0; fi if [[ "$MODE" == "update" ]]; then cat >"$BASELINE_FILE" <&2 echo " Migrate the new call site(s) to @bytelyst/ui primitives via" >&2 echo " @/components/ui/Primitives, or — if migration lowered other" >&2 echo " counts — run 'pnpm run audit:ui:ratchet:update' and commit." >&2 exit 1 fi echo echo "OK — no UI drift regressions above baseline."