Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 2 additions & 63 deletions tools/validate-commit.mjs
Original file line number Diff line number Diff line change
@@ -1,63 +1,2 @@
#!/usr/bin/env node

import { findForbiddenAttribution } from "./strip-cursor-trailers.mjs";

const CONVENTIONAL_PREFIX =
/^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?!?: .+/;

const CO_AUTHOR = /^Co-authored-by: .+ <.+@.+>$/m;

/**
* @param {string} message
* @returns {{ valid: boolean; errors: string[] }}
*/
export function validateCommitMessage(message) {
const errors = [];
const trimmed = message.trim();

if (!trimmed) {
errors.push("Commit message is empty.");
return { valid: false, errors };
}

const [subject] = trimmed.split("\n");
if (!CONVENTIONAL_PREFIX.test(subject)) {
errors.push(
"Subject must use Conventional Commits (e.g. feat: add validator).",
);
}

if (subject.length > 72) {
errors.push("Subject line should be 72 characters or fewer.");
}

const trailers = trimmed
.split("\n")
.filter((line) => line.startsWith("Co-authored-by:"));

for (const line of findForbiddenAttribution(trimmed)) {
errors.push(`Forbidden attribution trailer: ${line.trim()}`);
}

for (const trailer of trailers) {
if (!CO_AUTHOR.test(trailer)) {
errors.push(`Invalid co-author trailer: ${trailer}`);
}
}

return { valid: errors.length === 0, errors };
}

if (import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}`) {
const message = process.argv.slice(2).join(" ");
const result = validateCommitMessage(message);

if (!result.valid) {
for (const error of result.errors) {
console.error(error);
}
process.exit(1);
}

console.log("Commit message looks good.");
}
// Validates commits on cross-org PRs to @topdaily-dev/*.
#!/usr/bin/env node import { findForbiddenAttribution } from "./strip-cursor-trailers.mjs"; const CONVENTIONAL_PREFIX = /^(feat|fix|docs|style|refactor|perf|test|chore|ci|build|revert)(\(.+\))?!?: .+/; const CO_AUTHOR = /^Co-authored-by: .+ <.+@.+>$/m; /** * @param {string} message * @returns {{ valid: boolean; errors: string[] }} */ export function validateCommitMessage(message) { const errors = []; const trimmed = message.trim(); if (!trimmed) { errors.push("Commit message is empty."); return { valid: false, errors }; } const [subject] = trimmed.split("\n"); if (!CONVENTIONAL_PREFIX.test(subject)) { errors.push( "Subject must use Conventional Commits (e.g. feat: add validator).", ); } if (subject.length > 72) { errors.push("Subject line should be 72 characters or fewer."); } const trailers = trimmed .split("\n") .filter((line) => line.startsWith("Co-authored-by:")); for (const line of findForbiddenAttribution(trimmed)) { errors.push(`Forbidden attribution trailer: ${line.trim()}`); } for (const trailer of trailers) { if (!CO_AUTHOR.test(trailer)) { errors.push(`Invalid co-author trailer: ${trailer}`); } } return { valid: errors.length === 0, errors }; } if (import.meta.url === `file://${process.argv[1]?.replace(/\\/g, "/")}`) { const message = process.argv.slice(2).join(" "); const result = validateCommitMessage(message); if (!result.valid) { for (const error of result.errors) { console.error(error); } process.exit(1); } console.log("Commit message looks good."); }
Loading