diff --git a/.gitignore b/.gitignore index 2a4cb88..6b3704b 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,9 @@ vendor/ eval-report.json cat-eval-report.json +# ── Root tooling (husky) ───────────────────── +/node_modules/ + # ── Web ────────────────────────────────────── /web/node_modules/ /web/.svelte-kit/ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..9e972a9 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,84 @@ +#!/bin/sh +# Pre-commit checks for marktvogt monorepo. +# Replaces .pre-commit-config.yaml (Python pre-commit framework). +# Install via: pnpm install (at repo root). Husky 9 reads this file directly. + +set -e + +red() { printf '\033[31m%s\033[0m\n' "$*" >&2; } + +# 1. Block direct commits to main. +branch=$(git symbolic-ref --short HEAD 2>/dev/null || echo "") +if [ "$branch" = "main" ]; then + red "ERROR: direct commits to main are blocked. Create a feature branch." + exit 1 +fi + +# 2. Detect whitespace errors and merge-conflict markers in staged hunks. +# Replaces pre-commit-hooks: trailing-whitespace, check-merge-conflict. +if ! git diff --cached --check; then + red "ERROR: whitespace errors or merge-conflict markers in staged changes." + exit 1 +fi + +# 3. Reject staged files larger than 500 KB (excluding crawler test fixtures). +# Replaces pre-commit-hooks: check-added-large-files. +big=$(git diff --cached --name-only --diff-filter=ACMR -z | + while IFS= read -r -d '' f; do + case "$f" in + backend/internal/domain/discovery/crawler/testdata/*) continue ;; + esac + [ -f "$f" ] || continue + size=$(wc -c <"$f" 2>/dev/null || echo 0) + if [ "$size" -gt 524288 ]; then + printf '%s (%s bytes)\n' "$f" "$size" + fi + done) +if [ -n "$big" ]; then + red "ERROR: large files staged (>500 KB):" + printf '%s\n' "$big" >&2 + exit 1 +fi + +# Helper: list staged files matching a pattern. +staged_match() { + git diff --cached --name-only --diff-filter=ACMR | grep -E "$1" || true +} + +# 4. Backend Go checks — only when backend/*.go is staged. +if [ -n "$(staged_match '^backend/.*\.go$')" ]; then + echo "→ backend: gofmt" + unformatted=$(cd backend && gofmt -l .) + if [ -n "$unformatted" ]; then + red "ERROR: gofmt would change these files:" + printf '%s\n' "$unformatted" >&2 + exit 1 + fi + + echo "→ backend: go vet" + ( cd backend && go vet ./... ) + + echo "→ backend: golangci-lint" + ( cd backend && golangci-lint run --config .golangci.yml ./... ) +fi + +# 5. go.mod / go.sum tidy — only when those files are staged. +if [ -n "$(staged_match '^backend/go\.(mod|sum)$')" ]; then + echo "→ backend: go mod tidy (diff check)" + ( cd backend && go mod tidy -diff ) || { + red "ERROR: go mod tidy would change go.mod/go.sum. Run \`cd backend && go mod tidy\` and stage the result." + exit 1 + } +fi + +# 6. Web checks — only when web/ files are staged. +if [ -n "$(staged_match '^web/')" ]; then + echo "→ web: prettier --check" + ( cd web && pnpm run format:check ) + + echo "→ web: eslint" + ( cd web && pnpm run lint ) + + echo "→ web: svelte-check" + ( cd web && pnpm run check -- --threshold error ) +fi diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 421e26f..0000000 --- a/.pre-commit-config.yaml +++ /dev/null @@ -1,66 +0,0 @@ -repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v5.0.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - exclude: ^helm/marktvogt/templates/ - - id: check-json - exclude: ^web/tsconfig\.json$ - - id: check-merge-conflict - - id: check-added-large-files - exclude: ^backend/internal/domain/discovery/crawler/testdata/ - - id: no-commit-to-branch - args: ['--branch', 'main'] - - - repo: local - hooks: - - id: golangci-lint - name: golangci-lint - entry: bash -c 'cd backend && golangci-lint run --config .golangci.yml ./...' - language: system - files: ^backend/.*\.go$ - pass_filenames: false - - - id: go-fmt - name: go fmt - entry: bash -c 'cd backend && gofmt -w .' - language: system - files: ^backend/.*\.go$ - pass_filenames: false - - - id: go-vet - name: go vet - entry: bash -c 'cd backend && go vet ./...' - language: system - files: ^backend/.*\.go$ - pass_filenames: false - - - id: go-mod-tidy - name: go mod tidy - entry: bash -c 'cd backend && go mod tidy' - language: system - files: ^backend/go\.(mod|sum)$ - pass_filenames: false - - - id: prettier - name: prettier - entry: bash -c 'cd web && pnpm run format:check' - language: system - files: ^web/ - pass_filenames: false - - - id: eslint - name: eslint - entry: bash -c 'cd web && pnpm run lint' - language: system - files: ^web/ - pass_filenames: false - - - id: svelte-check - name: svelte-check - entry: bash -c 'cd web && pnpm run check -- --threshold error' - language: system - files: ^web/ - pass_filenames: false diff --git a/package.json b/package.json new file mode 100644 index 0000000..f1273d2 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "marktvogt-monorepo", + "private": true, + "description": "Repo-root tooling — husky pre-commit hooks. Application code lives in backend/, web/, app/.", + "packageManager": "pnpm@10.33.0", + "scripts": { + "prepare": "husky" + }, + "devDependencies": { + "husky": "^9.1.7" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..a177305 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,24 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + devDependencies: + husky: + specifier: ^9.1.7 + version: 9.1.7 + +packages: + + husky@9.1.7: + resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} + engines: {node: '>=18'} + hasBin: true + +snapshots: + + husky@9.1.7: {}