feat: add conventional commit message validation

This commit is contained in:
plenarius
2026-04-20 21:55:23 -04:00
parent 7f848aad5d
commit 3784d9f13a
2 changed files with 25 additions and 0 deletions
+7
View File
@@ -9,6 +9,7 @@ import {
commitFiles,
push,
getDiff,
validateCommitMessage,
} from "../services/git.service.js";
import type { RepoName } from "../config/repos.js";
@@ -88,6 +89,12 @@ router.post("/:repo/commit", async (req, res) => {
if (body) fullMessage += `\n\n${body}`;
if (issueRef) fullMessage += `\n\nFixes #${issueRef}`;
const validation = validateCommitMessage(fullMessage);
if (!validation.valid) {
res.status(400).json({ error: validation.error });
return;
}
const repoPath = getRepoPath(repoName);
const result = await commitFiles(repoPath, fullMessage, files);
res.json(result);
@@ -52,7 +52,25 @@ export async function pull(repoPath: string) {
return g.pull();
}
const COMMIT_PATTERN = /^(feat|fix|refactor|docs|chore|test|style|perf|ci|build|revert)(\(.+\))?: .{1,100}$/;
export function validateCommitMessage(message: string): { valid: boolean; error?: string } {
const firstLine = message.split("\n")[0];
if (!COMMIT_PATTERN.test(firstLine)) {
return {
valid: false,
error: `Invalid format. Expected: type(scope): description. Valid types: feat, fix, refactor, docs, chore, test, style, perf, ci, build, revert`,
};
}
return { valid: true };
}
export async function commitFiles(repoPath: string, message: string, files?: string[]) {
const validation = validateCommitMessage(message);
if (!validation.valid) {
throw new Error(validation.error);
}
const g = git(repoPath);
if (files && files.length > 0) {
await g.add(files);