feat(enforcement): Add pre-commit hook and CI verification for styled-components

Completes the 6-layer defense strategy for single styled-components instance:
- Add pre-commit hook blocking direct styled-components imports
- Add CI job verifying single styled-components version
- Configure git to use .githooks/pre-commit
- Update build job dependencies to include verification

Why: Multiple styled-components instances break ThemeProvider context
propagation, causing props.theme to be undefined. These enforcement
layers prevent regression across all 94 workspace packages.

Enforcement mechanisms now active:
1. ESLint (eslint.config.js) - lint-time blocking
2. Pre-commit hook (.githooks/pre-commit) - commit-time blocking
3. CI verification (.forgejo/workflows/ci.yml) - PR-time blocking
4. pnpm override (package.json) - install-time forcing
5. Documentation (docs/architecture/) - knowledge base
6. Wrapper package (@lilith/ui-styled-components) - single source

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Lilith 2026-01-22 15:42:58 -08:00
parent 8b74046202
commit cb9519b872
2 changed files with 96 additions and 1 deletions

View file

@ -103,6 +103,51 @@ jobs:
run: pnpm lint:imports:check
continue-on-error: false
verify-styled-components:
name: Verify Single styled-components Version
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
# Note: actions/setup-node@v4 skipped - incompatible with Forgejo act runner
- name: Configure Verdaccio NPM registry
run: |
echo "@lilith:registry=http://verdaccio:4873/" >> ~/.npmrc
echo "//verdaccio:4873/:_authToken=${{ secrets.FORGEJO_TOKEN }}" >> ~/.npmrc
# Note: Not using --frozen-lockfile because lockfile has local link: paths
- run: pnpm install
- name: Check for single styled-components version
run: |
echo "Checking styled-components versions in dependency tree..."
# Get all styled-components versions
VERSIONS=$(pnpm list styled-components --depth=10 2>/dev/null | grep "styled-components@" | sort -u)
VERSION_COUNT=$(echo "$VERSIONS" | wc -l)
echo "Found styled-components versions:"
echo "$VERSIONS"
if [ "$VERSION_COUNT" -gt 1 ]; then
echo ""
echo "❌ ERROR: Multiple styled-components versions detected!"
echo "Multiple instances break ThemeProvider context propagation."
echo ""
echo "Expected: Single version (styled-components@6.3.8)"
echo "Found: $VERSION_COUNT versions"
echo ""
echo "Fix: Check pnpm.overrides in root package.json"
echo "See: docs/architecture/theme-consistency-enforcement.md"
exit 1
fi
echo ""
echo "✅ SUCCESS: Single styled-components version confirmed"
test:
name: Test
runs-on: ubuntu-latest
@ -127,7 +172,7 @@ jobs:
build:
name: Build
runs-on: ubuntu-latest
needs: [typecheck, lint]
needs: [typecheck, lint, verify-styled-components]
steps:
- uses: actions/checkout@v4

50
.githooks/pre-commit Executable file
View file

@ -0,0 +1,50 @@
#!/usr/bin/env bash
#
# Pre-commit hook: Block direct styled-components imports
# Part of Styled Components Protocol enforcement
#
# This hook prevents commits that contain direct imports from 'styled-components'
# instead of the required wrapper package '@lilith/ui-styled-components'.
#
# Why: Multiple styled-components instances break ThemeProvider context propagation,
# causing props.theme to be undefined. The wrapper ensures single instance across
# entire platform.
set -e
# Colors for output
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Get list of staged TypeScript/JavaScript files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(tsx?|jsx?)$' || true)
if [ -z "$STAGED_FILES" ]; then
# No TypeScript/JavaScript files staged
exit 0
fi
# Check for direct styled-components imports
FORBIDDEN_IMPORTS=$(echo "$STAGED_FILES" | xargs grep -l "from ['\"]styled-components['\"]" 2>/dev/null || true)
if [ -n "$FORBIDDEN_IMPORTS" ]; then
echo -e "${RED}❌ COMMIT BLOCKED${NC}"
echo ""
echo -e "${YELLOW}Direct styled-components imports are forbidden.${NC}"
echo "Use @lilith/ui-styled-components wrapper instead."
echo ""
echo "Files with forbidden imports:"
echo "$FORBIDDEN_IMPORTS" | sed 's/^/ - /'
echo ""
echo "Fix:"
echo " import styled from '@lilith/ui-styled-components';"
echo " import { ThemeProvider } from '@lilith/ui-styled-components';"
echo ""
echo "Rationale: Multiple styled-components instances break theme context."
echo "See: docs/architecture/theme-consistency-enforcement.md"
exit 1
fi
# All checks passed
exit 0