Skip to content

Commit 12298ad

Browse files
feat(changelog): improve changelog generation scripts
1 parent 51f3a88 commit 12298ad

File tree

7 files changed

+260
-102
lines changed

7 files changed

+260
-102
lines changed

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@uirouter/publish-scripts",
3-
"version": "1.1.1",
3+
"version": "1.1.2",
44
"description": "Helper scripts for publishing UI-Router projects",
55
"main": "x",
66
"repository": {
@@ -23,7 +23,6 @@
2323
"modify_sourcemap_paths": "./modify_sourcemap_paths.js",
2424
"release": "./release.js",
2525
"show_changelog": "./show_changelog.js",
26-
"show_core_changelog": "./show_core_changelog.js",
2726
"update_changelog": "./update_changelog.js",
2827
"util": "./util.js"
2928
},
@@ -34,6 +33,8 @@
3433
"git-semver-tags": "^1.2.1",
3534
"readline-sync": "^1.4.7",
3635
"shelljs": "^0.7.8",
37-
"tweak-sourcemap-paths": "^0.0.2"
36+
"shx": "^0.2.2",
37+
"tweak-sourcemap-paths": "0.0.3",
38+
"yargs": "^9.0.1"
3839
}
39-
}
40+
}

show_changelog.js

Lines changed: 119 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,135 @@
11
#!/usr/bin/env node
22
"use strict";
3-
let conventionalChangelog = require('conventional-changelog');
3+
4+
const shelljs = require('shelljs');
5+
const path = require('path');
6+
const fs = require('fs');
7+
const conventionalChangelog = require('conventional-changelog');
8+
const _exec = require('./util')._exec;
9+
const yargs = require('yargs')
10+
.option('from', {
11+
description: 'The starting tag'
12+
})
13+
.option('to', {
14+
description: 'The ending tag'
15+
})
16+
.option('deps', {
17+
description: 'Include changelogs of dependencies',
18+
array: true,
19+
})
20+
.check(argv => {
21+
if (argv.to && !argv.from)
22+
throw new Error(`If you specify a 'to', you should also specify a 'from'.`);
23+
return true;
24+
});
425

526
let options = {
627
preset: 'ui-router-core'
728
};
829

9-
if(require.main === module) {
10-
let context, gitOpts;
30+
const context = {}, gitOpts = {};
31+
const to = yargs.argv.to;
32+
const from = yargs.argv.from;
33+
const deps = yargs.argv.deps || [];
34+
const scriptPath = path.resolve(__dirname, __filename);
35+
36+
if (to && from) {
37+
gitOpts.to = context.currentTag = to;
38+
gitOpts.from = context.previousTag = from;
39+
} else if (from) {
40+
gitOpts.from = context.previousTag = from;
41+
} else if (!to && !from) {
42+
gitOpts.from = context.previousTag = getVersion('current');
43+
} else {
44+
throw new Error('How did this happen?');
45+
}
46+
47+
const cwd = shelljs.pwd().stdout;
48+
49+
if (deps.length) {
50+
// If --deps was used, shell out and re-run the show_changelog command without the --deps argument
51+
// This is an awful hack to flush the changelog to stdout before getting the dependency changelog(s)
52+
// because conventional-changelog-core doesn't seem to have a callback to tap into
53+
const fromArg = (from ? ` --from ${from}` : '');
54+
const toArg = (to ? ` --to ${to}` : '');
55+
let stdout = _exec(`${scriptPath} ${fromArg} ${toArg}`, true).stdout;
56+
console.log(stdout.trim());
1157

12-
if (process.argv[2]) {
13-
context = {};
14-
gitOpts = {};
15-
gitOpts.from = context.previousTag = process.argv[2];
58+
shelljs.mkdir('.show_changelog.tmp');
59+
try {
60+
deps.forEach(showDepChangelog);
61+
} finally {
62+
shelljs.cd(cwd);
63+
shelljs.rm('-rf', '.show_changelog.tmp');
1664
}
65+
} else {
66+
showChangelog(context, gitOpts);
67+
}
68+
69+
function showDepChangelog(dependency) {
70+
if (typeof dependency !== 'string') throw new Error('Expected dep to be a string: ' + dependency);
71+
72+
const tmpdir = path.resolve(cwd, '.show_changelog.tmp', dependency.replace(/[^a-zA-Z]/g, "_"));
1773

18-
if (process.argv[3]) {
19-
gitOpts.to = context.currentTag = process.argv[3];
74+
const pkgPath = `${cwd}/node_modules/${dependency}/package.json`;
75+
const pkg = JSON.parse(fs.readFileSync(pkgPath));
76+
const repotype = pkg.repository && pkg.repository.type;
77+
let giturl = pkg.repository && pkg.repository.url;
78+
79+
if (repotype !== 'git') {
80+
throw new Error(`Expected repository.type to be 'git' in ${pkgPath} but it was '${repotype}'`);
2081
}
2182

22-
showChangelog(context, gitOpts);
83+
if (!giturl) {
84+
throw new Error(`Expected repository.url to be defined in ${pkgPath} `);
85+
}
86+
87+
giturl = giturl.replace(/^git\+/, '');
88+
89+
const from = getDepVersion(dependency, 'tag', gitOpts.from);
90+
const to = (function() {
91+
if (gitOpts.to) return getDepVersion(dependency, 'tag', gitOpts.to);
92+
return getDepVersion(dependency, 'workingcopy');
93+
}());
94+
95+
if (from === to) return;
96+
97+
try {
98+
_exec(`git clone ${giturl} ${tmpdir}`, true);
99+
shelljs.config.silent = true;
100+
shelljs.pushd(tmpdir);
101+
shelljs.config.silent = false;
102+
console.log(`\n`);
103+
console.log(`### Updated \`${dependency}\` from ${from} to ${to}`);
104+
let depChangelog = _exec(`node ${scriptPath} --from ${from} --to ${to}`, true).stdout.trim();
105+
console.log(depChangelog.split(/[\r\n]/).slice(1).join('\n'));
106+
} finally {
107+
shelljs.popd();
108+
}
109+
}
110+
111+
// mode: 'workingcopy', 'current', 'previous', 'tag'
112+
// tag: (optional) the tag to fetch from
113+
function getVersion(mode, tag) {
114+
const showversion = path.resolve(__dirname, 'show_version.js');
115+
return _exec(`node ${showversion} --${mode} ${tag || ''}`, true).stdout.replace(/[\r\n]/g, '');
116+
}
117+
118+
// dep: the dependency
119+
// mode: 'workingcopy', 'current', 'previous', 'tag'
120+
// tag: (optional) the tag to fetch from
121+
function getDepVersion(dep, mode, tag) {
122+
const showversion = path.resolve(__dirname, 'show_version.js');
123+
return _exec(`node ${showversion} --dep ${dep} --${mode} ${tag || ''}`, true).stdout.replace(/[\r\n]/g, '');
23124
}
24125

25126
function showChangelog(context, gitOpts) {
26-
var writerOpts = { doFlush: true, generateOn: function() { return false; } };
27-
conventionalChangelog(options, context, gitOpts, undefined, writerOpts).pipe(process.stdout);
127+
var writerOpts = {
128+
doFlush: true,
129+
generateOn: function () {
130+
return false;
131+
}
132+
};
133+
const readable = conventionalChangelog(options, context, gitOpts, undefined, writerOpts).pipe(process.stdout);
134+
return new Promise(resolve => readable.on('end', resolve));
28135
}

show_core_changelog.js

Lines changed: 0 additions & 71 deletions
This file was deleted.

show_version.js

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
4+
const gitSemverTags = require('git-semver-tags');
5+
const fs = require('fs');
6+
const shelljs = require('shelljs');
7+
const pkgJson = JSON.parse(fs.readFileSync('package.json'));
8+
9+
const opts = ['current', 'previous', 'workingcopy', 'tag'];
10+
const yargs = require('yargs')
11+
.group(opts, 'Show version number of:')
12+
.option('workingcopy', {
13+
alias: 'w',
14+
boolean: true,
15+
description: `The working copy's package.json`
16+
})
17+
.option('current', {
18+
alias: 'c',
19+
boolean: true,
20+
description: 'The latest tagged version'
21+
})
22+
.option('previous', {
23+
alias: 'p',
24+
boolean: true,
25+
description: 'The second latest tagged version'
26+
})
27+
.option('tag', {
28+
alias: 't',
29+
string: true,
30+
description: 'A specific tag'
31+
})
32+
.group(['dep'], 'Show version number of a dependency:')
33+
.option('dep', {
34+
alias: 'd',
35+
string: true,
36+
description: 'The name of the dependency',
37+
})
38+
.check(argv => {
39+
const optsDesc = opts.map(opt => `--${opt}`).join(', ');
40+
41+
if (opts.every(opt => !argv[opt])) throw new Error(`Specify one of: ${optsDesc}`);
42+
if (opts.filter(opt => !!argv[opt]).length > 1) throw new Error(`Opts ${optsDesc} are mutually exclusive`);
43+
44+
return true;
45+
});
46+
47+
function getPkgJson() {
48+
const mode = opts.find(opt => yargs.argv[ opt ] && opt);
49+
50+
const getPkgJsonForTag = (tag) =>
51+
JSON.parse(shelljs.exec('git show ' + tag + ':package.json', { silent: true }).stdout);
52+
53+
switch (mode) {
54+
case 'workingcopy':
55+
return Promise.resolve(pkgJson);
56+
case 'current':
57+
case 'previous':
58+
return findTag(mode).then(getPkgJsonForTag);
59+
case 'tag':
60+
return Promise.resolve(getPkgJsonForTag(yargs.argv.tag));
61+
default:
62+
throw new Error(`Unknown mode: ${mode}`);
63+
}
64+
}
65+
66+
function getSourceString() {
67+
switch (mode) {
68+
case 'workingcopy': return 'workingcopy';
69+
case 'current': return 'current tag';
70+
case 'previous': return 'previous tag';
71+
case 'tag': return `tag ${yargs.argv.tag}`;
72+
}
73+
}
74+
75+
getPkgJson().then(json => {
76+
let dep = yargs.argv.dep;
77+
78+
if (dep) {
79+
const depVer = json.dependencies && json.dependencies[ dep ];
80+
const devDepVer = json.devDependencies && json.devDependencies[ dep ];
81+
82+
if (!depVer && !devDepVer) {
83+
console.error(JSON.stringify(json));
84+
throw new Error(`package.json from ${getSourceString()} has no dependencies["${dep}"] or devDependencies["${dep}"] key.`);
85+
}
86+
87+
console.log((depVer || devDepVer).replace(/[^0-9a-zA-Z._+-]/g, ''));
88+
} else {
89+
console.log(json.version.replace(/[^0-9a-zA-Z._+-]/g, ''));
90+
}
91+
92+
process.exit(0);
93+
});
94+
95+
// previous or current
96+
function findTag(mode) {
97+
return new Promise((resolve, reject) => {
98+
gitSemverTags(function (err, tags) {
99+
if (err) return reject(err);
100+
101+
let tag;
102+
if (mode === 'current') tag = tags[0];
103+
if (mode === 'previous') tag = tags[1];
104+
105+
if (tag) resolve(tag);
106+
107+
reject("No tag found");
108+
});
109+
})
110+
}

update_changelog.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,11 @@ require('./util').packageDir();
44
require('shelljs/global');
55
const args = process.argv.slice(2);
66

7-
const path = require('path');
87
const fs = require('fs');
9-
const pkg = JSON.parse(fs.readFileSync('package.json'));
108
const _exec = require('./util')._exec;
119

1210
echo('Updating CHANGELOG...');
1311
cp('CHANGELOG.md', 'CHANGELOG.bak');
14-
_exec('./node_modules/.bin/show_changelog >> CHANGELOG.new');
15-
if (args[0] === '--include-core') {
16-
_exec('./node_modules/.bin/show_core_changelog >> CHANGELOG.new');
17-
}
12+
_exec('./node_modules/.bin/show_changelog ' + args + ' >> CHANGELOG.new');
1813
_exec('cat CHANGELOG.new CHANGELOG.bak > CHANGELOG.md');
1914
rm('CHANGELOG.bak', 'CHANGELOG.new');

util.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ function ensureCleanMaster(branch) {
1717
throw new Error('Working copy is dirty, aborting');
1818
}
1919

20-
function _exec(command) {
21-
echo(command);
22-
echo();
23-
var result = exec(command);
20+
function _exec(command, silent) {
21+
if (!silent) {
22+
echo(command);
23+
echo();
24+
}
25+
var result = exec(command, { silent: !!silent });
2426
if (result.code === 0) return result;
27+
echo(`cwd: ${process.cwd()}`);
2528
echo(`Aborting; non-zero return value (${result.code}) from: ${command}`);
29+
console.error(result.stderr);
2630
exit(result.code)
2731
}
2832

0 commit comments

Comments
 (0)