Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

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