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