Skip to content

Commit

Permalink
Merge pull request #43 from ariatemplates/tests
Browse files Browse the repository at this point in the history
Refactor to use this project as a module
  • Loading branch information
piuccio authored Mar 16, 2018
2 parents 3486aaa + 262bd35 commit df6e166
Show file tree
Hide file tree
Showing 23 changed files with 5,624 additions and 254 deletions.
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
lib/__tests__/scripts/syntax.js
4 changes: 4 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
{
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 6
},
"rules": {
"no-console": 0
}
Expand Down
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: node_js

node_js:
- 4
- 6
- 8
- 9

cache:
directories:
- node_modules
- $(npm config get prefix)/bin
- $(npm config get prefix)/lib/node_modules
- $HOME/.npm
45 changes: 40 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ It's preferable to install it globally through [`npm`](https://www.npmjs.com/pac

npm install -g git-release-notes

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)

### Usage

The basic usage is
The basic usage from the command line is

cd <your_git_project>
git-release-notes <since>..<until> <template>
Expand Down Expand Up @@ -69,7 +71,7 @@ More advanced options are
* `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)
* `s` or `script` External script for post-processing commits
* `c` or `merge-commits` List only merge commits, `git log` command is executed with the `--merges` flag instead of `--no-merges`
* `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`).
* `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`).

#### Title Parsing

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

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


### Usage as a module

#### Installation

npm install --save-dev git-release-notes

#### Usage

Inside your script file

```js
const releaseNotes = require('git-release-notes');

const OPTIONS = {
branch: 'master',
};
const RANGE = 'v1.0.0..v2.0.0';
const TEMPLATE = 'markdown';

releaseNotes(OPTIONS, RANGE, TEMPLATE)
.then((changelog) => {
console.log(`Changelog between ${RANGE}\n\n${changelog}`);
})
.catch((ex) => {
console.error(ex);
process.exit(1);
});
```

#### Options

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.


### Debug
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.

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

#### Linux
DEBUG=release-notes:*
export DEBUG=release-notes:*
git-release-notes ...

#### Windows

SET DEBUG=release-notes:cli,release-notes:externalscript
git-release-notes ...

Note the filtering options available: `release-notes:cli`, `release-notes:externalscript`, `release-notes:data`
62 changes: 62 additions & 0 deletions cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env node
var argv = require("optimist").usage("git-release-notes [<options>] <since>..<until> <template>")
.options("f", {
"alias": "file"
})
.options("p", {
"alias": "path",
"default": process.cwd()
})
.options("t", {
"alias": "title",
"default": "(.*)"
})
.boolean("i")
.alias("i", "ignore-case")
.options("m", {
"alias": "meaning",
"default": ['type']
})
.options("b", {
"alias": "branch",
"default": "master"
})
.options("s", {
"alias": "script"
})
.options("o", {
"alias": "gitlog-option",
"default" : []
})
.boolean("c")
.alias("c", "merge-commits")
.describe({
"f": "Configuration file",
"p": "Git project path",
"t": "Commit title regular expression",
"i": "Ignore case of title's regular expression",
"m": "Meaning of capturing block in title's regular expression",
"b": "Git branch, defaults to master",
"s": "External script to rewrite the commit history",
"c": "Only use merge commits",
"o": "Additional git log options AND ignore 'c' option"
})
.boolean("version")
.check(function (argv) {
if (argv._.length == 2) {
return true;
}
throw "Invalid parameters, please specify an interval and the template";
})
.argv;

const index = require('./index');
index(argv, argv._[0], argv._[1])
.then(function (output) {
process.stdout.write(output + "\n");
})
.catch(function (error) {
require("optimist").showHelp();
console.error('\n', error.message);
process.exit(1);
});
191 changes: 18 additions & 173 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,190 +1,35 @@
#!/usr/bin/env node
var argv = require("optimist").usage("git-release-notes [<options>] <since>..<until> <template>")
.options("f", {
"alias": "file"
})
.options("p", {
"alias": "path",
"default": process.cwd()
})
.options("t", {
"alias": "title",
"default": "(.*)"
})
.boolean("i")
.alias("i", "ignore-case")
.options("m", {
"alias": "meaning",
"default": ['type']
})
.options("b", {
"alias": "branch",
"default": "master"
})
.options("s", {
"alias": "script"
})
.options("o", {
"alias": "gitlog-option",
"default" : []
})
.boolean("c")
.alias("c", "merge-commits")
.describe({
"f": "Configuration file",
"p": "Git project path",
"t": "Commit title regular expression",
"i": "Ignore case of title's regular expression",
"m": "Meaning of capturing block in title's regular expression",
"b": "Git branch, defaults to master",
"s": "External script to rewrite the commit history",
"c": "Only use merge commits",
"o": "Additional git log options AND ignore 'c' option"
})
.boolean("version")
.check(function (argv) {
if (argv._.length == 2) {
return true;
}
throw "Invalid parameters, please specify an interval and the template";
})
.argv;

var git = require("./lib/git");
var fs = require("fs");
var ejs = require("ejs");
var path = require("path");
var debug = require("debug")("release-notes:cli");
var debugData = require("debug")("release-notes:data");
var dateFnsFormat = require('date-fns/format')

var template = argv._[1];
debug("Trying to locate template '%s'", template);
if (!fs.existsSync(template)) {
debug("Template file '%s' doesn't exist, maybe it's template name", template);
// Template name?
if (template.match(/[a-z]+(\.ejs)?/)) {
template = path.resolve(__dirname, "./templates/" + path.basename(template, ".ejs") + ".ejs");
} else {
require("optimist").showHelp();
console.error("\nUnable to locate template file " + template);
process.exit(1);
}
}

debug("Trying to locate script '%s'", argv.s);
if (argv.s && !fs.existsSync(argv.s)) {
debug("Script file '%s' doesn't exist");
require("optimist").showHelp();
console.error("\nExternal script must be a valid path " + argv.s);
process.exit(1);
}
var fileSystem = require('./lib/file-system');
var processCommits = require('./lib/process').processCommits;
var dateFnsFormat = require('date-fns/format');

debug("Trying to read template '%s'", template);
fs.readFile(template, function (err, templateContent) {
if (err) {
require("optimist").showHelp();
console.error("\nUnable to locate template file " + argv._[1]);
process.exit(5);
} else {
getOptions(function (options) {
debug("Running git log in '%s' on branch '%s' with range '%s'", options.p, options.b, argv._[0]);
git.log({
module.exports = function module(cliOptions, positionalRange, positionalTemplate) {
return fileSystem.resolveTemplate(positionalTemplate).then(function (template) {
return fileSystem.resolveOptions(cliOptions).then(function (options) {
debug("Running git log in '%s' on branch '%s' with range '%s'", options.p, options.b, positionalRange);
return git.log({
branch: options.b,
range: argv._[0],
range: positionalRange,
title: options.i ? new RegExp(options.t, 'i') : new RegExp(options.t),
meaning: Array.isArray(options.m) ? options.m: [options.m],
cwd: options.p,
mergeCommits: options.c,
additionalOptions: Array.isArray(options.o) ? options.o : [options.o]
}, function (commits) {
postProcess(templateContent, commits);
}).then(function (commits) {
return processCommits(options, commits, positionalRange);
}).then(function (data) {
return render(positionalRange, template, data);
});
});
}
});

function getOptions (callback) {
if (argv.f) {
debug("Trying to read configuration file '%s'", argv.f);
fs.readFile(argv.f, function (err, data) {
if (err) {
console.error("Unable to read configuration file\n" + err.message);
} else {
var options;
try {
var stored = JSON.parse(data);
options = {
b: stored.b || stored.branch || argv.b,
t: stored.t || stored.title || argv.t,
i: stored.i || stored.ignoreCase || argv.i,
m: stored.m || stored.meaning || argv.m,
o: stored.o || stored.gitlogOption || argv.o,
p: stored.p || stored.path || argv.p,
c: stored.c || stored.mergeCommits || argv.c
};
} catch (ex) {
console.error("Invalid JSON in configuration file");
}
if (options) {
callback(options);
}
}
});
} else {
callback(argv);
}
}

function postProcess(templateContent, commits) {
debug("Got %d commits", commits.length);
if (commits.length) {
if (argv.s) {
var externalScriptPath = argv.s;
try {
var externalScript = require(externalScriptPath);
} catch (ex) {
debug("Exception while reading external script '%s': '%s'", externalScriptPath, ex.message);
console.error('Unable to read external script');
process.exit(7);
}
debug("Trying to run the external script");
var inputData;
var outputData;
try {
inputData = {
commits: commits,
range: argv._[0],
dateFnsFormat: dateFnsFormat,
debug: require("debug")("release-notes:externalscript")
};
externalScript(inputData, function (data) {
outputData = data;
render(templateContent, data);
});
debug("Waiting for external script to call the callback");
} catch (ex) {
debug("Exception while running external script '%s'", ex.message);
debugData("Input data passed to the external script `%s`", JSON.stringify(inputData, null, ' '));
debugData("Output data received from the external script `%s`", outputData ? JSON.stringify(outputData, null, ' ') : '');
console.error('Error while processing external script', ex);
process.exit(8);
}
} else {
debug("Rendering template without post processing");
render(templateContent, { commits: commits });
}
} else {
console.error('No commits in the specified range');
process.exit(6);
}
}
});
};

function render(templateContent, data) {
function render(range, templateContent, data) {
debug("Rendering template");
var output = ejs.render(templateContent.toString(), Object.assign({
range: argv._[0],
return ejs.render(templateContent, Object.assign({
range: range,
dateFnsFormat: dateFnsFormat
}, data));
process.stdout.write(output + "\n");
}
Loading

0 comments on commit df6e166

Please sign in to comment.