diff --git a/.gitlab/dangerjs/mrDescriptionHasReleaseNotes.js b/.gitlab/dangerjs/mrDescriptionHasReleaseNotes.js index 67841b0c52..0f764a9b9e 100644 --- a/.gitlab/dangerjs/mrDescriptionHasReleaseNotes.js +++ b/.gitlab/dangerjs/mrDescriptionHasReleaseNotes.js @@ -7,22 +7,85 @@ */ module.exports = function () { 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) { - 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' - ); + 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).'); return null; } - let content = sectionReleaseNotes[1].replace(/(\r\n|\n|\r)/gm, "").trim(); // Remove empty lines and whitespace + const lines = sectionReleaseNotes[1].split("\n").filter(s => s.trim().length > 0); + let valid_entries_found = 0; + let no_release_notes_found = false; + let violations = []; - if (!content.length) { - warn( - "The `Release Notes` section seems to be empty (no section content)\n" - ); - return null; + 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 = ['']; // Add blank line on purpose, to avoid first line to be indented by dangerjs. + 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.`].join('\n\n'); + warn(error_output); + } + return null; }; + +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'); + } + + 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'); + } + + const area = 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; +}