@@ -8,114 +8,125 @@ import parseChangelog, { type Release } from './changelog-parser'
88import semver from 'semver'
99import Octokit from '@octokit/rest'
1010import getNpmToken from './getNpmToken'
11+ import memoize from './util/memoize'
1112
1213const { GH_TOKEN } = process . env
1314
1415const octokitOptions = { }
1516if ( GH_TOKEN ) octokitOptions . auth = `token ${ GH_TOKEN } `
1617const octokit = new Octokit ( octokitOptions )
1718
18- export async function getChangelog (
19- owner : string ,
20- repo : string
21- ) : Promise < Array < Release >> {
22- let changelog
23- for ( const file of [ 'CHANGELOG.md' , 'changelog.md' ] ) {
24- try {
25- const {
26- data : { content } ,
27- } = await octokit . repos . getContents ( {
28- owner,
29- repo,
30- path : file ,
31- } )
32- changelog = Base64 . decode ( content )
33- break
34- } catch ( error ) {
35- continue
19+ export const getChangelog = memoize (
20+ async ( owner : string , repo : string ) : Promise < { [ string ] : Release } > => {
21+ let changelog
22+ for ( const file of [ 'CHANGELOG.md' , 'changelog.md' ] ) {
23+ try {
24+ const {
25+ data : { content } ,
26+ } = await octokit . repos . getContents ( {
27+ owner,
28+ repo,
29+ path : file ,
30+ } )
31+ changelog = Base64 . decode ( content )
32+ break
33+ } catch ( error ) {
34+ continue
35+ }
3636 }
37- }
38- if ( ! changelog ) throw new Error ( 'failed to get changelog' )
39- return await parseChangelog ( changelog )
37+ if ( ! changelog ) throw new Error ( 'failed to get changelog' )
38+ return await parseChangelog ( changelog )
39+ } ,
40+ ( owner , repo ) => `${ owner } /${ repo } `
41+ )
42+
43+ function parseRepositoryUrl ( url : string ) : { owner: string , repo : string } {
44+ const match = / g i t h u b \. c o m [: / ] ( [ ^ \\ ] + ) \/ ( [ ^ . \\ ] + ) / i. exec ( url )
45+ if ( ! match ) throw new Error ( `repository.url not supported: ${ url } ` )
46+ const [ owner , repo ] = match . slice ( 1 )
47+ return { owner, repo }
4048}
4149
4250export async function whatBroke (
4351 pkg : string ,
4452 {
4553 fromVersion,
4654 toVersion,
55+ full,
4756 } : {
4857 fromVersion ?: ?string ,
4958 toVersion ?: ?string ,
59+ full ?: ?boolean ,
5060 } = { }
5161) : Promise < Object > {
5262 const npmInfo = await npmRegistryFetch . json ( pkg , {
5363 token : await getNpmToken ( ) ,
5464 } )
55- const { repository : { url } = { } } = npmInfo
56- if ( ! url ) throw new Error ( 'failed to get repository.url' )
57- const match = / g i t h u b \. c o m \/ ( [ ^ \\ ] + ) \/ ( [ ^ . \\ ] + ) / i. exec ( url )
58- if ( ! match ) throw new Error ( `repository.url not supported: ${ url } ` )
59- const [ owner , repo ] = match . slice ( 1 )
60- let changelog
61- await getChangelog ( owner , repo )
62- . then ( c => ( changelog = c ) )
63- . catch ( ( ) => { } )
6465
65- const result = [ ]
66+ const versions = Object . keys ( npmInfo . versions ) . filter (
67+ ( v : string ) : boolean => {
68+ if ( fromVersion && ! semver . gt ( v , fromVersion ) ) return false
69+ if ( toVersion && ! semver . lt ( v , toVersion ) ) return false
70+ return true
71+ }
72+ )
73+
74+ const releases = [ ]
6675
67- if ( changelog ) {
68- let prevVersion = fromVersion
69- for ( const release of changelog . reverse ( ) ) {
70- const { version } = release
71- if ( ! version ) continue
72- if ( prevVersion && semver . lte ( version , prevVersion ) ) continue
73- if ( toVersion && semver . gt ( version , toVersion ) ) break
74- if (
75- prevVersion == null ||
76- semver . prerelease ( version ) ||
77- ! semver . satisfies ( version , `^${ prevVersion } ` )
78- ) {
79- result . push ( release )
80- prevVersion = version
81- }
76+ let prevVersion = fromVersion
77+ for ( let version of versions ) {
78+ if (
79+ ! full &&
80+ prevVersion != null &&
81+ ! semver . prerelease ( version ) &&
82+ semver . satisfies ( version , `^${ prevVersion } ` ) &&
83+ ! ( semver . prerelease ( prevVersion ) && ! semver . prerelease ( version ) )
84+ ) {
85+ continue
8286 }
83- } else {
84- const versions = Object . keys ( npmInfo . versions )
87+ prevVersion = version
88+
89+ const release : Release = { version, date : new Date ( npmInfo . time [ version ] ) }
90+ releases . push ( release )
8591
86- let prevVersion = fromVersion
87- for ( const version of versions ) {
88- if ( ! version ) continue
89- if ( prevVersion && semver . lte ( version , prevVersion ) ) continue
90- if ( toVersion && semver . gt ( version , toVersion ) ) break
91- if (
92- prevVersion == null ||
93- semver . prerelease ( version ) ||
94- ! semver . satisfies ( version , `^${ prevVersion } ` )
95- ) {
96- await octokit . repos
97- . getReleaseByTag ( {
98- owner,
99- repo,
100- tag : `v${ version } ` ,
101- } )
102- . then ( ( { data : { body } } : Object ) => {
103- result . push ( { version, body } )
104- } )
105- . catch ( ( ) => { } )
92+ const { url } =
93+ npmInfo . versions [ version ] . repository || npmInfo . repository || { }
94+ if ( ! url ) {
95+ release . error = new Error ( 'failed to get repository url from npm' )
96+ }
10697
107- prevVersion = version
98+ try {
99+ const { owner , repo } = parseRepositoryUrl ( url )
100+
101+ try {
102+ release . body = ( await octokit . repos . getReleaseByTag ( {
103+ owner,
104+ repo,
105+ tag : `v${ version } ` ,
106+ } ) ) . data . body
107+ } catch ( error ) {
108+ const changelog = await getChangelog ( owner , repo )
109+ if ( changelog [ version ] ) release . body = changelog [ version ] . body
110+ }
111+ if ( ! release . body ) {
112+ release . error = new Error (
113+ `failed to find GitHub release or changelog entry for version ${ version } `
114+ )
108115 }
116+ } catch ( error ) {
117+ release . error = error
109118 }
110119 }
111120
112- return result
121+ return releases
113122}
114123
115124if ( ! module . parent ) {
116- const pkg = process . argv [ 2 ]
117- let fromVersion = process . argv [ 3 ] ,
118- toVersion = process . argv [ 4 ]
125+ const full = process . argv . indexOf ( '--full' ) >= 0
126+ const args = process . argv . slice ( 2 ) . filter ( a => a [ 0 ] !== '-' )
127+ const pkg = args [ 0 ]
128+ let fromVersion = args [ 1 ] ,
129+ toVersion = args [ 2 ]
119130 if ( ! fromVersion ) {
120131 try {
121132 // $FlowFixMe
@@ -130,11 +141,14 @@ if (!module.parent) {
130141 }
131142 }
132143 /* eslint-env node */
133- whatBroke ( pkg , { fromVersion, toVersion } ) . then (
144+ whatBroke ( pkg , { fromVersion, toVersion, full } ) . then (
134145 ( changelog : Array < Release > ) => {
135- for ( const { version, body } of changelog ) {
146+ for ( const { version, body, error } of changelog ) {
136147 process . stdout . write ( chalk . bold ( version ) + '\n\n' )
137148 if ( body ) process . stdout . write ( body + '\n\n' )
149+ if ( error ) {
150+ process . stdout . write ( `Failed to get changelog: ${ error . stack } \n\n` )
151+ }
138152 }
139153 process . exit ( 0 )
140154 } ,
0 commit comments