Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100755 211 lines (155 sloc) 5.518 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
36831ec 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 {
4474633 Georgios Kalpakas chore(CHANGELOG): add an extra new line after each item in the "BREAKING...
gkalpak authored
115 stream.write(util.format('%s %s\n', 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 });
36831ec 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);
36831ec 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) {
4474633 Georgios Kalpakas chore(CHANGELOG): add an extra new line after each item in the "BREAKING...
gkalpak authored
191
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
192 getPreviousTag().then(function(tag) {
193 console.log('Reading git log since', tag);
a14266e Matias Niemelä chore(CHANGELOG): introduce perf() section for performance-related commi...
matsko authored
194 readGitLog('^fix|^feat|^perf|BREAKING', tag).then(function(commits) {
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
195 console.log('Parsed', commits.length, 'commits');
196 console.log('Generating changelog to', file || 'stdout', '(', version, ')');
197 writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
198 });
199 });
200 };
201
202
203 // publish for testing
204 exports.parseRawCommit = parseRawCommit;
c5cba6e Georgios Kalpakas chore(changelog): add test for addition of trailing newline
gkalpak authored
205 exports.printSection = printSection;
4557881 Vojta Jina chore(release scripts): auto release scripts
vojtajina authored
206
207 // hacky start if not run by jasmine :-D
208 if (process.argv.join('').indexOf('jasmine-node') === -1) {
209 generate(process.argv[2], process.argv[3]);
210 }
Something went wrong with that request. Please try again.