2023-09-22 04:35:24 -04:00
const { recordRuleExitStatus } = require ( "./configParameters.js" ) ;
2023-02-21 02:56:09 -05:00
/ * *
* 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 ( ) {
2023-09-22 04:35:24 -04:00
const ruleName = 'Merge request Release Notes section' ;
2023-02-21 02:56:09 -05:00
const mrDescription = danger . gitlab . mr . description ;
2023-05-21 14:08:05 -04:00
const wiki _link = ` ${ process . env . DANGER _GITLAB _HOST } /espressif/esp-idf/-/wikis/rfc/How-to-write-release-notes-properly ` ;
2023-02-21 02:56:09 -05:00
const regexSectionReleaseNotes = /## Release notes([\s\S]*?)(?=## |$)/ ;
2023-08-02 14:17:57 -04:00
const regexValidEntry = /^\s*[-*+]\s+.+/ ;
2023-05-21 14:08:05 -04:00
const regexNoReleaseNotes = /no release note/i ;
2023-02-21 02:56:09 -05:00
2023-05-21 14:08:05 -04:00
const sectionReleaseNotes = mrDescription . match ( regexSectionReleaseNotes ) ;
2023-02-21 02:56:09 -05:00
if ( ! sectionReleaseNotes ) {
2023-09-22 04:35:24 -04:00
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 \n See [Release Notes Format Rules]( ${ wiki _link } ). ` ) ;
2023-02-21 02:56:09 -05:00
}
2023-08-02 14:17:57 -04:00
const releaseNotesLines = sectionReleaseNotes [ 1 ] . replace ( /<!--[\s\S]*?-->/g , '' )
const lines = releaseNotesLines . split ( "\n" ) . filter ( s => s . trim ( ) . length > 0 ) ;
2023-05-21 14:08:05 -04:00
let valid _entries _found = 0 ;
let no _release _notes _found = false ;
let violations = [ ] ;
2023-02-21 02:56:09 -05:00
2023-05-21 14:08:05 -04:00
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 ;
}
} ) ;
2023-08-02 14:17:57 -04:00
let error _output = [ ] ;
2023-05-21 14:08:05 -04:00
if ( violations . length > 0 ) {
error _output = [ ... error _output , 'Invalid release note entries:' , violations . join ( '\n' ) ] ;
2023-02-21 02:56:09 -05:00
}
2023-05-21 14:08:05 -04:00
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 ) {
2023-08-02 14:17:57 -04:00
// Paragraphs joined by double `\n`s.
error _output = [ ... error _output , ` See [Release Notes Format Guide]( ${ wiki _link } ). ` ] . join ( '\n\n' ) ;
2023-09-22 04:35:24 -04:00
recordRuleExitStatus ( ruleName , "Failed" ) ;
return warn ( error _output ) ;
2023-05-21 14:08:05 -04:00
}
2023-09-22 04:35:24 -04:00
// At this point, the rule has passed
recordRuleExitStatus ( ruleName , 'Passed' ) ;
2023-02-21 02:56:09 -05:00
} ;
2023-05-21 14:08:05 -04:00
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' ) ;
}
2023-09-26 08:28:28 -04:00
// 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' ) ;
2023-05-21 14:08:05 -04:00
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' ) ;
}
2023-08-02 14:17:57 -04:00
// area is in match[2]
2023-05-21 14:08:05 -04:00
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 ;
}