From 52400c599ccbcc3faea738f710a43ad156139037 Mon Sep 17 00:00:00 2001 From: Andrey Avtomonov <7889985+andreybavt@users.noreply.github.com> Date: Tue, 12 May 2026 13:02:06 +0200 Subject: [PATCH] chore: standardize pre-commit checks --- .pre-commit-config.yaml | 70 ++++++++++++++++++++++++++++++++ scripts/precommit-check.mjs | 16 +++----- scripts/precommit-check.test.mjs | 15 +++++-- 3 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..555d7fc0 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,70 @@ +# See https://pre-commit.com for hook documentation. +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-toml + - id: check-added-large-files + args: ["--maxkb=1000"] + - id: check-merge-conflict + - id: check-case-conflict + - id: mixed-line-ending + + - repo: https://github.com/asottile/pyupgrade + rev: v3.21.2 + hooks: + - id: pyupgrade + name: pyupgrade (python) + files: ^python/ + args: [--py313-plus] + + - repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.2 + hooks: + - id: ruff + name: ruff (python) + files: ^python/ + args: [--fix, --exit-non-zero-on-fix] + - id: ruff-format + name: ruff format (python) + files: ^python/ + + - repo: local + hooks: + - id: ktx-package-checks + name: ktx package checks + entry: node scripts/precommit-check.mjs + language: system + files: ^(packages/|scripts/|python/|package\.json$|pnpm-lock\.yaml$|pnpm-workspace\.yaml$|release-policy\.json$|tsconfig\.base\.json$|pyproject\.toml$|uv\.lock$|uv\.toml$) + + - repo: https://github.com/Yelp/detect-secrets + rev: v1.5.0 + hooks: + - id: detect-secrets + exclude: | + (?x)^( + .*\.lock$| + .*pnpm-lock\.yaml$| + .*package-lock\.json$| + .*yarn\.lock$| + .*\.log$| + .*\.dump$| + .*\.sql$| + .*\.csv$| + .*\.db$| + .*\.sqlite$| + .*\.sqlite3$| + .*/node_modules/.*| + .*/\.venv/.*| + .*/dist/.*| + .*/build/.*| + .*/coverage/.*| + .*/htmlcov/.*| + .*\.gen\.ts$| + .*\.gen\.py$| + .*\.generated\.ts$ + )$ diff --git a/scripts/precommit-check.mjs b/scripts/precommit-check.mjs index fdd405bf..299db534 100644 --- a/scripts/precommit-check.mjs +++ b/scripts/precommit-check.mjs @@ -1,12 +1,11 @@ #!/usr/bin/env node import { spawnSync } from 'node:child_process'; import { existsSync, readFileSync } from 'node:fs'; -import { dirname, join, relative, sep } from 'node:path'; +import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; const scriptPath = fileURLToPath(import.meta.url); const ktxRoot = dirname(dirname(scriptPath)); -const repoRoot = dirname(ktxRoot); const packageNameByDir = new Map( [ @@ -35,7 +34,8 @@ const pythonPackageTests = new Map([ ]); function normalizeFilePath(filePath) { - return filePath.replaceAll('\\', '/').replace(/^\.\//, ''); + const normalized = filePath.replaceAll('\\', '/').replace(/^\.\//, ''); + return normalized.startsWith('ktx/') ? normalized.slice('ktx/'.length) : normalized; } function stablePush(commands, key, cmd, args) { @@ -68,13 +68,7 @@ export function planChecks(files) { let runAllPythonTests = false; for (const rawFile of files) { - const file = normalizeFilePath(rawFile); - - if (!file.startsWith('ktx/')) { - continue; - } - - const ktxFile = file.slice('ktx/'.length); + const ktxFile = normalizeFilePath(rawFile); if (ktxFile.startsWith('packages/')) { const [, packageDir, ...rest] = ktxFile.split('/'); @@ -189,6 +183,6 @@ export function runChecks(files) { return 0; } -if (process.argv[1] && relative(repoRoot, process.argv[1]).split(sep).join('/') === 'ktx/scripts/precommit-check.mjs') { +if (process.argv[1] && resolve(process.argv[1]) === scriptPath) { process.exitCode = runChecks(process.argv.slice(2)); } diff --git a/scripts/precommit-check.test.mjs b/scripts/precommit-check.test.mjs index 55ef66bb..40bd1716 100644 --- a/scripts/precommit-check.test.mjs +++ b/scripts/precommit-check.test.mjs @@ -12,7 +12,16 @@ describe('precommit-check', () => { assert.deepEqual(commandKeys(['outside-workspace/src/app.ts']), []); }); - it('runs only the touched package checks for package code', () => { + it('runs only the touched package checks for standalone package paths', () => { + assert.deepEqual(commandKeys(['packages/cli/src/index.ts']), [ + 'boundary-check', + 'type-check:@ktx/cli', + 'build:@ktx/cli', + 'test:@ktx/cli', + ]); + }); + + it('accepts legacy subtree-prefixed package paths', () => { assert.deepEqual(commandKeys(['ktx/packages/cli/src/index.ts']), [ 'boundary-check', 'type-check:@ktx/cli', @@ -22,12 +31,12 @@ describe('precommit-check', () => { }); it('runs the matching script test when a script changes', () => { - assert.deepEqual(commandKeys(['ktx/scripts/check-boundaries.mjs']), [ + assert.deepEqual(commandKeys(['scripts/check-boundaries.mjs']), [ 'script-test:scripts/check-boundaries.test.mjs', ]); }); it('runs the touched python package tests', () => { - assert.deepEqual(commandKeys(['ktx/python/ktx-sl/semantic_layer/parser.py']), ['pytest:ktx-sl']); + assert.deepEqual(commandKeys(['python/ktx-sl/semantic_layer/parser.py']), ['pytest:ktx-sl']); }); });