Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 205 lines (151 sloc) 5.376 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: {},
145 breaks: {}
146 };
147
148 sections.breaks[EMPTY_COMPONENT] = [];
149
150 commits.forEach(function(commit) {
151 var section = sections[commit.type];
152 var component = commit.component || EMPTY_COMPONENT;
153
154 if (section) {
155 section[component] = section[component] || [];
156 section[component].push(commit);
157 }
158
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
159 if (commit.breaking) {
160 sections.breaks[component] = sections.breaks[component] || [];
161 sections.breaks[component].push({
162 subject: util.format("due to %s,\n %s", linkToCommit(commit.hash), commit.breaking),
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
163 hash: commit.hash,
164 closes: []
165 });
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
166 };
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
167 });
168
169 stream.write(util.format(HEADER_TPL, version, version, currentDate()));
170 printSection(stream, 'Bug Fixes', sections.fix);
171 printSection(stream, 'Features', sections.feat);
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
172 printSection(stream, 'Breaking Changes', sections.breaks, false);
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
173 }
174
175
176 var getPreviousTag = function() {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
177 var deferred = q.defer();
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
178 child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
179 if (code) deferred.reject('Cannot get the previous tag.');
180 else deferred.resolve(stdout.replace('\n', ''));
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
181 });
07a58dd Igor Minar chore(changelog.js): improve the changelog script
IgorMinar authored
182 return deferred.promise;
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
183 };
184
185
186 var generate = function(version, file) {
187 getPreviousTag().then(function(tag) {
188 console.log('Reading git log since', tag);
e3e57fb Igor Minar chore(changelog.js): pickup breaking changes f/ chore/refactor commits
IgorMinar authored
189 readGitLog('^fix|^feat|BREAKING', tag).then(function(commits) {
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
190 console.log('Parsed', commits.length, 'commits');
191 console.log('Generating changelog to', file || 'stdout', '(', version, ')');
192 writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
193 });
194 });
195 };
196
197
198 // publish for testing
199 exports.parseRawCommit = parseRawCommit;
200
201 // hacky start if not run by jasmine :-D
202 if (process.argv.join('').indexOf('jasmine-node') === -1) {
203 generate(process.argv[2], process.argv[3]);
204 }
Something went wrong with that request. Please try again.