UNPKG

6.14 kBJavaScriptView Raw
1/*
2 * grunt-release
3 * https://github.com/geddski/grunt-release
4 *
5 * Copyright (c) 2013 Dave Geddes
6 * Licensed under the MIT license.
7 */
8
9var shell = require('shelljs');
10var semver = require('semver');
11var request = require('superagent');
12var Q = require('q');
13
14module.exports = function(grunt){
15 grunt.registerTask('release', 'bump version, git tag, git push, npm publish', function(type){
16
17 //defaults
18 var options = grunt.util._.extend({
19 bump: true,
20 // file is in charge of master information, ie, it is it which define the base version to work on
21 file: grunt.config('pkgFile') || 'package.json',
22 // additionalFiles are additional files that also need to be bumped
23 additionalFiles: [],
24 add: true,
25 commit: true,
26 tag: true,
27 push: true,
28 pushTags: true,
29 npm : true,
30 remote: "origin"
31 }, grunt.config(this.name).options);
32
33 var config = setup(options.file, type);
34 var templateOptions = {
35 data: {
36 name: config.pkg.name,
37 version: config.newVersion
38 }
39 };
40
41 var tagName = grunt.template.process(grunt.config.getRaw(this.name + '.options.tagName') || '<%= version %>', templateOptions);
42 var commitMessage = grunt.template.process(grunt.config.getRaw(this.name + '.options.commitMessage') || 'release <%= version %>', templateOptions);
43 var tagMessage = grunt.template.process(grunt.config.getRaw(this.name + '.options.tagMessage') || 'version <%= version %>', templateOptions);
44
45 var nowrite = grunt.option('no-write');
46 var indentation = grunt.option('indentation') || ' ';
47 var task = this;
48 var done = this.async();
49
50 if (!config.newVersion) {
51 grunt.warn("Resulting version number is empty.");
52 }
53
54
55 if (nowrite){
56 grunt.log.ok('-------RELEASE DRY RUN-------');
57 }
58
59 Q()
60 .then(ifEnabled('bump', bump))
61 .then(ifEnabled('add', add))
62 .then(ifEnabled('commit', commit))
63 .then(ifEnabled('tag', tag))
64 .then(ifEnabled('push', push))
65 .then(ifEnabled('pushTags', pushTags))
66 .then(ifEnabled('npm', publish))
67 .then(ifEnabled('github', githubRelease))
68 .catch(function(msg){
69 grunt.fail.warn(msg || 'release failed')
70 })
71 .finally(done);
72
73
74 function setup(file, type){
75 var pkg = grunt.file.readJSON(file);
76 var newVersion = pkg.version;
77 if (options.bump) {
78 newVersion = semver.inc(pkg.version, type || 'patch');
79 }
80 options.additionalFiles.push(file);
81 return {files: options.additionalFiles, newVersion: newVersion};
82 }
83
84 function getNpmTag(){
85 var tag = grunt.option('npmtag') || options.npmtag;
86 if(tag === true) { tag = config.newVersion; }
87 return tag;
88 }
89
90 function ifEnabled(option, fn){
91 if (options[option]) return fn;
92 }
93
94 function run(cmd, msg){
95 var deferred = Q.defer();
96 grunt.verbose.writeln('Running: ' + cmd);
97
98 if (nowrite) {
99 grunt.log.ok(msg || cmd);
100 deferred.resolve();
101 }
102 else {
103 var success = shell.exec(cmd, {silent:true}).code === 0;
104
105 if (success){
106 grunt.log.ok(msg || cmd);
107 deferred.resolve();
108 }
109 else{
110 // fail and stop execution of further tasks
111 deferred.reject('Failed when executing: `' + cmd + '`\n');
112 }
113 }
114 return deferred.promise;
115 }
116
117 function add(){
118 var files = config.files.join(' ');
119 return run('git add ' + files, ' staged ' + files);
120 }
121
122 function commit(){
123 if (typeof commitMessage === 'string') {
124 commitMessage = [commitMessage];
125 }
126 var message = commitMessage.map(function(el) {
127 return '-m "' + grunt.template.process(el, templateOptions) + '"';
128 }).join(' ');
129
130 return run('git commit '+ config.file + ' ' + message, config.file + ' committed');
131 }
132
133 function tag(){
134 return run('git tag ' + tagName + ' -m "'+ tagMessage +'"', 'created new git tag: ' + tagName);
135 }
136
137 function push(){
138 run('git push ' + config.remote + ' HEAD', 'pushed to remote');
139 }
140
141 function pushTags(config){
142 run('git push ' + config.remote + ' ' + tagName, 'pushed new tag '+ config.newVersion +' to remote');
143 }
144
145 function publish(){
146 var cmd = 'npm publish';
147 var msg = 'published version '+ config.newVersion +' to npm';
148 var npmtag = getNpmTag();
149 if (npmtag){
150 cmd += ' --tag ' + npmtag;
151 msg += ' with a tag of "' + npmtag + '"';
152 }
153
154 if (options.folder){ cmd += ' ' + options.folder; }
155 return run(cmd, msg);
156 }
157
158 function bump(){
159 var i, l, file, pkg, promise;
160 var promises = [];
161 for (i = 0, l = config.files.length ; i < l ; i++) {
162 file = config.files[i];
163 promise = (function(file){
164 return Q.fcall(function () {
165 pkg = grunt.file.readJSON(file);
166 pkg.version = config.newVersion;
167 grunt.file.write(file, JSON.stringify(pkg, null, indentation) + '\n');
168 grunt.log.ok('bumped version of ' + file + ' to ' + config.newVersion);
169 });
170 }(file));
171 promises.push(promise);
172 }
173 return Q.all(promises);
174 }
175
176 function githubRelease(){
177 var deferred = Q.defer();
178 if (nowrite){
179 success();
180 return;
181 }
182
183 request
184 .post('https://api.github.com/repos/' + options.github.repo + '/releases')
185 .auth(process.env[options.github.usernameVar], process.env[options.github.passwordVar])
186 .set('Accept', 'application/vnd.github.manifold-preview')
187 .set('User-Agent', 'grunt-release')
188 .send({"tag_name": tagName, "name": tagMessage})
189 .end(function(res){
190 if (res.statusCode === 201){
191 success();
192 }
193 else {
194 deferred.reject('Error creating github release. Response: ' + res.text);
195 }
196 });
197
198 function success(){
199 grunt.log.ok('created ' + tagName + ' release on github.');
200 deferred.resolve();
201 }
202
203 return deferred.promise;
204 }
205
206 });
207};