@@ -63,6 +63,18 @@ const setPullRequest = (commit, owner, repo, number) => {
6363 }
6464}
6565
66+ // copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13
67+ const OMIT_FROM_RELEASE_NOTES_KEYS = [
68+ 'no-notes' ,
69+ 'no notes' ,
70+ 'no_notes' ,
71+ 'none' ,
72+ 'no' ,
73+ 'nothing' ,
74+ 'empty' ,
75+ 'blank'
76+ ]
77+
6678const getNoteFromBody = body => {
6779 if ( ! body ) {
6880 return null
@@ -76,21 +88,15 @@ const getNoteFromBody = body => {
7688 . find ( paragraph => paragraph . startsWith ( NOTE_PREFIX ) )
7789
7890 if ( note ) {
79- const placeholder = '<!-- One-line Change Summary Here-->'
8091 note = note
8192 . slice ( NOTE_PREFIX . length )
82- . replace ( placeholder , '' )
93+ . replace ( / < ! - - . * - - > / , '' ) // '<!-- change summary here-->'
8394 . replace ( / \r ? \n / , ' ' ) // remove newlines
8495 . trim ( )
8596 }
8697
87- if ( note ) {
88- if ( note . match ( / ^ [ N n ] o [ _ - ] [ N n ] o t e s \. ? $ / ) ) {
89- return NO_NOTES
90- }
91- if ( note . match ( / ^ [ N n ] o n e \. ? $ / ) ) {
92- return NO_NOTES
93- }
98+ if ( note && OMIT_FROM_RELEASE_NOTES_KEYS . includes ( note . toLowerCase ( ) ) ) {
99+ return NO_NOTES
94100 }
95101
96102 return note
@@ -137,8 +143,11 @@ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => {
137143
138144 // if the subject begins with 'word:', treat it as a semantic commit
139145 if ( ( match = subject . match ( / ^ ( \w + ) : \s ( .* ) $ / ) ) ) {
140- commit . type = match [ 1 ] . toLocaleLowerCase ( )
141- subject = match [ 2 ]
146+ const type = match [ 1 ] . toLocaleLowerCase ( )
147+ if ( knownTypes . has ( type ) ) {
148+ commit . type = type
149+ subject = match [ 2 ]
150+ }
142151 }
143152
144153 // Check for GitHub commit message that indicates a PR
@@ -221,7 +230,6 @@ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => {
221230 }
222231
223232 commit . subject = subject . trim ( )
224-
225233 return commit
226234}
227235
@@ -389,7 +397,7 @@ const getDependencyCommits = async (pool, from, to) => {
389397**** Main
390398***/
391399
392- const getNotes = async ( fromRef , toRef ) => {
400+ const getNotes = async ( fromRef , toRef , newVersion ) => {
393401 if ( ! fs . existsSync ( CACHE_DIR ) ) {
394402 fs . mkdirSync ( CACHE_DIR )
395403 }
@@ -437,45 +445,89 @@ const getNotes = async (fromRef, toRef) => {
437445 // scrape PRs for release note 'Notes:' comments
438446 for ( const commit of pool . commits ) {
439447 let pr = commit . pr
448+ let prSubject
440449 while ( pr && ! commit . note ) {
441450 const prData = await getPullRequest ( pr . number , pr . owner , pr . repo )
442451 if ( ! prData || ! prData . data ) {
443452 break
444453 }
445454
446455 // try to pull a release note from the pull comment
447- commit . note = getNoteFromBody ( prData . data . body )
448- if ( commit . note ) {
449- break
450- }
456+ const prParsed = { }
457+ parseCommitMessage ( `${ prData . data . title } \n\n${ prData . data . body } ` , pr . owner , pr . repo , prParsed )
458+ commit . note = commit . note || prParsed . note
459+ commit . type = commit . type || prParsed . type
460+ prSubject = prSubject || prParsed . subject
451461
452- // if the PR references another PR, maybe follow it
453- parseCommitMessage ( `${ prData . data . title } \n\n${ prData . data . body } ` , pr . owner , pr . repo , commit )
454- pr = pr . number !== commit . pr . number ? commit . pr : null
462+ pr = prParsed . pr && ( prParsed . pr . number !== pr . number ) ? prParsed . pr : null
455463 }
464+
465+ // if we still don't have a note, it's because someone missed a 'Notes:
466+ // comment in a PR somewhere... use the PR subject as a fallback.
467+ commit . note = commit . note || prSubject
456468 }
457469
458- // remove uninteresting commits
470+ // remove non-user-facing commits
459471 pool . commits = pool . commits
460472 . filter ( commit => commit . note !== NO_NOTES )
461473 . filter ( commit => ! ( ( commit . note || commit . subject ) . match ( / ^ [ B b ] u m p v \d + \. \d + \. \d + / ) ) )
462474
475+ // if this is a stable release,
476+ // remove notes for changes that already landed in a previous major/minor series
477+ if ( semver . valid ( newVersion ) && ! semver . prerelease ( newVersion ) ) {
478+ // load all the prDatas
479+ await Promise . all (
480+ pool . commits . map ( commit => new Promise ( async ( resolve ) => {
481+ const { pr } = commit
482+ if ( typeof pr === 'object' ) {
483+ const prData = await getPullRequest ( pr . number , pr . owner , pr . repo )
484+ if ( prData ) {
485+ commit . prData = prData
486+ }
487+ }
488+ resolve ( )
489+ } ) )
490+ )
491+
492+ // remove items that already landed in a previous major/minor series
493+ pool . commits = pool . commits
494+ . filter ( commit => {
495+ if ( ! commit . prData ) {
496+ return true
497+ }
498+ const reducer = ( accumulator , current ) => {
499+ if ( ! semver . valid ( accumulator ) ) { return current }
500+ if ( ! semver . valid ( current ) ) { return accumulator }
501+ return semver . lt ( accumulator , current ) ? accumulator : current
502+ }
503+ const earliestRelease = commit . prData . data . labels
504+ . map ( label => label . name . match ( / m e r g e d \/ ( \d + ) - ( \d + ) - x / ) )
505+ . filter ( label => ! ! label )
506+ . map ( label => `${ label [ 1 ] } .${ label [ 2 ] } .0` )
507+ . reduce ( reducer , null )
508+ if ( ! semver . valid ( earliestRelease ) ) {
509+ return true
510+ }
511+ return semver . diff ( earliestRelease , newVersion ) . includes ( 'patch' )
512+ } )
513+ }
514+
463515 const notes = {
464- breaks : [ ] ,
516+ breaking : [ ] ,
465517 docs : [ ] ,
466518 feat : [ ] ,
467519 fix : [ ] ,
468520 other : [ ] ,
469521 unknown : [ ] ,
470- ref : toRef
522+ name : newVersion
471523 }
472524
473525 pool . commits . forEach ( commit => {
474526 const str = commit . type
475527 if ( ! str ) {
476528 notes . unknown . push ( commit )
477529 } else if ( breakTypes . has ( str ) ) {
478- notes . breaks . push ( commit )
530+ notes . breaking . push ( commit )
479531 } else if ( docTypes . has ( str ) ) {
480532 notes . docs . push ( commit )
481533 } else if ( featTypes . has ( str ) ) {
@@ -562,7 +614,7 @@ const renderCommit = (commit, explicitLinks) => {
562614}
563615
564616const renderNotes = ( notes , explicitLinks ) => {
565- const rendered = [ `# Release Notes for ${ notes . ref } \n\n` ]
617+ const rendered = [ `# Release Notes for ${ notes . name } \n\n` ]
566618
567619 const renderSection = ( title , commits ) => {
568620 if ( commits . length === 0 ) {
@@ -582,7 +634,7 @@ const renderNotes = (notes, explicitLinks) => {
582634 rendered . push ( ...lines . sort ( ) , '\n' )
583635 }
584636
585- renderSection ( 'Breaking Changes' , notes . breaks )
637+ renderSection ( 'Breaking Changes' , notes . breaking )
586638 renderSection ( 'Features' , notes . feat )
587639 renderSection ( 'Fixes' , notes . fix )
588640 renderSection ( 'Other Changes' , notes . other )
0 commit comments