1
+ "use strict" ;
2
+
3
+ const _ = require ( "lodash" ) ;
4
+ const request = require ( "request" ) ;
5
+ const fs = require ( "fs" ) ;
6
+ const path = require ( "path" ) ;
7
+ require ( "colors" ) ;
8
+
9
+ const argv = process . argv ;
10
+ if ( argv . length < 3 || argv . length > 4 ) {
11
+ console . error ( `Incorrect usage. You need to pass the milestone and optionally the Authorization token.\n` . red +
12
+ `### Example:
13
+ node generate_changelog.js 6.2.2 2d2156c261bb1494f7a6e22f11fa446c7ca0e6b7\n` . yellow ) ;
14
+ process . exit ( 127 ) ;
15
+ }
16
+
17
+ const selectedMilestone = process . argv [ 2 ] ;
18
+ const token = process . argv [ 3 ] || process . env . NS_CLI_CHANGELOG_AUTHORIZATION ;
19
+ if ( ! token ) {
20
+ console . error ( `Unable to find Authorization token.\n` . red +
21
+ `You must either set NS_CLI_CHANGELOG_AUTHORIZATION environment variable or pass the token as an argument to the script:\n` . yellow +
22
+ `node generate_changelog.js 6.2.2 2d2156c261bb1494f7a6e22f11fa446c7ca0e6b7\n` . green ) ;
23
+ process . exit ( 127 ) ;
24
+ }
25
+
26
+ const sendRequest = ( query ) => {
27
+ return new Promise ( ( resolve , reject ) => {
28
+ request . post ( "https://api.github.com/graphql" , {
29
+ headers : {
30
+ "Accept" : "application/json" ,
31
+ "Authorization" : `Bearer ${ token } ` ,
32
+ "User-Agent" : "NativeScript CLI Changelog Generator"
33
+ } ,
34
+ body : JSON . stringify ( query ) ,
35
+ followAllRedirects : true
36
+ } , ( err , response , body ) => {
37
+ if ( err ) {
38
+ reject ( err ) ;
39
+ return ;
40
+ }
41
+ resolve ( JSON . parse ( body ) ) ;
42
+ } ) ;
43
+ } ) ;
44
+ } ;
45
+
46
+ const getMilestonesInfoQuery = {
47
+ query : `{
48
+ repository(owner:"NativeScript", name:"nativescript-cli") {
49
+ milestones(first: 100, states: OPEN) {
50
+ nodes {
51
+ number
52
+ id
53
+ title
54
+ url
55
+ }
56
+ }
57
+ }
58
+ }`
59
+ } ;
60
+
61
+ sendRequest ( getMilestonesInfoQuery )
62
+ . then ( result => {
63
+ const milestones = result && result . data && result . data . repository && result . data . repository . milestones && result . data . repository . milestones . nodes || [ ] ;
64
+ const matchingMilestone = _ . find ( milestones , m => m . title === selectedMilestone ) ;
65
+ if ( ! matchingMilestone ) {
66
+ throw new Error ( `Unable to find milestone ${ selectedMilestone } in the milestones. Current milestones info is: ${ JSON . stringify ( milestones , null , 2 ) } ` ) ;
67
+ }
68
+ return matchingMilestone . number ;
69
+ } )
70
+ . then ( ( milestone ) => {
71
+ const getItemsForMilestoneQuery = {
72
+ query : `{
73
+ repository(owner:"NativeScript", name:"nativescript-cli") {
74
+ milestone(number: ${ milestone } ) {
75
+ number
76
+ id
77
+ issuePrioritiesDebug
78
+ url
79
+ issues(first: 100) {
80
+ nodes {
81
+ title
82
+ url
83
+ number
84
+ labels(first:100) {
85
+ edges {
86
+ node {
87
+ name
88
+ }
89
+ }
90
+ }
91
+ projectCards(first: 100) {
92
+ nodes {
93
+ column {
94
+ name
95
+ }
96
+ project {
97
+ name
98
+ number
99
+ }
100
+ state
101
+ }
102
+ }
103
+ }
104
+ }
105
+ }
106
+ }
107
+ }`
108
+ } ;
109
+ return sendRequest ( getItemsForMilestoneQuery ) ;
110
+ } )
111
+ . then ( ( milestoneQueryResult ) => {
112
+ const issues = ( milestoneQueryResult && milestoneQueryResult . data && milestoneQueryResult . data . repository &&
113
+ milestoneQueryResult . data . repository . milestone && milestoneQueryResult . data . repository . milestone . issues &&
114
+ milestoneQueryResult . data . repository . milestone . issues . nodes ) || [ ] ;
115
+ const finalIssuesForChangelog = [ ] ;
116
+ issues . forEach ( ( issue ) => {
117
+ const labels = ( ( issue . labels && issue . labels . edges ) || [ ] ) . map ( ( lblObj ) => lblObj && lblObj . node && lblObj . node . name ) ;
118
+ const isFeature = labels . indexOf ( "feature" ) !== - 1 ;
119
+ const isBug = labels . indexOf ( "bug" ) !== - 1 ;
120
+ const shouldBeSkipped = labels . indexOf ( "no-changelog" ) !== - 1 ;
121
+ if ( isFeature && isBug ) {
122
+ console . error ( `The item '${ issue . title } ' has both bug and feature label. Clear one of them and try again.` . red ) ;
123
+ process . exit ( 1 ) ;
124
+ } else if ( shouldBeSkipped ) {
125
+ console . log ( `Item ${ issue && issue . url } (${ issue && issue . title } ) will not be included in changelog as it has no-changelog label` . yellow ) ;
126
+ } else {
127
+ // check if we have resolved it:
128
+ const columns = ( issue && issue . projectCards && issue . projectCards . nodes || [ ] ) . map ( c => c && c . column && c . column . name ) ;
129
+ // There shouldn't be more than one columns.
130
+ const column = _ . first ( columns ) ;
131
+ if ( columns && column === "Ready for Test" || column === "In Testing" || column === "Done" ) {
132
+ finalIssuesForChangelog . push ( {
133
+ type : isFeature ? "feature" : "bug" ,
134
+ number : issue && issue . number ,
135
+ title : issue && issue . title ,
136
+ url : issue && issue . url
137
+ } ) ;
138
+ } else {
139
+ console . log ( `Item ${ issue && issue . url } (${ issue && issue . title } ) will not be included in changelog as its status is ${ columns } ` . yellow ) ;
140
+ }
141
+ }
142
+ } ) ;
143
+
144
+ return finalIssuesForChangelog ;
145
+ } )
146
+ . then ( data => {
147
+ const features = [ ] ;
148
+ const bugs = [ ] ;
149
+
150
+ _ . sortBy ( data , ( d ) => d . number )
151
+ . forEach ( d => {
152
+ if ( d . type === "feature" ) {
153
+ features . push ( `* [Implemented #${ d . number } ](${ d . url } ): ${ d . title } ` ) ;
154
+ } else {
155
+ bugs . push ( `* [Fixed #${ d . number } ](${ d . url } ): ${ d . title } ` ) ;
156
+ }
157
+ } ) ;
158
+
159
+ const pathToChangelog = path . join ( __dirname , "CHANGELOG.md" ) ;
160
+ let changelogContent = fs . readFileSync ( pathToChangelog ) . toString ( ) ;
161
+
162
+ if ( features . length === 0 && bugs . length === 0 ) {
163
+ console . error ( `Unable to find anything ready for milestone ${ selectedMilestone } ` . red ) ;
164
+ process . exit ( 2 ) ;
165
+ }
166
+
167
+ const monthNames = [ "January" , "February" , "March" , "April" , "May" , "June" ,
168
+ "July" , "August" , "September" , "October" , "November" , "December"
169
+ ] ;
170
+ const currentDate = new Date ( ) ;
171
+
172
+ let newChangelogContent = `\n${ selectedMilestone } (${ currentDate . getFullYear ( ) } , ${ monthNames [ currentDate . getMonth ( ) ] } ${ currentDate . getDate ( ) } )
173
+ ===
174
+ ` ;
175
+ if ( features . length > 0 ) {
176
+ newChangelogContent += `
177
+ ### New
178
+
179
+ ${ features . join ( "\n" ) }
180
+ ` ;
181
+ }
182
+ if ( bugs . length ) {
183
+ newChangelogContent += `
184
+ ### Fixed
185
+
186
+ ${ bugs . join ( "\n" ) }
187
+ ` ;
188
+ }
189
+
190
+ changelogContent = changelogContent . replace ( / ( N a t i v e S c r i p t C L I C h a n g e l o g \r ? \n = + \r ? \n ) ( [ \s \S ] * ) / m, `$1${ newChangelogContent } \n$2` ) ;
191
+ fs . writeFileSync ( pathToChangelog , changelogContent ) ;
192
+ console . log ( `Successfully added Changelog for ${ selectedMilestone } ` . green ) ;
193
+ console . log ( "Commit the local changes and send a PR." . magenta ) ;
194
+ } )
195
+ . catch ( error => console . error ( error ) ) ;
0 commit comments