#!/usr/bin/env node import fs from "node:fs" const messagePath = process.argv[2] if (!messagePath) { console.error("validate-commit-message: не указан файл сообщения коммита") process.exit(2) } if (process.env.SKIP_COMMIT_MESSAGE_RU === "1") { process.exit(0) } const content = fs.readFileSync(messagePath, "utf8") const subject = content .split(/\r?\n/) .map((line) => line.trim()) .find((line) => line.length > 0 && !line.startsWith("#")) if (!subject) { process.exit(0) } const skipPatterns = [ /^Merge\b/i, /^Revert\b/i, /^fixup!/i, /^squash!/i, /^amend!/i, ] if (skipPatterns.some((pattern) => pattern.test(subject))) { process.exit(0) } const cyrillic = /[\u0400-\u04FF\u0500-\u052F]/ if (!cyrillic.test(subject)) { console.error("") console.error("Коммит отклонён: subject должен быть на русском.") console.error("Формат: fix(scope): краткое описание на русском") console.error("Правила: .cursor/rules/commit-messages-ru.mdc") console.error("Обход только для аварий: SKIP_COMMIT_MESSAGE_RU=1") console.error("") console.error(`Subject: ${subject}`) process.exit(1) } const conventional = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([^)]+\))?!?:\s*(.+)$/ const match = subject.match(conventional) if (match && !cyrillic.test(match[3])) { console.error("") console.error("Коммит отклонён: текст после «:» должен быть на русском.") console.error(`Subject: ${subject}`) process.exit(1) } process.exit(0)