Skip to content

Commit efe093f

Browse files
committed
Refactor to use this project as a module
You don't always have to use the CLI. Add a bunch of unit tests
1 parent 3486aaa commit efe093f

22 files changed

+5605
-254
lines changed

Diff for: .eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
lib/__tests__/scripts/syntax.js

Diff for: .eslintrc.json

+4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
{
22
"env": {
3+
"es6": true,
34
"node": true
45
},
56
"extends": "eslint:recommended",
7+
"parserOptions": {
8+
"ecmaVersion": 6
9+
},
610
"rules": {
711
"no-console": 0
812
}

Diff for: README.md

+40-5
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ It's preferable to install it globally through [`npm`](https://www.npmjs.com/pac
88

99
npm install -g git-release-notes
1010

11+
It's also possible to use `git-release-notes` as a node module. Check the usage on [usage as a module](#Usage_as_a_module)
12+
1113
### Usage
1214

13-
The basic usage is
15+
The basic usage from the command line is
1416

1517
cd <your_git_project>
1618
git-release-notes <since>..<until> <template>
@@ -69,7 +71,7 @@ More advanced options are
6971
* `f` or `file` JSON Configuration file, better option when you don't want to pass all parameters to the command line, for an example see [options.json](https://github.com/ariatemplates/git-release-notes/blob/master/options.json)
7072
* `s` or `script` External script for post-processing commits
7173
* `c` or `merge-commits` List only merge commits, `git log` command is executed with the `--merges` flag instead of `--no-merges`
72-
* `o` or `gitlog-option` to add some additional git log options **and** ignores the `merge-commits` option, this is direct given to `git log` by adding a `--` to each longname option from the array (e.g. `-o first-parent`).
74+
* `o` or `gitlog-option` to add some additional git log options **and** ignores the `merge-commits` option, this is direct given to `git log` by adding a `--` to each longname option from the array (e.g. `-o first-parent`).
7375

7476
#### Title Parsing
7577

@@ -133,18 +135,51 @@ The object passed to the callback will be merged with the input data and passed
133135

134136
For an example check `samples/post-processing.js`
135137

138+
139+
### Usage as a module
140+
141+
#### Installation
142+
143+
npm install --save-dev git-release-notes
144+
145+
#### Usage
146+
147+
Inside your script file
148+
149+
```js
150+
const releaseNotes = require('git-release-notes');
151+
152+
const OPTIONS = {
153+
branch: 'master',
154+
};
155+
const RANGE = 'v1.0.0..v2.0.0';
156+
const TEMPLATE = 'markdown';
157+
158+
releaseNotes(OPTIONS, RANGE, TEMPLATE)
159+
.then((changelog) => {
160+
console.log(`Changelog between ${RANGE}\n\n${changelog}`);
161+
})
162+
.catch((ex) => {
163+
console.error(ex);
164+
process.exit(1);
165+
});
166+
```
167+
168+
#### Options
169+
170+
The syntax reflects the command line parameters, so options is an object containing `path`, `branch`, `title` and so on. You can refer to the list of options in the command line usage section. You can use either the long or short syntax, the module will use the same defaults as the command line if an option is missing.
171+
172+
136173
### Debug
137174
If your post processing script or template throws an exception, the JSON data will be written to the file system in the same folder as the processing script.
138175

139176
The DEBUG environment variable can also be useful for fault diagnosis:
140177

141178
#### Linux
142-
DEBUG=release-notes:*
179+
export DEBUG=release-notes:*
143180
git-release-notes ...
144181

145182
#### Windows
146183

147184
SET DEBUG=release-notes:cli,release-notes:externalscript
148185
git-release-notes ...
149-
150-
Note the filtering options available: `release-notes:cli`, `release-notes:externalscript`, `release-notes:data`

Diff for: cli.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/usr/bin/env node
2+
var argv = require("optimist").usage("git-release-notes [<options>] <since>..<until> <template>")
3+
.options("f", {
4+
"alias": "file"
5+
})
6+
.options("p", {
7+
"alias": "path",
8+
"default": process.cwd()
9+
})
10+
.options("t", {
11+
"alias": "title",
12+
"default": "(.*)"
13+
})
14+
.boolean("i")
15+
.alias("i", "ignore-case")
16+
.options("m", {
17+
"alias": "meaning",
18+
"default": ['type']
19+
})
20+
.options("b", {
21+
"alias": "branch",
22+
"default": "master"
23+
})
24+
.options("s", {
25+
"alias": "script"
26+
})
27+
.options("o", {
28+
"alias": "gitlog-option",
29+
"default" : []
30+
})
31+
.boolean("c")
32+
.alias("c", "merge-commits")
33+
.describe({
34+
"f": "Configuration file",
35+
"p": "Git project path",
36+
"t": "Commit title regular expression",
37+
"i": "Ignore case of title's regular expression",
38+
"m": "Meaning of capturing block in title's regular expression",
39+
"b": "Git branch, defaults to master",
40+
"s": "External script to rewrite the commit history",
41+
"c": "Only use merge commits",
42+
"o": "Additional git log options AND ignore 'c' option"
43+
})
44+
.boolean("version")
45+
.check(function (argv) {
46+
if (argv._.length == 2) {
47+
return true;
48+
}
49+
throw "Invalid parameters, please specify an interval and the template";
50+
})
51+
.argv;
52+
53+
const index = require('./index');
54+
index(argv, argv._[0], argv._[1])
55+
.then(function (output) {
56+
process.stdout.write(output + "\n");
57+
})
58+
.catch(function (error) {
59+
require("optimist").showHelp();
60+
console.error('\n', error.message);
61+
process.exit(1);
62+
});

Diff for: index.js

+18-173
Original file line numberDiff line numberDiff line change
@@ -1,190 +1,35 @@
1-
#!/usr/bin/env node
2-
var argv = require("optimist").usage("git-release-notes [<options>] <since>..<until> <template>")
3-
.options("f", {
4-
"alias": "file"
5-
})
6-
.options("p", {
7-
"alias": "path",
8-
"default": process.cwd()
9-
})
10-
.options("t", {
11-
"alias": "title",
12-
"default": "(.*)"
13-
})
14-
.boolean("i")
15-
.alias("i", "ignore-case")
16-
.options("m", {
17-
"alias": "meaning",
18-
"default": ['type']
19-
})
20-
.options("b", {
21-
"alias": "branch",
22-
"default": "master"
23-
})
24-
.options("s", {
25-
"alias": "script"
26-
})
27-
.options("o", {
28-
"alias": "gitlog-option",
29-
"default" : []
30-
})
31-
.boolean("c")
32-
.alias("c", "merge-commits")
33-
.describe({
34-
"f": "Configuration file",
35-
"p": "Git project path",
36-
"t": "Commit title regular expression",
37-
"i": "Ignore case of title's regular expression",
38-
"m": "Meaning of capturing block in title's regular expression",
39-
"b": "Git branch, defaults to master",
40-
"s": "External script to rewrite the commit history",
41-
"c": "Only use merge commits",
42-
"o": "Additional git log options AND ignore 'c' option"
43-
})
44-
.boolean("version")
45-
.check(function (argv) {
46-
if (argv._.length == 2) {
47-
return true;
48-
}
49-
throw "Invalid parameters, please specify an interval and the template";
50-
})
51-
.argv;
52-
531
var git = require("./lib/git");
54-
var fs = require("fs");
552
var ejs = require("ejs");
56-
var path = require("path");
573
var debug = require("debug")("release-notes:cli");
58-
var debugData = require("debug")("release-notes:data");
59-
var dateFnsFormat = require('date-fns/format')
60-
61-
var template = argv._[1];
62-
debug("Trying to locate template '%s'", template);
63-
if (!fs.existsSync(template)) {
64-
debug("Template file '%s' doesn't exist, maybe it's template name", template);
65-
// Template name?
66-
if (template.match(/[a-z]+(\.ejs)?/)) {
67-
template = path.resolve(__dirname, "./templates/" + path.basename(template, ".ejs") + ".ejs");
68-
} else {
69-
require("optimist").showHelp();
70-
console.error("\nUnable to locate template file " + template);
71-
process.exit(1);
72-
}
73-
}
74-
75-
debug("Trying to locate script '%s'", argv.s);
76-
if (argv.s && !fs.existsSync(argv.s)) {
77-
debug("Script file '%s' doesn't exist");
78-
require("optimist").showHelp();
79-
console.error("\nExternal script must be a valid path " + argv.s);
80-
process.exit(1);
81-
}
4+
var fileSystem = require('./lib/file-system');
5+
var processCommits = require('./lib/process').processCommits;
6+
var dateFnsFormat = require('date-fns/format');
827

83-
debug("Trying to read template '%s'", template);
84-
fs.readFile(template, function (err, templateContent) {
85-
if (err) {
86-
require("optimist").showHelp();
87-
console.error("\nUnable to locate template file " + argv._[1]);
88-
process.exit(5);
89-
} else {
90-
getOptions(function (options) {
91-
debug("Running git log in '%s' on branch '%s' with range '%s'", options.p, options.b, argv._[0]);
92-
git.log({
8+
module.exports = function module(cliOptions, positionalRange, positionalTemplate) {
9+
return fileSystem.resolveTemplate(positionalTemplate).then(function (template) {
10+
return fileSystem.resolveOptions(cliOptions).then(function (options) {
11+
debug("Running git log in '%s' on branch '%s' with range '%s'", options.p, options.b, positionalRange);
12+
return git.log({
9313
branch: options.b,
94-
range: argv._[0],
14+
range: positionalRange,
9515
title: options.i ? new RegExp(options.t, 'i') : new RegExp(options.t),
9616
meaning: Array.isArray(options.m) ? options.m: [options.m],
9717
cwd: options.p,
9818
mergeCommits: options.c,
9919
additionalOptions: Array.isArray(options.o) ? options.o : [options.o]
100-
}, function (commits) {
101-
postProcess(templateContent, commits);
20+
}).then(function (commits) {
21+
return processCommits(options, commits, positionalRange);
22+
}).then(function (data) {
23+
return render(positionalRange, template, data);
10224
});
10325
});
104-
}
105-
});
106-
107-
function getOptions (callback) {
108-
if (argv.f) {
109-
debug("Trying to read configuration file '%s'", argv.f);
110-
fs.readFile(argv.f, function (err, data) {
111-
if (err) {
112-
console.error("Unable to read configuration file\n" + err.message);
113-
} else {
114-
var options;
115-
try {
116-
var stored = JSON.parse(data);
117-
options = {
118-
b: stored.b || stored.branch || argv.b,
119-
t: stored.t || stored.title || argv.t,
120-
i: stored.i || stored.ignoreCase || argv.i,
121-
m: stored.m || stored.meaning || argv.m,
122-
o: stored.o || stored.gitlogOption || argv.o,
123-
p: stored.p || stored.path || argv.p,
124-
c: stored.c || stored.mergeCommits || argv.c
125-
};
126-
} catch (ex) {
127-
console.error("Invalid JSON in configuration file");
128-
}
129-
if (options) {
130-
callback(options);
131-
}
132-
}
133-
});
134-
} else {
135-
callback(argv);
136-
}
137-
}
138-
139-
function postProcess(templateContent, commits) {
140-
debug("Got %d commits", commits.length);
141-
if (commits.length) {
142-
if (argv.s) {
143-
var externalScriptPath = argv.s;
144-
try {
145-
var externalScript = require(externalScriptPath);
146-
} catch (ex) {
147-
debug("Exception while reading external script '%s': '%s'", externalScriptPath, ex.message);
148-
console.error('Unable to read external script');
149-
process.exit(7);
150-
}
151-
debug("Trying to run the external script");
152-
var inputData;
153-
var outputData;
154-
try {
155-
inputData = {
156-
commits: commits,
157-
range: argv._[0],
158-
dateFnsFormat: dateFnsFormat,
159-
debug: require("debug")("release-notes:externalscript")
160-
};
161-
externalScript(inputData, function (data) {
162-
outputData = data;
163-
render(templateContent, data);
164-
});
165-
debug("Waiting for external script to call the callback");
166-
} catch (ex) {
167-
debug("Exception while running external script '%s'", ex.message);
168-
debugData("Input data passed to the external script `%s`", JSON.stringify(inputData, null, ' '));
169-
debugData("Output data received from the external script `%s`", outputData ? JSON.stringify(outputData, null, ' ') : '');
170-
console.error('Error while processing external script', ex);
171-
process.exit(8);
172-
}
173-
} else {
174-
debug("Rendering template without post processing");
175-
render(templateContent, { commits: commits });
176-
}
177-
} else {
178-
console.error('No commits in the specified range');
179-
process.exit(6);
180-
}
181-
}
26+
});
27+
};
18228

183-
function render(templateContent, data) {
29+
function render(range, templateContent, data) {
18430
debug("Rendering template");
185-
var output = ejs.render(templateContent.toString(), Object.assign({
186-
range: argv._[0],
31+
return ejs.render(templateContent, Object.assign({
32+
range: range,
18733
dateFnsFormat: dateFnsFormat
18834
}, data));
189-
process.stdout.write(output + "\n");
19035
}

0 commit comments

Comments
 (0)