Skip to content

Instantly share code, notes, and snippets.

@eins78
Created March 2, 2026 18:56
Show Gist options
  • Select an option

  • Save eins78/d3a740b5fa2c93723780976186dbc9ee to your computer and use it in GitHub Desktop.

Select an option

Save eins78/d3a740b5fa2c93723780976186dbc9ee to your computer and use it in GitHub Desktop.
Pre-commit hook improvements: speed + CI parity

Pre-Commit Hook Improvements: Speed + CI Parity

2026-03-02T18:53:55Z by Showboat 0.6.1

What Changed

The pre-commit hook was slow (~45s) and incomplete (prettier gaps vs CI). This commit:

  1. Targeted typecheck — only checks packages affected by staged TS files (uses cross-package dependency graph)
  2. Fixed lint-staged coverage — added .scss/.css/.yaml/.md to prettier (previously missed, causing CI failures)
  3. Scoped ESLint — JS/TS only (was incorrectly running on .json/.html/.java)
  4. Targeted config validation — validates only changed service-definition configs (not all manifests)
  5. Removed dead config — deleted legacy husky v4 block from package.json
  6. Fixed shell safety — subshell for cd in pre-commit, bash 3.2 compatible (no declare -A)

Files Changed

File Action
package.json Removed lint-staged + husky sections
.lintstagedrc.js New lint-staged config with correct file scoping
.husky/pre-commit Simplified, uses targeted typecheck
scripts/typecheck-changed.sh New: maps staged files to affected packages
scripts/validate-changed-configs.sh Rewritten: validates only changed configs
packages/server/scripts/validate-specific-configs.ts New: single-file validation using existing validateConfigFile()

Test 1: SCSS-only change (expect: prettier only, no typecheck)

echo "/* test */" >> packages/client/src/components/service-order-form/service-order-form.scss && git add packages/client/src/components/service-order-form/service-order-form.scss && time git commit -m "test: scss only" 2>&1; git reset --hard 5b50896a >/dev/null 2>&1
Running pre-commit checks...
No service definition configs changed
[STARTED] Backing up original state...
[COMPLETED] Backed up original state in git stash (1ddb6a45)
[STARTED] Running tasks for staged files...
[STARTED] .lintstagedrc.js — 1 file
[STARTED] *.{js,ts,tsx,jsx} — 0 files
[STARTED] *.{json,html,md,css,scss,yaml,yml} — 1 file
[SKIPPED] *.{js,ts,tsx,jsx} — no files
[STARTED] prettier --write
[COMPLETED] prettier --write
[COMPLETED] *.{json,html,md,css,scss,yaml,yml} — 1 file
[COMPLETED] .lintstagedrc.js — 1 file
[COMPLETED] Running tasks for staged files...
[STARTED] Applying modifications from tasks...
[COMPLETED] Applying modifications from tasks...
[STARTED] Cleaning up temporary files...
[COMPLETED] Cleaning up temporary files...
No TypeScript files staged, skipping typecheck
Pre-commit checks passed!
[worktree-dev e3d4ae72] test: scss only
 1 file changed, 1 insertion(+)

real	0m1.280s
user	0m0.614s
sys	0m0.412s

Result: 1.3s — prettier only, typecheck skipped. (Was ~45s before.)

Test 2: Single client TS file (expect: client + server typecheck via cross-dep)

echo "// test" >> packages/client/src/api/index.ts && git add packages/client/src/api/index.ts && time git commit -m "test: client ts" 2>&1; git reset --hard 5b50896a >/dev/null 2>&1
Running pre-commit checks...
No service definition configs changed
[STARTED] Backing up original state...
[COMPLETED] Backed up original state in git stash (4b67e009)
[STARTED] Running tasks for staged files...
[STARTED] .lintstagedrc.js — 1 file
[STARTED] *.{js,ts,tsx,jsx} — 1 file
[STARTED] *.{json,html,md,css,scss,yaml,yml} — 0 files
[SKIPPED] *.{json,html,md,css,scss,yaml,yml} — no files
[STARTED] prettier --write
[COMPLETED] prettier --write
[STARTED] eslint --fix
[COMPLETED] eslint --fix
[COMPLETED] *.{js,ts,tsx,jsx} — 1 file
[COMPLETED] .lintstagedrc.js — 1 file
[COMPLETED] Running tasks for staged files...
[STARTED] Applying modifications from tasks...
[COMPLETED] Applying modifications from tasks...
[STARTED] Cleaning up temporary files...
[COMPLETED] Cleaning up temporary files...
Type-checking affected packages: client server
  Checking packages/client...

> @qs/cds-cpq-client@0.1.0 typecheck /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/client
> tsc -p tsconfig.spec.json --noEmit

  Checking packages/server...

> @qs/cds-cpq-server@0.1.0 typecheck /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/server
> tsc -p tsconfig.spec.json --noEmit

Type check passed
Pre-commit checks passed!
[worktree-dev aa86bc72] test: client ts
 1 file changed, 1 insertion(+)

real	0m18.014s
user	0m29.120s
sys	0m3.928s

Result: 18s — correctly detected cross-package dependency (client/src/api/ -> server imports @qs/cds-cpq-client). Only 2 of 4 packages checked. (Was ~45s before.)

Test 3: JSON service-definition config (expect: targeted validation + prettier, no typecheck)

python3 -c "
import json
with open('packages/server/data/configurations/service-definitions/pv-anlagenreinigung/config.json', 'r') as f:
    d = json.load(f)
d['description'] = d.get('description', '') + ' test'
with open('packages/server/data/configurations/service-definitions/pv-anlagenreinigung/config.json', 'w') as f:
    json.dump(d, f, indent=4, ensure_ascii=False)
    f.write('\n')
" && git add packages/server/data/configurations/service-definitions/pv-anlagenreinigung/config.json && time git commit -m 'test: json config' 2>&1; git reset --hard 5b50896a >/dev/null 2>&1
Running pre-commit checks...
Validating changed service definition configs...
  - packages/server/data/configurations/service-definitions/pv-anlagenreinigung/config.json
  OK /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/server/data/configurations/service-definitions/pv-anlagenreinigung/config.json
[STARTED] Backing up original state...
[COMPLETED] Backed up original state in git stash (9d2eab96)
[STARTED] Running tasks for staged files...
[STARTED] .lintstagedrc.js — 1 file
[STARTED] *.{js,ts,tsx,jsx} — 0 files
[STARTED] *.{json,html,md,css,scss,yaml,yml} — 1 file
[SKIPPED] *.{js,ts,tsx,jsx} — no files
[STARTED] prettier --write
[COMPLETED] prettier --write
[COMPLETED] *.{json,html,md,css,scss,yaml,yml} — 1 file
[COMPLETED] .lintstagedrc.js — 1 file
[COMPLETED] Running tasks for staged files...
[STARTED] Applying modifications from tasks...
[COMPLETED] Applying modifications from tasks...
[STARTED] Cleaning up temporary files...
[COMPLETED] Cleaning up temporary files...
No TypeScript files staged, skipping typecheck
Pre-commit checks passed!
[worktree-dev 78e64062] test: json config
 1 file changed, 1 insertion(+), 1 deletion(-)

real	0m1.647s
user	0m0.933s
sys	0m0.506s

Result: 1.6s — validated only the 1 changed config file (not all ~50+ manifests). Prettier on JSON, no typecheck. (Was ~15s before.)

Test 4: Shared TS file (expect: all 4 packages typecheck — transitive dependents)

echo "// test" >> packages/shared/src/index.ts && git add packages/shared/src/index.ts && time git commit -m "test: shared ts" 2>&1; git reset --hard 5b50896a >/dev/null 2>&1
Running pre-commit checks...
No service definition configs changed
[STARTED] Backing up original state...
[COMPLETED] Backed up original state in git stash (bca1d514)
[STARTED] Running tasks for staged files...
[STARTED] .lintstagedrc.js — 1 file
[STARTED] *.{js,ts,tsx,jsx} — 1 file
[STARTED] *.{json,html,md,css,scss,yaml,yml} — 0 files
[SKIPPED] *.{json,html,md,css,scss,yaml,yml} — no files
[STARTED] prettier --write
[COMPLETED] prettier --write
[STARTED] eslint --fix
[COMPLETED] eslint --fix
[COMPLETED] *.{js,ts,tsx,jsx} — 1 file
[COMPLETED] .lintstagedrc.js — 1 file
[COMPLETED] Running tasks for staged files...
[STARTED] Applying modifications from tasks...
[COMPLETED] Applying modifications from tasks...
[STARTED] Cleaning up temporary files...
[COMPLETED] Cleaning up temporary files...
Type-checking affected packages: shared client server demo-app
  Checking packages/shared...

> @qs/cds-cpq-shared@0.1.0 typecheck /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/shared
> tsc -p tsconfig.spec.json --noEmit

  Checking packages/client...

> @qs/cds-cpq-client@0.1.0 typecheck /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/client
> tsc -p tsconfig.spec.json --noEmit

  Checking packages/server...

> @qs/cds-cpq-server@0.1.0 typecheck /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/server
> tsc -p tsconfig.spec.json --noEmit

  Checking packages/demo-app...

> @qs/cds-cpq-demo-app@0.1.0 typecheck /Users/ma/CODE/CDS/cpq-cds/.claude/worktrees/dev/packages/demo-app
> tsc -p tsconfig.json --noEmit

Type check passed
Pre-commit checks passed!
[worktree-dev 58bb0909] test: shared ts
 1 file changed, 1 insertion(+)

real	0m23.322s
user	0m39.684s
sys	0m4.704s

Result: 23s — correctly detected shared as a transitive dependency: all 4 packages checked (shared, client, server, demo-app). This is the worst-case scenario. (Was ~45s before — still a ~50% improvement because the targeted script runs packages sequentially without pnpm's recursive overhead.)

Summary

Scenario Before After Speedup
SCSS-only ~45s 1.3s ~35x
JSON config ~15s 1.6s ~9x
Client TS (cross-dep) ~45s 18s ~2.5x
Shared TS (worst case) ~45s 23s ~2x

Additionally, .scss, .css, .yaml, .md files are now prettier-checked in pre-commit, closing the CI parity gap that caused formatting failures in the continuous-build-multi Jenkins pipeline.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment