changelog-machine 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +8 -0
- package/README.md +108 -0
- package/bin/cli.js +2 -0
- package/lib/cli/commands/printHelp.d.ts +2 -0
- package/lib/cli/commands/printHelp.js +15 -0
- package/lib/cli/commands/printMarkdown.d.ts +3 -0
- package/lib/cli/commands/printMarkdown.js +26 -0
- package/lib/cli/commands/printVersion.d.ts +2 -0
- package/lib/cli/commands/printVersion.js +7 -0
- package/lib/cli/config/options.d.ts +3 -0
- package/lib/cli/config/options.js +13 -0
- package/lib/cli/config/readConfigFile.d.ts +3 -0
- package/lib/cli/config/readConfigFile.js +25 -0
- package/lib/cli/index.d.ts +1 -0
- package/lib/cli/index.js +38 -0
- package/lib/core/async.d.ts +8 -0
- package/lib/core/async.js +28 -0
- package/lib/core/default.d.ts +6 -0
- package/lib/core/default.js +9 -0
- package/lib/core/git.d.ts +8 -0
- package/lib/core/git.js +88 -0
- package/lib/core/markdown.d.ts +8 -0
- package/lib/core/markdown.js +78 -0
- package/lib/core/test-helpers.d.ts +6 -0
- package/lib/core/test-helpers.js +14 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +13 -0
- package/lib/models/commit.d.ts +6 -0
- package/lib/models/commit.js +2 -0
- package/lib/models/config.d.ts +30 -0
- package/lib/models/config.js +2 -0
- package/lib/models/index.d.ts +3 -0
- package/lib/models/index.js +15 -0
- package/lib/models/tagGroup.d.ts +7 -0
- package/lib/models/tagGroup.js +2 -0
- package/package.json +46 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
Copyright (c) 2022 Philipp Kief
|
|
3
|
+
|
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
5
|
+
|
|
6
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
7
|
+
|
|
8
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<h1 align="center">
|
|
2
|
+
<br>
|
|
3
|
+
<img src="./logo.png" alt="logo" width="200">
|
|
4
|
+
<br><br>
|
|
5
|
+
Changelog Machine
|
|
6
|
+
<br>
|
|
7
|
+
<br>
|
|
8
|
+
</h1>
|
|
9
|
+
|
|
10
|
+
<h4 align="center">Generate a changelog based on Git commit messages and tags.</h4>
|
|
11
|
+
|
|
12
|
+
## Getting started
|
|
13
|
+
|
|
14
|
+
The tool can be executed with this command:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
npx changelog-machine
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
It will generate a file called CHANGELOG.md in the root directory of the project.
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
The generation of the changelog can be configured via a config file.
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
npx changelog-machine --config changelog.config.json
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Command overview
|
|
31
|
+
|
|
32
|
+
| COMMAND | ALIAS | DESCRIPTION |
|
|
33
|
+
| ----------- | ----- | ------------------------------ |
|
|
34
|
+
| `--config` | `-c` | Path to the configuration file |
|
|
35
|
+
| `--version` | `-v` | Print version |
|
|
36
|
+
| `--help` | `-h` | Print command instructions |
|
|
37
|
+
|
|
38
|
+
### Configuration file
|
|
39
|
+
|
|
40
|
+
The configuration file can be configured like this:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"title": "Changelog",
|
|
45
|
+
"description": "Some description text",
|
|
46
|
+
"repositoryUrl": "https://github.com/PKief/vscode-material-icon-theme",
|
|
47
|
+
"blacklistPattern": "Release|^\\d+\\.\\d+\\.\\d+$",
|
|
48
|
+
"filePath": "CHANGELOG.md"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
More detailed description of the config:
|
|
53
|
+
|
|
54
|
+
| CONFIG | DESCRIPTION | DEFAULT |
|
|
55
|
+
| ------------------ | ----------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
56
|
+
| `title` | Title of the changelog | "Changelog" |
|
|
57
|
+
| `description` | Description under the title | "All notable changes to this project will be documented in this file. Dates are displayed in UTC." |
|
|
58
|
+
| `filePath` | Path of the output file | "CHANGELOG.md" |
|
|
59
|
+
| `repositoryUrl` | Links to the repository for further information | |
|
|
60
|
+
| `blacklistPattern` | Regex to remove commit messages | |
|
|
61
|
+
|
|
62
|
+
## Example output
|
|
63
|
+
|
|
64
|
+
The following commit summary is printed into the CHANGELOG.md file and can look like this:
|
|
65
|
+
|
|
66
|
+
#### [v4.12.1](https://github.com/PKief/vscode-material-icon-theme/compare/v4.12.1...HEAD)
|
|
67
|
+
|
|
68
|
+
> February 5, 2022
|
|
69
|
+
|
|
70
|
+
- Update release commit message [`3167316`](https://github.com/PKief/vscode-material-icon-theme/commit/3167316)
|
|
71
|
+
- Update release workflow [`7f2520d`](https://github.com/PKief/vscode-material-icon-theme/commit/7f2520d)
|
|
72
|
+
- Adjust preview script for folder icons [`6a00111`](https://github.com/PKief/vscode-material-icon-theme/commit/6a00111)
|
|
73
|
+
- Fix compare link [`fa45abf`](https://github.com/PKief/vscode-material-icon-theme/commit/fa45abf)
|
|
74
|
+
- Filter out release commits [`e3468ca`](https://github.com/PKief/vscode-material-icon-theme/commit/e3468ca)
|
|
75
|
+
|
|
76
|
+
## Programmatic use
|
|
77
|
+
|
|
78
|
+
The tool can be imported as module into existing JavaScript or TypeScript code. Therefor it is necessary to install it via npm or yarn:
|
|
79
|
+
|
|
80
|
+
NPM:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
npm install --save-dev changelog-machine
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Yarn:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
yarn add --dev changelog-machine
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
The module can be imported like this:
|
|
93
|
+
|
|
94
|
+
```ts
|
|
95
|
+
import { printMarkdown } from 'changelog-machine';
|
|
96
|
+
|
|
97
|
+
// usage with default config
|
|
98
|
+
printMarkdown();
|
|
99
|
+
|
|
100
|
+
// usage with customizations
|
|
101
|
+
printMarkdown({
|
|
102
|
+
filePath: './changelog/CHANGES.md',
|
|
103
|
+
description: 'List of changes',
|
|
104
|
+
title: 'All releases',
|
|
105
|
+
blacklistPattern: '^Release', // excludes all commits that start with "Release"
|
|
106
|
+
repositoryUrl: 'https://github.com/PKief/vscode-material-icon-theme',
|
|
107
|
+
});
|
|
108
|
+
```
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printHelp = void 0;
|
|
4
|
+
const printHelp = () => {
|
|
5
|
+
return console.log(`
|
|
6
|
+
Usage
|
|
7
|
+
$ npx changelog-machine -c changelog.config.json
|
|
8
|
+
|
|
9
|
+
Options
|
|
10
|
+
--config, -c Path to the configuration file
|
|
11
|
+
--version, -v Show version
|
|
12
|
+
--help, -h Show help
|
|
13
|
+
`);
|
|
14
|
+
};
|
|
15
|
+
exports.printHelp = printHelp;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.printMarkdown = void 0;
|
|
13
|
+
const async_1 = require("../../core/async");
|
|
14
|
+
const markdown_1 = require("../../core/markdown");
|
|
15
|
+
const default_1 = require("../../core/default");
|
|
16
|
+
const printMarkdown = (config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
17
|
+
var _a;
|
|
18
|
+
const output = yield (0, markdown_1.createMarkdown)(config);
|
|
19
|
+
try {
|
|
20
|
+
yield (0, async_1.outputFileAsync)((_a = config === null || config === void 0 ? void 0 : config.filePath) !== null && _a !== void 0 ? _a : default_1.defaultConfig.outputFilename, output);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error(error);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
exports.printMarkdown = printMarkdown;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.printVersion = void 0;
|
|
4
|
+
const printVersion = () => {
|
|
5
|
+
return console.log(`${process.env.npm_package_name} v${process.env.npm_package_version}`);
|
|
6
|
+
};
|
|
7
|
+
exports.printVersion = printVersion;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.flags = void 0;
|
|
4
|
+
const flags = {
|
|
5
|
+
string: 'config',
|
|
6
|
+
boolean: ['version', 'help'],
|
|
7
|
+
alias: { v: 'version', h: 'help' },
|
|
8
|
+
unknown: (a) => {
|
|
9
|
+
console.log(`The argument '${a}' is invalid for this command. Please use --help to read the description of this command.`);
|
|
10
|
+
return false;
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
exports.flags = flags;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.readConfigFile = void 0;
|
|
13
|
+
const async_1 = require("../../core/async");
|
|
14
|
+
const readConfigFile = (configFilePath) => __awaiter(void 0, void 0, void 0, function* () {
|
|
15
|
+
var _a;
|
|
16
|
+
try {
|
|
17
|
+
const file = yield (0, async_1.readFileAsync)(configFilePath);
|
|
18
|
+
return JSON.parse(file.toLocaleString());
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
throw new Error((_a = `Unable to read the config file at the location ${configFilePath}\n` +
|
|
22
|
+
error) !== null && _a !== void 0 ? _a : '');
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
exports.readConfigFile = readConfigFile;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/lib/cli/index.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
const minimist_1 = __importDefault(require("minimist"));
|
|
16
|
+
const default_1 = require("../core/default");
|
|
17
|
+
const printHelp_1 = require("./commands/printHelp");
|
|
18
|
+
const printMarkdown_1 = require("./commands/printMarkdown");
|
|
19
|
+
const printVersion_1 = require("./commands/printVersion");
|
|
20
|
+
const options_1 = require("./config/options");
|
|
21
|
+
const readConfigFile_1 = require("./config/readConfigFile");
|
|
22
|
+
const run = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
23
|
+
const args = (0, minimist_1.default)(process.argv.slice(2), options_1.flags);
|
|
24
|
+
if (args.version) {
|
|
25
|
+
(0, printVersion_1.printVersion)();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (args.help) {
|
|
29
|
+
(0, printHelp_1.printHelp)();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
let config = default_1.defaultConfig;
|
|
33
|
+
if (args.config) {
|
|
34
|
+
config = Object.assign(Object.assign({}, default_1.defaultConfig), (yield (0, readConfigFile_1.readConfigFile)(args.config)));
|
|
35
|
+
}
|
|
36
|
+
yield (0, printMarkdown_1.printMarkdown)(config);
|
|
37
|
+
});
|
|
38
|
+
run();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import { readFile, writeFile } from 'fs';
|
|
4
|
+
declare const execAsync: typeof exec.__promisify__;
|
|
5
|
+
declare const writeFileAsync: typeof writeFile.__promisify__;
|
|
6
|
+
declare const readFileAsync: typeof readFile.__promisify__;
|
|
7
|
+
declare const outputFileAsync: (path: string, contents: string) => Promise<void>;
|
|
8
|
+
export { execAsync, writeFileAsync, readFileAsync, outputFileAsync };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.outputFileAsync = exports.readFileAsync = exports.writeFileAsync = exports.execAsync = void 0;
|
|
13
|
+
const child_process_1 = require("child_process");
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
const util_1 = require("util");
|
|
17
|
+
const execAsync = (0, util_1.promisify)(child_process_1.exec);
|
|
18
|
+
exports.execAsync = execAsync;
|
|
19
|
+
const writeFileAsync = (0, util_1.promisify)(fs_1.writeFile);
|
|
20
|
+
exports.writeFileAsync = writeFileAsync;
|
|
21
|
+
const readFileAsync = (0, util_1.promisify)(fs_1.readFile);
|
|
22
|
+
exports.readFileAsync = readFileAsync;
|
|
23
|
+
const mkdirAsync = (0, util_1.promisify)(fs_1.mkdir);
|
|
24
|
+
const outputFileAsync = (path, contents) => __awaiter(void 0, void 0, void 0, function* () {
|
|
25
|
+
yield mkdirAsync((0, path_1.dirname)(path), { recursive: true });
|
|
26
|
+
yield writeFileAsync(path, contents);
|
|
27
|
+
});
|
|
28
|
+
exports.outputFileAsync = outputFileAsync;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultConfig = void 0;
|
|
4
|
+
const defaultConfig = {
|
|
5
|
+
outputFilename: 'CHANGELOG.md',
|
|
6
|
+
title: 'Changelog',
|
|
7
|
+
description: 'All notable changes to this project will be documented in this file. Dates are displayed in UTC.',
|
|
8
|
+
};
|
|
9
|
+
exports.defaultConfig = defaultConfig;
|
package/lib/core/git.js
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
12
|
+
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
13
|
+
var m = o[Symbol.asyncIterator], i;
|
|
14
|
+
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
15
|
+
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
16
|
+
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
17
|
+
};
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.groupCommitsByTags = void 0;
|
|
20
|
+
const async_1 = require("./async");
|
|
21
|
+
const getReleaseTags = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
22
|
+
const command = `git tag -l --sort=-creatordate --format="%(creatordate:short),%(refname:short)"`;
|
|
23
|
+
const result = yield (0, async_1.execAsync)(command);
|
|
24
|
+
return result.stdout
|
|
25
|
+
.split('\n')
|
|
26
|
+
.filter((tag) => !!tag)
|
|
27
|
+
.map((tag) => {
|
|
28
|
+
const value = tag.split(',');
|
|
29
|
+
return {
|
|
30
|
+
creatorDate: value[0],
|
|
31
|
+
tag: value[1],
|
|
32
|
+
};
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
const getReleaseCommits = (toTag, fromTag, blacklistPattern) => __awaiter(void 0, void 0, void 0, function* () {
|
|
36
|
+
const separator = '_||_';
|
|
37
|
+
const command = `git log ${fromTag}..${toTag} --no-merges --pretty=format:"%ad${separator}%h${separator}%ae${separator}%s"`;
|
|
38
|
+
const logResult = yield (0, async_1.execAsync)(command);
|
|
39
|
+
return logResult.stdout
|
|
40
|
+
.split('\n')
|
|
41
|
+
.filter((message) => !!message)
|
|
42
|
+
.map((message) => {
|
|
43
|
+
const data = message.split(separator);
|
|
44
|
+
return {
|
|
45
|
+
date: new Date(data[0]),
|
|
46
|
+
hash: data[1],
|
|
47
|
+
author: data[2],
|
|
48
|
+
subject: data[3],
|
|
49
|
+
};
|
|
50
|
+
})
|
|
51
|
+
.filter((commit) => {
|
|
52
|
+
if (!blacklistPattern) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
return !new RegExp(blacklistPattern, 'gm').test(commit.subject);
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
const groupCommitsByTags = (blacklistPattern) => __awaiter(void 0, void 0, void 0, function* () {
|
|
59
|
+
var e_1, _a;
|
|
60
|
+
const releaseCommits = [];
|
|
61
|
+
const headTag = {
|
|
62
|
+
creatorDate: new Date().toISOString().slice(0, 10),
|
|
63
|
+
tag: 'HEAD',
|
|
64
|
+
};
|
|
65
|
+
const allTags = [headTag, ...(yield getReleaseTags())];
|
|
66
|
+
try {
|
|
67
|
+
for (var _b = __asyncValues(allTags.slice(0, -1).entries()), _c; _c = yield _b.next(), !_c.done;) {
|
|
68
|
+
const [index, value] = _c.value;
|
|
69
|
+
const previousTag = allTags[index + 1].tag;
|
|
70
|
+
const commits = yield getReleaseCommits(value.tag, previousTag, blacklistPattern);
|
|
71
|
+
releaseCommits.push({
|
|
72
|
+
commits,
|
|
73
|
+
previousTag,
|
|
74
|
+
tag: value.tag,
|
|
75
|
+
date: new Date(value.creatorDate),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
80
|
+
finally {
|
|
81
|
+
try {
|
|
82
|
+
if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
|
|
83
|
+
}
|
|
84
|
+
finally { if (e_1) throw e_1.error; }
|
|
85
|
+
}
|
|
86
|
+
return releaseCommits;
|
|
87
|
+
});
|
|
88
|
+
exports.groupCommitsByTags = groupCommitsByTags;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Config } from '../models';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a changelog formatted in Markdown syntax
|
|
4
|
+
* @param config Config
|
|
5
|
+
* @returns Text in Markdown syntax
|
|
6
|
+
*/
|
|
7
|
+
declare const createMarkdown: (config?: Config | undefined) => Promise<string>;
|
|
8
|
+
export { createMarkdown };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.createMarkdown = void 0;
|
|
13
|
+
const default_1 = require("./default");
|
|
14
|
+
const git_1 = require("./git");
|
|
15
|
+
const createLink = (linkText, url) => {
|
|
16
|
+
return `[${linkText}](${url})`;
|
|
17
|
+
};
|
|
18
|
+
const formatDate = (date) => {
|
|
19
|
+
return date.toLocaleString('en', {
|
|
20
|
+
month: 'long',
|
|
21
|
+
day: 'numeric',
|
|
22
|
+
year: 'numeric',
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
const ticketRecognition = (commitMessage) => {
|
|
26
|
+
var _a, _b;
|
|
27
|
+
return (_b = (_a = commitMessage.match(/\((?<ticket>#\d+)\)/)) === null || _a === void 0 ? void 0 : _a.groups) === null || _b === void 0 ? void 0 : _b.ticket;
|
|
28
|
+
};
|
|
29
|
+
const createReleaseSubtitle = (group, repository) => {
|
|
30
|
+
const latestTag = 'v' + process.env.npm_package_version;
|
|
31
|
+
const latestVersion = latestTag === group.previousTag ? 'Unreleased changes' : latestTag;
|
|
32
|
+
const subtitleLinkText = group.tag === 'HEAD' ? latestVersion : group.tag;
|
|
33
|
+
const subtitleLink = `${repository}/compare/${group.previousTag}...${group.tag}`;
|
|
34
|
+
const subtitle = `${createLink(subtitleLinkText, subtitleLink)}`;
|
|
35
|
+
return `#### ${repository ? subtitle : subtitleLinkText}`;
|
|
36
|
+
};
|
|
37
|
+
const createDateInformation = (group) => {
|
|
38
|
+
return `> ${formatDate(group.date)}`;
|
|
39
|
+
};
|
|
40
|
+
const createCommitList = (group, repository) => {
|
|
41
|
+
return group.commits.reduce((result, commit) => {
|
|
42
|
+
const ticket = ticketRecognition(commit.subject);
|
|
43
|
+
let reference = '';
|
|
44
|
+
if (ticket) {
|
|
45
|
+
commit.subject = commit.subject.replace(`(${ticket})`, ''); // remove ticket number
|
|
46
|
+
const ticketLinkText = `\`${ticket}\``;
|
|
47
|
+
reference = repository
|
|
48
|
+
? createLink(ticketLinkText, `${repository}/pull/${ticket.slice(1)}`)
|
|
49
|
+
: ticketLinkText;
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const commitHashText = `\`${commit.hash}\``;
|
|
53
|
+
reference = repository
|
|
54
|
+
? createLink(commitHashText, `${repository}/commit/${commit.hash}`)
|
|
55
|
+
: commitHashText;
|
|
56
|
+
}
|
|
57
|
+
return (result +
|
|
58
|
+
`- ${commit.subject.replace(/[<<]/, '<').trim()} ${reference}\n`);
|
|
59
|
+
}, '');
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Returns a changelog formatted in Markdown syntax
|
|
63
|
+
* @param config Config
|
|
64
|
+
* @returns Text in Markdown syntax
|
|
65
|
+
*/
|
|
66
|
+
const createMarkdown = (config) => __awaiter(void 0, void 0, void 0, function* () {
|
|
67
|
+
var _a, _b;
|
|
68
|
+
const title = `### ${(_a = config === null || config === void 0 ? void 0 : config.title) !== null && _a !== void 0 ? _a : default_1.defaultConfig.title} \n\n ${(_b = config === null || config === void 0 ? void 0 : config.description) !== null && _b !== void 0 ? _b : default_1.defaultConfig.description}\n\n`;
|
|
69
|
+
const tagGroups = yield (0, git_1.groupCommitsByTags)(config === null || config === void 0 ? void 0 : config.blacklistPattern);
|
|
70
|
+
return (title +
|
|
71
|
+
tagGroups.reduce((markdown, group) => {
|
|
72
|
+
const subtitle = createReleaseSubtitle(group, config === null || config === void 0 ? void 0 : config.repositoryUrl);
|
|
73
|
+
const date = createDateInformation(group);
|
|
74
|
+
const commitList = createCommitList(group, config === null || config === void 0 ? void 0 : config.repositoryUrl);
|
|
75
|
+
return `${markdown} \n${subtitle} \n\n${date} \n\n${commitList}`;
|
|
76
|
+
}, ''));
|
|
77
|
+
});
|
|
78
|
+
exports.createMarkdown = createMarkdown;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mockNewDateTime = exports.createExecAsyncMock = void 0;
|
|
4
|
+
const jest_mock_1 = require("jest-mock");
|
|
5
|
+
const async_1 = require("./async");
|
|
6
|
+
const createExecAsyncMock = () => {
|
|
7
|
+
const mockExecAsync = (0, jest_mock_1.mocked)(async_1.execAsync);
|
|
8
|
+
return mockExecAsync;
|
|
9
|
+
};
|
|
10
|
+
exports.createExecAsyncMock = createExecAsyncMock;
|
|
11
|
+
const mockNewDateTime = () => {
|
|
12
|
+
jest.useFakeTimers().setSystemTime(new Date('2022-02-08').getTime());
|
|
13
|
+
};
|
|
14
|
+
exports.mockNewDateTime = mockNewDateTime;
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './core/markdown';
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./core/markdown"), exports);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface Config {
|
|
2
|
+
/**
|
|
3
|
+
* Name of the file in which the output should be written. Can also include a path name.
|
|
4
|
+
* @default `CHANGELOG.md`
|
|
5
|
+
* @optional
|
|
6
|
+
*/
|
|
7
|
+
filePath?: string;
|
|
8
|
+
/**
|
|
9
|
+
* Title of the page
|
|
10
|
+
* @default `Changelog`
|
|
11
|
+
* @optional
|
|
12
|
+
*/
|
|
13
|
+
title?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Description of the page
|
|
16
|
+
* @default `All notable changes to this project will be documented in this file. Dates are displayed in UTC.`
|
|
17
|
+
* @optional
|
|
18
|
+
*/
|
|
19
|
+
description?: string;
|
|
20
|
+
/**
|
|
21
|
+
* URL of the repository
|
|
22
|
+
* @optional If not set, no links will be provided.
|
|
23
|
+
*/
|
|
24
|
+
repositoryUrl?: string;
|
|
25
|
+
/**
|
|
26
|
+
* Regular expression pattern to exclude commit messages
|
|
27
|
+
* @optional
|
|
28
|
+
*/
|
|
29
|
+
blacklistPattern?: string;
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
|
5
|
+
}) : (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
o[k2] = m[k];
|
|
8
|
+
}));
|
|
9
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
10
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
11
|
+
};
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
__exportStar(require("./commit"), exports);
|
|
14
|
+
__exportStar(require("./config"), exports);
|
|
15
|
+
__exportStar(require("./tagGroup"), exports);
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "changelog-machine",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Changelog generator based on Git commit messages and tags",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"module": "./lib/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"changelog-machine": "./bin/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc -p tsconfig.json",
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"lint": "eslint -c .eslintrc.json --ext .ts ./src/**/*.ts",
|
|
14
|
+
"changelog": "ts-node src/cli/index.ts --config changelog.config.json",
|
|
15
|
+
"prepublish": "npm run build",
|
|
16
|
+
"version": "npm run changelog && git add CHANGELOG.md"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"lib/"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"Changelog",
|
|
23
|
+
"Generator"
|
|
24
|
+
],
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=4.2.0"
|
|
27
|
+
},
|
|
28
|
+
"author": "Philipp Kief",
|
|
29
|
+
"license": "MIT",
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/jest": "^27.4.0",
|
|
32
|
+
"@types/minimist": "^1.2.2",
|
|
33
|
+
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
|
34
|
+
"@typescript-eslint/parser": "^5.4.0",
|
|
35
|
+
"eslint": "^8.3.0",
|
|
36
|
+
"eslint-config-prettier": "^8.3.0",
|
|
37
|
+
"eslint-plugin-prettier": "^4.0.0",
|
|
38
|
+
"jest": "^27.5.0",
|
|
39
|
+
"ts-jest": "^27.1.3",
|
|
40
|
+
"ts-node": "^10.4.0",
|
|
41
|
+
"typescript": "^4.5.5"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"minimist": "^1.2.5"
|
|
45
|
+
}
|
|
46
|
+
}
|