const { recordRuleExitStatus } = require("./configParameters.js"); /** * Check if MR Description contains mandatory section "Release notes" * * Extracts the content of the "Release notes" section from the GitLab merge request description. * * @dangerjs WARN (if section missing, is empty or wrong markdown format) */ module.exports = function () { const ruleName = 'Merge request Release Notes section'; const mrDescription = danger.gitlab.mr.description; const wiki_link = `${process.env.DANGER_GITLAB_HOST}/espressif/esp-idf/-/wikis/rfc/How-to-write-release-notes-properly`; const regexSectionReleaseNotes = /## Release notes([\s\S]*?)(?=## |$)/; const regexValidEntry = /^\s*[-*+]\s+.+/; const regexNoReleaseNotes = /no release note/i; const sectionReleaseNotes = mrDescription.match(regexSectionReleaseNotes); if (!sectionReleaseNotes) { recordRuleExitStatus(ruleName, "Failed"); return warn(`The \`Release Notes\` section seems to be missing. Please check if the section header in MR description is present and in the correct markdown format ("## Release Notes").\n\nSee [Release Notes Format Rules](${wiki_link}).`); } const releaseNotesLines = sectionReleaseNotes[1].replace(//g, '') const lines = releaseNotesLines.split("\n").filter(s => s.trim().length > 0); let valid_entries_found = 0; let no_release_notes_found = false; let violations = []; lines.forEach((line) => { if (line.match(regexValidEntry)) { valid_entries_found++; const error_msg = check_entry(line); if (error_msg) { violations.push(error_msg); } } else if (line.match(regexNoReleaseNotes)) { no_release_notes_found = true; } }); let error_output = []; if (violations.length > 0) { error_output = [...error_output, 'Invalid release note entries:', violations.join('\n')]; } if (no_release_notes_found) { if (valid_entries_found > 0) { error_output.push('`No release notes` comment shows up when there is valid entry. Remove bullets before comments in release notes section.'); } } else { if (!valid_entries_found) { error_output.push('The `Release Notes` section seems to have no valid entries. Add bullets before valid entries, or add `No release notes` comment to suppress this error if you mean to have no release notes.'); } } if (error_output.length > 0) { // Paragraphs joined by double `\n`s. error_output = [...error_output, `See [Release Notes Format Guide](${wiki_link}).`].join('\n\n'); recordRuleExitStatus(ruleName, "Failed"); return warn(error_output); } // At this point, the rule has passed recordRuleExitStatus(ruleName, 'Passed'); }; function check_entry(entry) { const entry_str = `- \`${entry}\``; const indent = " "; if (entry.match(/no\s+release\s+note/i)) { return [entry_str, `${indent}- \`No release notes\` comment shouldn't start with bullet.`].join('\n'); } // Remove a leading escaping backslash of the special characters, https://www.markdownguide.org/basic-syntax/#characters-you-can-escape const escapeCharRegex = /\\([\\`*_{}[\]<>()+#-.!|])/g; entry = entry.replace(escapeCharRegex, '$1'); const regex = /^(\s*)[-*+]\s+\[([^\]]+)\]\s+(.*)$/; const match = regex.exec(entry); if (!match) { return [entry_str, `${indent}- Please specify the [area] to which the change belongs (see guide). If this line is just a comment, remove the bullet.`].join('\n'); } // area is in match[2] const description = match[3].trim(); let violations = []; if (match[1]) { violations.push(`${indent}- Release note entry should start from the beginning of line. (Nested release note not allowed.)`); } if (!/^[A-Z0-9]/.test(description)) { violations.push(`${indent}- Release note statement should start with a capital letter or digit.`); } if (violations.length > 0) { return [entry_str, ...violations].join('\n'); } return null; }