Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 209 lines (154 sloc) 5.478 kb
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
1 #!/usr/bin/env node
2
3 // TODO(vojta): pre-commit hook for validating messages
4 // TODO(vojta): report errors, currently Q silence everything which really sucks
5
fe1188d Michał Gołębiowski refactor(jshint): reduce duplication & test all JS files
mzgol authored
6 'use strict';
7
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
8 var child = require('child_process');
9 var fs = require('fs');
10 var util = require('util');
11 var q = require('qq');
12
13 var GIT_LOG_CMD = 'git log --grep="%s" -E --format=%s %s..HEAD';
14 var GIT_TAG_CMD = 'git describe --tags --abbrev=0';
15
16 var HEADER_TPL = '<a name="%s"></a>\n# %s (%s)\n\n';
17 var LINK_ISSUE = '[#%s](https://github.com/angular/angular.js/issues/%s)';
18 var LINK_COMMIT = '[%s](https://github.com/angular/angular.js/commit/%s)';
19
20 var EMPTY_COMPONENT = '$$';
21
22
23 var warn = function() {
24 console.log('WARNING:', util.format.apply(null, arguments));
25 };
26
27
28 var parseRawCommit = function(raw) {
29 if (!raw) return null;
30
31 var lines = raw.split('\n');
32 var msg = {}, match;
33
34 msg.hash = lines.shift();
35 msg.subject = lines.shift();
36 msg.closes = [];
37 msg.breaks = [];
38
39 lines.forEach(function(line) {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
40 match = line.match(/(?:Closes|Fixes)\s#(\d+)/);
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
41 if (match) msg.closes.push(parseInt(match[1]));
42 });
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
43
e9ccec7 Igor Minar docs(changelog): release notes for 1.0.0rc5 reality-distortion
IgorMinar authored
44 match = raw.match(/BREAKING CHANGE:([\s\S]*)/);
45 if (match) {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
46 msg.breaking = match[1];
e9ccec7 Igor Minar docs(changelog): release notes for 1.0.0rc5 reality-distortion
IgorMinar authored
47 }
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
48
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
49
50 msg.body = lines.join('\n');
51 match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
52
53 if (!match || !match[1] || !match[3]) {
54 warn('Incorrect message: %s %s', msg.hash, msg.subject);
55 return null;
56 }
57
58 msg.type = match[1];
59 msg.component = match[2];
60 msg.subject = match[3];
61
62 return msg;
63 };
64
65
66 var linkToIssue = function(issue) {
67 return util.format(LINK_ISSUE, issue, issue);
68 };
69
70
71 var linkToCommit = function(hash) {
72 return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
73 };
74
75
76 var currentDate = function() {
77 var now = new Date();
78 var pad = function(i) {
79 return ('0' + i).substr(-2);
80 };
81
82 return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
83 };
84
85
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
86 var printSection = function(stream, title, section, printCommitLinks) {
87 printCommitLinks = printCommitLinks === undefined ? true : printCommitLinks;
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
88 var components = Object.getOwnPropertyNames(section).sort();
89
90 if (!components.length) return;
91
92 stream.write(util.format('\n## %s\n\n', title));
93
94 components.forEach(function(name) {
95 var prefix = '-';
7c430c5 Vojta Jina chore(release scripts): group changelog only if more than 1 entry
vojtajina authored
96 var nested = section[name].length > 1;
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
97
98 if (name !== EMPTY_COMPONENT) {
7c430c5 Vojta Jina chore(release scripts): group changelog only if more than 1 entry
vojtajina authored
99 if (nested) {
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
100 stream.write(util.format('- **%s:**\n', name));
101 prefix = ' -';
102 } else {
103 prefix = util.format('- **%s:**', name);
104 }
105 }
106
107 section[name].forEach(function(commit) {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
108 if (printCommitLinks) {
109 stream.write(util.format('%s %s\n (%s', prefix, commit.subject, linkToCommit(commit.hash)));
110 if (commit.closes.length) {
111 stream.write(',\n ' + commit.closes.map(linkToIssue).join(', '));
112 }
113 stream.write(')\n');
114 } else {
115 stream.write(util.format('%s %s', prefix, commit.subject));
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
116 }
117 });
118 });
119
120 stream.write('\n');
121 };
122
123
124 var readGitLog = function(grep, from) {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
125 var deferred = q.defer();
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
126
127 // TODO(vojta): if it's slow, use spawn and stream it instead
128 child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
129 var commits = [];
130
131 stdout.split('\n==END==\n').forEach(function(rawCommit) {
132 var commit = parseRawCommit(rawCommit);
133 if (commit) commits.push(commit);
134 });
135
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
136 deferred.resolve(commits);
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
137 });
138
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
139 return deferred.promise;
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
140 };
141
142
143 var writeChangelog = function(stream, commits, version) {
144 var sections = {
145 fix: {},
146 feat: {},
a14266e Matias Niemelä chore(CHANGELOG): introduce perf() section for performance-related commi...
matsko authored
147 perf: {},
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
148 breaks: {}
149 };
150
151 sections.breaks[EMPTY_COMPONENT] = [];
152
153 commits.forEach(function(commit) {
154 var section = sections[commit.type];
155 var component = commit.component || EMPTY_COMPONENT;
156
157 if (section) {
158 section[component] = section[component] || [];
159 section[component].push(commit);
160 }
161
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
162 if (commit.breaking) {
163 sections.breaks[component] = sections.breaks[component] || [];
164 sections.breaks[component].push({
165 subject: util.format("due to %s,\n %s", linkToCommit(commit.hash), commit.breaking),
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
166 hash: commit.hash,
167 closes: []
168 });
fe1188d Michał Gołębiowski refactor(jshint): reduce duplication & test all JS files
mzgol authored
169 }
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
170 });
171
172 stream.write(util.format(HEADER_TPL, version, version, currentDate()));
173 printSection(stream, 'Bug Fixes', sections.fix);
174 printSection(stream, 'Features', sections.feat);
a14266e Matias Niemelä chore(CHANGELOG): introduce perf() section for performance-related commi...
matsko authored
175 printSection(stream, 'Performance Improvements', sections.perf);
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
176 printSection(stream, 'Breaking Changes', sections.breaks, false);
fe1188d Michał Gołębiowski refactor(jshint): reduce duplication & test all JS files
mzgol authored
177 };
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
178
179
180 var getPreviousTag = function() {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
181 var deferred = q.defer();
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
182 child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
183 if (code) deferred.reject('Cannot get the previous tag.');
184 else deferred.resolve(stdout.replace('\n', ''));
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
185 });
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
186 return deferred.promise;
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
187 };
188
189
190 var generate = function(version, file) {
191 getPreviousTag().then(function(tag) {
192 console.log('Reading git log since', tag);
a14266e Matias Niemelä chore(CHANGELOG): introduce perf() section for performance-related commi...
matsko authored
193 readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
194 console.log('Parsed', commits.length, 'commits');
195 console.log('Generating changelog to', file || 'stdout', '(', version, ')');
196 writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
197 });
198 });
199 };
200
201
202 // publish for testing
203 exports.parseRawCommit = parseRawCommit;
204
205 // hacky start if not run by jasmine :-D
206 if (process.argv.join('').indexOf('jasmine-node') === -1) {
207 generate(process.argv[2], process.argv[3]);
208 }
Something went wrong with that request. Please try again.