1
1
module . exports = function ( grunt ) {
2
2
var Git = require ( 'nodegit' ) ;
3
3
var npmUtils = require ( 'npm-utils' ) ;
4
+ var Github = require ( 'github' ) ;
4
5
var gitUser = process . env . GIT_USER ;
5
6
var gitPassword = process . env . GIT_PASSWORD ;
6
7
var gitEmail = process . env . GIT_EMAIL ;
7
- var releaseNotes ;
8
+ var lastCommit ;
8
9
9
10
function getMasterCommit ( repo ) {
10
11
return repo . getMasterCommit ( ) ;
@@ -66,9 +67,7 @@ module.exports = function (grunt) {
66
67
newContent = JSON . parse ( blobs [ 0 ] . toString ( 'utf-8' ) ) ;
67
68
oldContent = JSON . parse ( blobs [ 1 ] . toString ( 'utf-8' ) ) ;
68
69
if ( newContent . version != oldContent . version ) {
69
- return buildReleaseNotes ( repo , oldContent . version ) . then ( function ( ) {
70
- return true ;
71
- } ) ;
70
+ return true ;
72
71
}
73
72
return false ;
74
73
} ) . then ( function ( versionChanged ) {
@@ -101,6 +100,10 @@ module.exports = function (grunt) {
101
100
return ;
102
101
}
103
102
return getMasterCommit ( repository )
103
+ . then ( function ( masterCommit ) {
104
+ lastCommit = masterCommit . sha ( ) ;
105
+ return masterCommit ;
106
+ } )
104
107
. then ( getPatches )
105
108
. then ( findPackageJsonPatch )
106
109
. then ( function ( patch ) {
@@ -164,7 +167,7 @@ module.exports = function (grunt) {
164
167
return Git . Object . lookup ( repository , id , Git . Object . TYPE . COMMIT ) ;
165
168
} )
166
169
. then ( function ( object ) {
167
- return Git . Tag . annotationCreate ( repository , version , object , author , 'Release v' + version ) ; // 0 = don't force tag creation
170
+ return Git . Tag . create ( repository , version , object , author , 'Release v' + version , 0 ) ;
168
171
} ) . then ( function ( ) {
169
172
grunt . log . writeln ( 'Created tag ' + version ) ;
170
173
return repository . getRemote ( 'origin' ) ;
@@ -189,30 +192,59 @@ module.exports = function (grunt) {
189
192
} ) ;
190
193
}
191
194
192
- function buildReleaseNotes ( repository , version ) {
193
- releaseNotes = '### Release ' + grunt . config . data . version + '\n Commits: \n' ;
194
- return repository . getReferenceCommit ( version ) . then ( function ( targetCommit ) {
195
- return getMasterCommit ( repository ) . then ( function ( masterCommit ) {
196
- return new Promise ( function ( resolve ) {
197
- var history = masterCommit . history ( Git . Revwalk . SORT . TIME ) ;
198
- history . on ( 'end' , function ( commits ) {
199
- for ( var i = 0 ; i < commits . length ; i ++ ) {
200
- var commit = commits [ i ] ;
201
- if ( commit . id ( ) . toString ( ) == targetCommit . id ( ) . toString ( ) ) {
202
- break ;
203
- }
204
-
205
- if ( process . env . TRAVIS_REPO_SLUG ) {
206
- releaseNotes += '* [[`' + commit . sha ( ) + '`](https://github.com/' + process . env . TRAVIS_REPO_SLUG + 'commit/' + commit . sha ( ) + ')] - ' + commit . summary ( ) + '\n' ;
207
- } else {
208
- releaseNotes += '* [`' + commit . sha ( ) + '`] - ' + commit . summary ( ) + '\n' ;
209
- }
210
- }
211
- resolve ( ) ;
212
- } ) ;
213
- history . start ( ) ;
195
+ function publishReleaseNotes ( ) {
196
+ var github = new Github ( ) ;
197
+ if ( ! process . env . TRAVIS_REPO_SLUG ) return ;
198
+ var owner = process . env . TRAVIS_REPO_SLUG . split ( '/' ) [ 0 ] ;
199
+ var repo = process . env . TRAVIS_REPO_SLUG . split ( '/' ) [ 1 ] ;
200
+ github . authenticate ( {
201
+ type : 'basic' ,
202
+ username : gitUser ,
203
+ password : gitPassword
204
+ } ) ;
205
+ var commitParser = new GithubCommitParser ( github , owner , repo ) ;
206
+ return github . gitdata . getTags ( {
207
+ owner : owner ,
208
+ repo : repo ,
209
+ per_page : 1
210
+ } ) . then ( function ( latestTag ) {
211
+ var latestReleaseCommit = null ;
212
+ if ( latestTag [ 0 ] ) {
213
+ latestReleaseCommit = latestTag [ 0 ] . object . sha ;
214
+ }
215
+ return commitParser . loadCommits ( lastCommit , latestReleaseCommit )
216
+ . then ( commitParser . loadClosedIssuesForCommits . bind ( commitParser ) )
217
+ . then ( commitParser . loadMergedPullRequestsForCommits . bind ( commitParser ) ) ;
218
+ } ) . then ( function ( ) {
219
+ var releaseNotes = '### Release ' + grunt . config . data . version + '\n' ;
220
+ if ( commitParser . closedIssues . length > 0 ) {
221
+ releaseNotes += '## Issues closed in this release: \n' ;
222
+ commitParser . closedIssues . forEach ( function ( issue ) {
223
+ releaseNotes += '* [[`#' + issue . number + '`]](' + issue . html_url + ') - ' + issue . title + '\n' ;
224
+ } ) ;
225
+ }
226
+ if ( commitParser . mergedPullRequests . length > 0 ) {
227
+ releaseNotes += '## Merged pull requests in this release: \n' ;
228
+ commitParser . mergedPullRequests . forEach ( function ( pr ) {
229
+ releaseNotes += '* [[`#' + pr . number + '`]](' + pr . html_url + ') - ' + pr . title + '\n' ;
214
230
} ) ;
231
+ }
232
+ releaseNotes += '## Commits in this release: \n' ;
233
+ commitParser . commits . forEach ( function ( commit ) {
234
+ releaseNotes += '* [[`' + commit . sha . substr ( 0 , 7 ) + '`]](' + commit . html_url + ') - ' + commit . split ( '\n' ) [ 0 ] + '\n' ;
235
+ } ) ;
236
+ return releaseNotes ;
237
+ } ) . then ( function ( releaseNotes ) {
238
+ return github . repos . createRelease ( {
239
+ owner : owner ,
240
+ repo : repo ,
241
+ tag_name : grunt . config . data . version ,
242
+ name : 'Release ' + grunt . config . data . version ,
243
+ body : releaseNotes
215
244
} ) ;
245
+ } )
246
+ . then ( function ( ) {
247
+ grunt . log . writeln ( 'created github release' ) ;
216
248
} ) ;
217
249
}
218
250
@@ -231,16 +263,134 @@ module.exports = function (grunt) {
231
263
}
232
264
233
265
grunt . registerTask ( 'publish-post-build' , function ( ) {
234
- grunt . task . requires ( 'build-only' ) ;
235
- var done = this . async ( ) ;
236
- commitRelease ( )
237
- . then ( function ( ) {
238
- grunt . log . writeln ( 'Done publishing.' ) ;
239
- done ( ) ;
240
- } ) . catch ( function ( e ) {
241
- grunt . log . error ( e ) ;
242
- grunt . log . error ( e . stack ( ) ) ;
243
- done ( false ) ;
266
+ grunt . task . requires ( 'build-only' ) ;
267
+ var done = this . async ( ) ;
268
+ commitRelease ( )
269
+ . then ( publishReleaseNotes )
270
+ . then ( function ( ) {
271
+ grunt . log . writeln ( 'Done publishing.' ) ;
272
+ done ( ) ;
273
+ } ) . catch ( function ( e ) {
274
+ grunt . log . error ( e ) ;
275
+ grunt . log . error ( e . stack ( ) ) ;
276
+ done ( false ) ;
277
+ } ) ;
278
+ } ) ;
279
+
280
+ } ;
281
+
282
+ function GithubCommitParser ( github , user , repository ) {
283
+ this . github = github ;
284
+ this . user = user ;
285
+ this . repository = repository ;
286
+ this . reset ( ) ;
287
+ }
288
+ GithubCommitParser . prototype = {
289
+ reset : function ( ) {
290
+ this . commits = [ ] ;
291
+ this . closedIssuesEvents = [ ] ;
292
+ this . closedIssues = [ ] ;
293
+ this . commitPage = 1 ;
294
+ this . issueEventsPage = 1 ;
295
+ this . issueEvents = [ ] ;
296
+ this . mergedPullRequests = [ ] ;
297
+ this . mergedPullRequestEvents = [ ] ;
298
+ } ,
299
+ loadCommits : function ( startCommitSha , endCommitSha ) {
300
+ var containsCommit = this . _containsCommit . bind ( this , endCommitSha ) ;
301
+ var self = this ;
302
+ return this . github . repos . getCommits ( {
303
+ owner : this . user ,
304
+ repo : this . repository ,
305
+ sha : startCommitSha ,
306
+ page : self . commitPage ++ ,
307
+ per_page : 100
308
+ } ) . then ( function ( commits ) {
309
+ self . commits = self . commits . concat ( commits ) ;
310
+ return containsCommit ( ) ;
311
+ } ) . then ( function ( res ) {
312
+ if ( res . contains && self . commits . length % 100 === 0 ) {
313
+ self . commits . splice ( res . index + 1 ) ;
314
+ return self ;
315
+ } else {
316
+ return self . loadCommits ( startCommitSha , endCommitSha ) ;
317
+ }
318
+ } ) ;
319
+ } ,
320
+ loadClosedIssuesForCommits : function ( ) {
321
+ var self = this ;
322
+ return new Promise ( function ( resolve ) {
323
+ if ( self . issueEvents . length === 0 ) {
324
+ resolve ( self . _loadIssueEvents ( ) ) ;
325
+ } else {
326
+ resolve ( ) ;
327
+ }
328
+ } ) . then ( function ( ) {
329
+ self . _filterClosedIssueEvents ( ) ;
330
+ } ) . then ( function ( ) {
331
+ self . closedIssues = self . closedIssuesEvents . map ( function ( event ) {
332
+ return event . issue ;
244
333
} ) ;
245
- } ) ;
334
+ return self ;
335
+ } ) ;
336
+ } ,
337
+ loadMergedPullRequestsForCommits : function ( ) {
338
+ var self = this ;
339
+ return new Promise ( function ( resolve ) {
340
+ if ( self . issueEvents . length === 0 ) {
341
+ resolve ( self . _loadIssueEvents ( ) ) ;
342
+ } else {
343
+ resolve ( ) ;
344
+ }
345
+ } ) . then ( function ( ) {
346
+ self . _filterPullRequests ( ) ;
347
+ } ) . then ( function ( ) {
348
+ self . mergedPullRequests = self . mergedPullRequestEvents . filter ( function ( event ) {
349
+ return event . issue ;
350
+ } ) ;
351
+ return self ;
352
+ } ) ;
353
+ } ,
354
+ _loadIssueEvents : function ( ) {
355
+ var self = this ;
356
+ return this . github . issues . getEventsForRepo ( {
357
+ owner : this . user ,
358
+ repo : this . repository ,
359
+ page : this . issueEventsPage ++ ,
360
+ per_page : 100
361
+ } ) . then ( function ( issueEvents ) {
362
+ self . issueEvents = self . issueEvents . concat ( issueEvents ) ;
363
+ if ( issueEvents . length < 100 ) {
364
+ return ;
365
+ } else {
366
+ return self . _loadIssueEvents ( ) ;
367
+ }
368
+ } ) ;
369
+ } ,
370
+ _filterPullRequests : function ( ) {
371
+ var commitShas = this . commits . map ( function ( commit ) {
372
+ return commit . sha ;
373
+ } ) ;
374
+ this . mergedPullRequestEvents = this . issueEvents . filter ( function ( event ) {
375
+ return commitShas . indexOf ( event . commit_id ) != - 1 && event . event == 'merged' ;
376
+ } ) ;
377
+ } ,
378
+ _filterClosedIssueEvents : function ( ) {
379
+ var commitShas = this . commits . map ( function ( commit ) {
380
+ return commit . sha ;
381
+ } ) ;
382
+ this . closedIssuesEvents = this . issueEvents . filter ( function ( issueEvent ) {
383
+ return commitShas . indexOf ( issueEvent . commit_id ) != - 1 && issueEvent . event == 'closed' && ! issueEvent . issue . pullRequest ;
384
+ } ) ;
385
+ } ,
386
+ _containsCommit : function ( commitSha ) {
387
+ var commits = this . commits . map ( function ( commit ) {
388
+ return commit . sha ;
389
+ } ) ;
390
+ if ( commits . indexOf ( commitSha ) == - 1 ) {
391
+ return { contains : false , index : null } ;
392
+ } else {
393
+ return { contains : true , index : commits . indexOf ( commitSha ) } ;
394
+ }
395
+ }
246
396
} ;
0 commit comments