| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- const {
- minimumSummaryChars,
- maximumSummaryChars,
- maximumBodyLineChars,
- allowedTypes,
- } = require("./mrCommitsConstants.js");
- const { recordRuleExitStatus } = require("./configParameters.js");
- /**
- * Check that commit messages are based on the Espressif ESP-IDF project's rules for git commit messages.
- *
- * @dangerjs WARN
- */
- module.exports = async function () {
- const ruleName = "Commit messages style";
- const mrCommits = danger.gitlab.commits;
- const lint = require("@commitlint/lint").default;
- const lintingRules = {
- // rule definition: [(0-1 = off/on), (always/never = must be/mustn't be), (value)]
- "body-max-line-length": [1, "always", maximumBodyLineChars], // Max length of the body line
- "footer-leading-blank": [1, "always"], // Always have a blank line before the footer section
- "footer-max-line-length": [1, "always", maximumBodyLineChars], // Max length of the footer line
- "subject-max-length": [1, "always", maximumSummaryChars], // Max length of the "Summary"
- "subject-min-length": [1, "always", minimumSummaryChars], // Min length of the "Summary"
- "scope-case": [1, "always", "lower-case"], // "scope/component" must start with lower-case
- "subject-full-stop": [1, "never", "."], // "Summary" must not end with a full stop (period)
- "subject-empty": [1, "never"], // "Summary" is mandatory
- "type-case": [1, "always", "lower-case"], // "type/action" must start with lower-case
- "type-empty": [1, "never"], // "type/action" is mandatory
- "type-enum": [1, "always", allowedTypes], // "type/action" must be one of the allowed types
- "body-leading-blank": [1, "always"], // Always have a blank line before the body section
- };
- // Switcher for AI suggestions (for poor messages)
- let generateAISuggestion = false;
- // Search for the messages in each commit
- let issuesAllCommitMessages = [];
- for (const commit of mrCommits) {
- const commitMessage = commit.message;
- const commitMessageTitle = commit.title;
- let issuesSingleCommitMessage = [];
- let reportSingleCommitMessage = "";
- // Check if the commit message contains any Jira ticket references
- const jiraTicketRegex = /[A-Z0-9]+-[0-9]+/g;
- const jiraTicketMatches = commitMessage.match(jiraTicketRegex);
- if (jiraTicketMatches) {
- const jiraTicketNames = jiraTicketMatches.join(", ");
- issuesSingleCommitMessage.push(
- `- probably contains Jira ticket reference (\`${jiraTicketNames}\`). Please remove Jira tickets from commit messages.`
- );
- }
- // Lint commit messages with @commitlint (Conventional Commits style)
- const result = await lint(commit.message, lintingRules);
- for (const warning of result.warnings) {
- // Custom messages for each rule with terminology used by Espressif conventional commits guide
- switch (warning.name) {
- case "subject-max-length":
- issuesSingleCommitMessage.push(
- `- *summary* appears to be too long`
- );
- break;
- case "type-empty":
- issuesSingleCommitMessage.push(
- `- *type/action* looks empty`
- );
- break;
- case "type-case":
- issuesSingleCommitMessage.push(
- `- *type/action* should start with a lowercase letter`
- );
- break;
- case "scope-empty":
- issuesSingleCommitMessage.push(
- `- *scope/component* looks empty`
- );
- break;
- case "scope-case":
- issuesSingleCommitMessage.push(
- `- *scope/component* should be lowercase without whitespace, allowed special characters are \`_\` \`/\` \`.\` \`,\` \`*\` \`-\` \`.\``
- );
- break;
- case "subject-empty":
- issuesSingleCommitMessage.push(`- *summary* looks empty`);
- generateAISuggestion = true;
- break;
- case "subject-min-length":
- issuesSingleCommitMessage.push(
- `- *summary* looks too short`
- );
- generateAISuggestion = true;
- break;
- case "subject-case":
- issuesSingleCommitMessage.push(
- `- *summary* should start with a capital letter`
- );
- break;
- case "subject-full-stop":
- issuesSingleCommitMessage.push(
- `- *summary* should not end with a period (full stop)`
- );
- break;
- case "type-enum":
- issuesSingleCommitMessage.push(
- `- *type/action* should be one of [${allowedTypes
- .map((type) => `\`${type}\``)
- .join(", ")}]`
- );
- break;
- default:
- issuesSingleCommitMessage.push(`- ${warning.message}`);
- }
- }
- if (issuesSingleCommitMessage.length) {
- reportSingleCommitMessage = `- the commit message \`"${commitMessageTitle}"\`:\n${issuesSingleCommitMessage
- .map((message) => ` ${message}`) // Indent each issue by 2 spaces
- .join("\n")}`;
- issuesAllCommitMessages.push(reportSingleCommitMessage);
- }
- }
- // Create report
- if (issuesAllCommitMessages.length) {
- issuesAllCommitMessages.sort();
- const basicTips = [
- `- correct format of commit message should be: \`<type/action>(<scope/component>): <summary>\`, for example \`fix(esp32): Fixed startup timeout issue\``,
- `- allowed types are: \`${allowedTypes}\``,
- `- sufficiently descriptive message summary should be between ${minimumSummaryChars} to ${maximumSummaryChars} characters and start with upper case letter`,
- `- avoid Jira references in commit messages (unavailable/irrelevant for our customers)`,
- `- follow this [commit messages guide](${process.env.DANGER_GITLAB_HOST}/espressif/esp-idf/-/wikis/dev-proc/Commit-messages)`,
- ];
- let dangerMessage = `\n**Some issues found for the commit messages in this MR:**\n${issuesAllCommitMessages.join(
- "\n"
- )}
- \n***
- \n**Please consider updating these commit messages** - here are some basic tips:\n${basicTips.join(
- "\n"
- )}
- \n \`TIP:\` You can install commit-msg pre-commit hook (\`pre-commit install -t pre-commit -t commit-msg\`) to run this check when committing.
- \n***
- `;
- if (generateAISuggestion) {
- // Create AI generated suggestion for git commit message based of gitDiff and current commit messages
- const AImessageSuggestion =
- await require("./aiGenerateGitMessage.js")();
- dangerMessage += AImessageSuggestion;
- }
- recordRuleExitStatus(ruleName, "Failed");
- return warn(dangerMessage);
- }
- // At this point, the rule has passed
- recordRuleExitStatus(ruleName, "Passed");
- };
|