Skip to content

Commit 4557881

Browse files
vojtajinaIgorMinar
authored andcommitted
chore(release scripts): auto release scripts
1 parent af0ad65 commit 4557881

6 files changed

+430
-0
lines changed

changelog.js

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
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+
var MAX_SUBJECT_LENGTH = 80;
20+
21+
22+
var warn = function() {
23+
console.log('WARNING:', util.format.apply(null, arguments));
24+
};
25+
26+
27+
var parseRawCommit = function(raw) {
28+
if (!raw) return null;
29+
30+
var lines = raw.split('\n');
31+
var msg = {}, match;
32+
33+
msg.hash = lines.shift();
34+
msg.subject = lines.shift();
35+
msg.closes = [];
36+
msg.breaks = [];
37+
38+
lines.forEach(function(line) {
39+
match = line.match(/Closes\s#(\d+)/);
40+
if (match) msg.closes.push(parseInt(match[1]));
41+
42+
match = line.match(/Breaks\s(.*)/);
43+
if (match) msg.breaks.push(match[1]);
44+
});
45+
46+
msg.body = lines.join('\n');
47+
match = msg.subject.match(/^(.*)\((.*)\)\:\s(.*)$/);
48+
49+
if (!match || !match[1] || !match[3]) {
50+
warn('Incorrect message: %s %s', msg.hash, msg.subject);
51+
return null;
52+
}
53+
54+
if (match[3].length > MAX_SUBJECT_LENGTH) {
55+
warn('Too long subject: %s %s', msg.hash, msg.subject);
56+
match[3] = match[3].substr(0, MAX_SUBJECT_LENGTH);
57+
}
58+
59+
msg.type = match[1];
60+
msg.component = match[2];
61+
msg.subject = match[3];
62+
63+
return msg;
64+
};
65+
66+
67+
var linkToIssue = function(issue) {
68+
return util.format(LINK_ISSUE, issue, issue);
69+
};
70+
71+
72+
var linkToCommit = function(hash) {
73+
return util.format(LINK_COMMIT, hash.substr(0, 8), hash);
74+
};
75+
76+
77+
var currentDate = function() {
78+
var now = new Date();
79+
var pad = function(i) {
80+
return ('0' + i).substr(-2);
81+
};
82+
83+
return util.format('%d-%s-%s', now.getFullYear(), pad(now.getMonth() + 1), pad(now.getDate()));
84+
};
85+
86+
87+
var printSection = function(stream, title, section) {
88+
var NESTED = true;
89+
var components = Object.getOwnPropertyNames(section).sort();
90+
91+
if (!components.length) return;
92+
93+
stream.write(util.format('\n## %s\n\n', title));
94+
95+
components.forEach(function(name) {
96+
var prefix = '-';
97+
98+
if (name !== EMPTY_COMPONENT) {
99+
if (NESTED) {
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) {
108+
stream.write(util.format('%s %s (%s', prefix, commit.subject, linkToCommit(commit.hash)));
109+
if (commit.closes.length) {
110+
stream.write(', closes ' + commit.closes.map(linkToIssue).join(', '));
111+
}
112+
stream.write(')\n');
113+
});
114+
});
115+
116+
stream.write('\n');
117+
};
118+
119+
120+
var readGitLog = function(grep, from) {
121+
var deffered = q.defer();
122+
123+
// TODO(vojta): if it's slow, use spawn and stream it instead
124+
child.exec(util.format(GIT_LOG_CMD, grep, '%H%n%s%n%b%n==END==', from), function(code, stdout, stderr) {
125+
var commits = [];
126+
127+
stdout.split('\n==END==\n').forEach(function(rawCommit) {
128+
var commit = parseRawCommit(rawCommit);
129+
if (commit) commits.push(commit);
130+
});
131+
132+
deffered.resolve(commits);
133+
});
134+
135+
return deffered.promise;
136+
};
137+
138+
139+
var writeChangelog = function(stream, commits, version) {
140+
var sections = {
141+
fix: {},
142+
feat: {},
143+
breaks: {}
144+
};
145+
146+
sections.breaks[EMPTY_COMPONENT] = [];
147+
148+
commits.forEach(function(commit) {
149+
var section = sections[commit.type];
150+
var component = commit.component || EMPTY_COMPONENT;
151+
152+
if (section) {
153+
section[component] = section[component] || [];
154+
section[component].push(commit);
155+
}
156+
157+
commit.breaks.forEach(function(breakMsg) {
158+
sections.breaks[EMPTY_COMPONENT].push({
159+
subject: breakMsg,
160+
hash: commit.hash,
161+
closes: []
162+
});
163+
});
164+
});
165+
166+
stream.write(util.format(HEADER_TPL, version, version, currentDate()));
167+
printSection(stream, 'Bug Fixes', sections.fix);
168+
printSection(stream, 'Features', sections.feat);
169+
printSection(stream, 'Breaking Changes', sections.breaks);
170+
}
171+
172+
173+
var getPreviousTag = function() {
174+
var deffered = q.defer();
175+
child.exec(GIT_TAG_CMD, function(code, stdout, stderr) {
176+
if (code) deffered.reject('Cannot get the previous tag.');
177+
else deffered.resolve(stdout.replace('\n', ''));
178+
});
179+
return deffered.promise;
180+
};
181+
182+
183+
var generate = function(version, file) {
184+
getPreviousTag().then(function(tag) {
185+
console.log('Reading git log since', tag);
186+
readGitLog('^fix|^feat|Breaks', tag).then(function(commits) {
187+
console.log('Parsed', commits.length, 'commits');
188+
console.log('Generating changelog to', file || 'stdout', '(', version, ')');
189+
writeChangelog(file ? fs.createWriteStream(file) : process.stdout, commits, version);
190+
});
191+
});
192+
};
193+
194+
195+
// publish for testing
196+
exports.parseRawCommit = parseRawCommit;
197+
198+
// hacky start if not run by jasmine :-D
199+
if (process.argv.join('').indexOf('jasmine-node') === -1) {
200+
generate(process.argv[2], process.argv[3]);
201+
}

changelog.spec.js

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
describe('changelog.js', function() {
2+
var ch = require('./changelog');
3+
4+
describe('parseRawCommit', function() {
5+
it('should parse raw commit', function() {
6+
var msg = ch.parseRawCommit(
7+
'9b1aff905b638aa274a5fc8f88662df446d374bd\n' +
8+
'feat(scope): broadcast $destroy event on scope destruction\n' +
9+
'perf testing shows that in chrome this change adds 5-15% overhead\n' +
10+
'when destroying 10k nested scopes where each scope has a $destroy listener\n');
11+
12+
expect(msg.type).toBe('feat');
13+
expect(msg.hash).toBe('9b1aff905b638aa274a5fc8f88662df446d374bd');
14+
expect(msg.subject).toBe('broadcast $destroy event on scope destruction');
15+
expect(msg.body).toBe('perf testing shows that in chrome this change adds 5-15% overhead\n' +
16+
'when destroying 10k nested scopes where each scope has a $destroy listener\n')
17+
expect(msg.component).toBe('scope');
18+
});
19+
20+
21+
it('should parse closed issues', function() {
22+
var msg = ch.parseRawCommit(
23+
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
24+
'feat(ng-list): Allow custom separator\n' +
25+
'bla bla bla\n\n' +
26+
'Closes #123\nCloses #25\n');
27+
28+
expect(msg.closes).toEqual([123, 25]);
29+
});
30+
31+
32+
it('should parse breaking changes', function() {
33+
var msg = ch.parseRawCommit(
34+
'13f31602f396bc269076ab4d389cfd8ca94b20ba\n' +
35+
'feat(ng-list): Allow custom separator\n' +
36+
'bla bla bla\n\n' +
37+
'Breaks first breaking change\nsomething else\n' +
38+
'Breaks another breaking change\n');
39+
40+
expect(msg.breaks).toEqual(['first breaking change', 'another breaking change']);
41+
});
42+
});
43+
});

changelog.tmp.md

+80
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<a name="v1.0.0rc3"></a>
2+
# v1.0.0rc3 (2012-03-27)
3+
4+
5+
## Bug Fixes
6+
7+
- **$compile:**
8+
- create new (isolate) scopes for directives on root elements ([5390fb37](https://github.com/angular/angular.js/commit/5390fb37d2c01937922613fc57df4986af521787), closes [#817](https://github.com/angular/angular.js/issues/817))
9+
- don't touch static element attributes ([9cb2195e](https://github.com/angular/angular.js/commit/9cb2195e61a78e99020ec19d687a221ca88b5900))
10+
- Merge interpolated css class when replacing an element ([f49eaf8b](https://github.com/angular/angular.js/commit/f49eaf8bf2df5f4e0e82d6c89e849a4f82c8d414))
11+
- **$http:**
12+
- don't send Content-Type header when no data ([1a5bebd9](https://github.com/angular/angular.js/commit/1a5bebd927ecd22f9c34617642fdf58fe3f62efb), closes [#749](https://github.com/angular/angular.js/issues/749))
13+
- **$log:**
14+
- avoid console.log.apply calls in IE ([15213ec2](https://github.com/angular/angular.js/commit/15213ec212769837cb2b7e781ffc5bfd598d27ca), closes [#805](https://github.com/angular/angular.js/issues/805))
15+
- **$resource:**
16+
- support escaping of ':' in resource url ([6d6f8753](https://github.com/angular/angular.js/commit/6d6f875345e01f2c6c63ef95164f6f39e923da15))
17+
- **compiler:**
18+
- allow transclusion of root elements ([9918b748](https://github.com/angular/angular.js/commit/9918b748be01266eb10db39d51b4d3098d54ab66))
19+
- **e2e runner:**
20+
- fix typo that caused errors on IE8 ([ee5a5352](https://github.com/angular/angular.js/commit/ee5a5352fd4b94cedee6ef20d4bf2d43ce77e00b), closes [#806](https://github.com/angular/angular.js/issues/806))
21+
- **forEach:**
22+
- should ignore prototypically inherited properties ([8d7e6948](https://github.com/angular/angular.js/commit/8d7e6948496ff26ef1da8854ba02fcb8eebfed61), closes [#813](https://github.com/angular/angular.js/issues/813))
23+
- **forms:**
24+
- Remove double registering of form ([1faafa31](https://github.com/angular/angular.js/commit/1faafa31582c4e9413f48dc7d12f5b681f9fe9fd))
25+
- Set ng-valid/ng-invalid correctly ([08bfea18](https://github.com/angular/angular.js/commit/08bfea183a850b29da270eac47f80b598cbe600f))
26+
- **init:**
27+
- use jQuery#ready for init if available ([cb2ad9ab](https://github.com/angular/angular.js/commit/cb2ad9abf24e6f855cc749efe3155bd7987ece9d), closes [#818](https://github.com/angular/angular.js/issues/818))
28+
- **json:**
29+
- added support for iso8061 timezone ([5ac14f63](https://github.com/angular/angular.js/commit/5ac14f633a69f49973b5512780c6ec7752405967))
30+
- **matchers.toHaveClass:**
31+
- Correct reference to angular.mock.dump ([f701ce08](https://github.com/angular/angular.js/commit/f701ce08f9d63be05fc3b92f57ad473e1e749b2d))
32+
- **ng-switch:**
33+
- properly destroy child scopes ([2315d9b3](https://github.com/angular/angular.js/commit/2315d9b3610994b36c44e4a97fb1427d59471ce8))
34+
- **ngDocSpec:**
35+
- fix broken tests ([53b6f522](https://github.com/angular/angular.js/commit/53b6f522a56eea314cbd084816e08f24b2c7879f))
36+
- **ngForm:**
37+
- alias name||ngForm ([823adb23](https://github.com/angular/angular.js/commit/823adb231995e917bc060bfa49453e2a96bac2b6))
38+
- **ngRepeat:**
39+
- correct variable reference in error message ([935c1018](https://github.com/angular/angular.js/commit/935c1018da05dbf3124b2dd33619c4a3c82d7a2a))
40+
- **ngView:**
41+
- controller not published ([21e74c2d](https://github.com/angular/angular.js/commit/21e74c2d2e8e985b23711785287feb59965cbd90))
42+
- **q:**
43+
- resolve all of nothing to nothing ([ac75079e](https://github.com/angular/angular.js/commit/ac75079e2113949d5d64adbcf23d56f3cf295d41))
44+
- **select:**
45+
- multiselect failes to update view on selection insert ([6ecac8e7](https://github.com/angular/angular.js/commit/6ecac8e71a84792a434d21db2c245b3648c55f18))
46+
47+
48+
## Features
49+
50+
- **$compile:**
51+
- do not interpolate boolean attributes, rather evaluate them ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
52+
- **$controller:**
53+
- support controller registration via $controllerProvider ([d54dfecb](https://github.com/angular/angular.js/commit/d54dfecb00fba41455536c5ddd55310592fdaf84))
54+
- **$route:**
55+
- when matching consider trailing slash as optional ([a4fe51da](https://github.com/angular/angular.js/commit/a4fe51da3ba0dc297ecd389e230d6664f250c9a6), closes [#784](https://github.com/angular/angular.js/issues/784))
56+
- **assertArgFn:**
57+
- should support array annotated fns ([4b8d9260](https://github.com/angular/angular.js/commit/4b8d926062eb4d4483555bdbdec4656f585ab40b))
58+
- **http:**
59+
- added params parameter ([73c85930](https://github.com/angular/angular.js/commit/73c8593077155a9f2e8ef42efd4c497eba0bef4f))
60+
- **injector:**
61+
- infer _foo_ as foo ([f13dd339](https://github.com/angular/angular.js/commit/f13dd3393dfb7a33565c9360342c193bc0bddcb6))
62+
- **input.radio:**
63+
- Allow value attribute to be interpolated ([ade6c452](https://github.com/angular/angular.js/commit/ade6c452753145c84884d17027a7865bf4b34b0c))
64+
- **jqLite:**
65+
- make injector() and scope() work with the document object ([5fdab52d](https://github.com/angular/angular.js/commit/5fdab52dd7c269f99839f4fa6b5854d9548269fa))
66+
- add .controller() method ([6c5a05ad](https://github.com/angular/angular.js/commit/6c5a05ad49a1e083570c3dfe331403398f899dbe))
67+
- **ngValue:**
68+
- allow radio inputs to have non string values ([09e175f0](https://github.com/angular/angular.js/commit/09e175f02cca0f4a295fd0c9b980cd8f432e722b), closes [#816](https://github.com/angular/angular.js/issues/816))
69+
- **scope:**
70+
- broadcast $destroy event on scope destruction ([9b1aff90](https://github.com/angular/angular.js/commit/9b1aff905b638aa274a5fc8f88662df446d374bd))
71+
- **scope.$eval:**
72+
- Allow passing locals to the expression ([192ff61f](https://github.com/angular/angular.js/commit/192ff61f5d61899e667c6dbce4d3e6e399429d8b))
73+
74+
75+
## Breaking Changes
76+
77+
- boolean attrs are evaluated rather than interpolated ([a08cbc02](https://github.com/angular/angular.js/commit/a08cbc02e78e789a66e9af771c410e8ad1646e25))
78+
- ng-bind-attr directive removed ([55027132](https://github.com/angular/angular.js/commit/55027132f3d57e5dcf94683e6e6bd7b0aae0087d))
79+
- any app that depends on this service and its fallback to Modernizr, please ([aaedefb9](https://github.com/angular/angular.js/commit/aaedefb92e6bec6626e173e5155072c91471596a))
80+

release-commit.sh

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
3+
function catch_errors() {
4+
echo "ERROR. That's life."
5+
exit 1
6+
}
7+
8+
trap catch_errors ERR
9+
10+
TMP_FILE='changelog.tmp'
11+
CHANGELOG_FILE='CHANGELOG.md'
12+
13+
echo "Getting current version..."
14+
VERSION=`./version.js --current`
15+
16+
echo "Generating changelog..."
17+
./changelog.js $VERSION $TMP_FILE
18+
19+
cat $CHANGELOG_FILE >> $TMP_FILE
20+
mv -f $TMP_FILE $CHANGELOG_FILE
21+
22+
23+
echo "Updating version..."
24+
./version.js --remove-snapshot
25+
26+
echo "CONFIRM TO COMMIT"
27+
read WHATEVER
28+
29+
30+
echo "Creating commit..."
31+
git commit version.yaml CHANGELOG.md -m "chore(relase): cutting the v$VERSION release"
32+
33+
echo "Creating tag..."
34+
git tag "v$VERSION"

start-iteration.sh

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
./version.js --minor-bump
4+
VERSION=`./version.js --curent`
5+
git commit -a -m "chore(relase): start v$VERSION iteration"

0 commit comments

Comments
 (0)