@release-it/keep-a-changelog 4.0.0 → 6.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/.github/workflows/pkg.pr.new.yml +21 -0
- package/.github/workflows/test.yml +30 -0
- package/README.md +7 -1
- package/index.js +34 -8
- package/package.json +9 -7
- package/test.js +17 -26
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
name: Publish through pkr.pr.new
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- '**'
|
|
7
|
+
tags:
|
|
8
|
+
- '!**'
|
|
9
|
+
pull_request:
|
|
10
|
+
workflow_dispatch:
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
publish:
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-node@v4
|
|
19
|
+
with:
|
|
20
|
+
node-version: 20
|
|
21
|
+
- run: npx --yes pkg-pr-new publish --compact --comment=update
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
name: Cross-OS Tests
|
|
2
|
+
|
|
3
|
+
on: [push, pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
test:
|
|
7
|
+
strategy:
|
|
8
|
+
matrix:
|
|
9
|
+
os:
|
|
10
|
+
- ubuntu-latest
|
|
11
|
+
- windows-latest
|
|
12
|
+
- macos-latest
|
|
13
|
+
node:
|
|
14
|
+
- 18
|
|
15
|
+
- 20
|
|
16
|
+
|
|
17
|
+
runs-on: ${{ matrix.os }}
|
|
18
|
+
name: ${{ matrix.os }} (Node v${{ matrix.node }})
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v3
|
|
22
|
+
- uses: actions/setup-node@v3
|
|
23
|
+
with:
|
|
24
|
+
node-version: ${{ matrix.node }}
|
|
25
|
+
|
|
26
|
+
- name: Install dependencies
|
|
27
|
+
run: npm install
|
|
28
|
+
|
|
29
|
+
- name: Run tests
|
|
30
|
+
run: npm test
|
package/README.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
This [release-it plugin](https://github.com/release-it/release-it/blob/master/docs/plugins.md) maintains your
|
|
4
4
|
CHANGELOG.md file according to the [Keep A Changelog](https://keepachangelog.com/) standards.
|
|
5
5
|
|
|
6
|
+
It updates the changelog automatically by:
|
|
7
|
+
|
|
8
|
+
- replacing the `## [Unreleased]` header with the current version and release date.
|
|
9
|
+
- [optionally] adding a new `## [Unreleased]` header as preparation for future changes.
|
|
10
|
+
|
|
11
|
+
It does not automatically populate the changelog with commit messages from the git history!
|
|
12
|
+
|
|
6
13
|
The idea and initial implementation comes [from @eMarek](https://github.com/release-it/release-it/issues/662).
|
|
7
14
|
|
|
8
15
|
```
|
|
@@ -24,7 +31,6 @@ In [release-it](https://github.com/release-it/release-it) config:
|
|
|
24
31
|
| option | default value | description |
|
|
25
32
|
| ----------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
26
33
|
| filename | `'CHANGELOG.md'` | File with changelogs. |
|
|
27
|
-
| strictLatest | `true` | Entry of latest version must be present in order to get correct changelog. Set this option to `false` if you expect latest version without logs. |
|
|
28
34
|
| addUnreleased | `false` | It leaves "Unreleased" title row if set to `true`. |
|
|
29
35
|
| keepUnreleased | `false` | It leaves "Unreleased" title row unchanged if set to `true`. |
|
|
30
36
|
| addVersionUrl | `false` | Links the version to the according changeset. Uses GitHub-compatible URLs by default, see other options to configure the URL format. |
|
package/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import fs from 'fs';
|
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { detectNewline } from 'detect-newline';
|
|
5
5
|
import format from 'string-template';
|
|
6
|
+
import semver from "semver";
|
|
6
7
|
|
|
7
8
|
const pad = num => ('0' + num).slice(-2);
|
|
8
9
|
|
|
@@ -28,10 +29,9 @@ const defaultVersionUrlFormats = {
|
|
|
28
29
|
class KeepAChangelog extends Plugin {
|
|
29
30
|
async init() {
|
|
30
31
|
await super.init();
|
|
31
|
-
const { filename,
|
|
32
|
+
const { filename, addUnreleased, keepUnreleased, addVersionUrl, versionUrlFormats, head } = this.options;
|
|
32
33
|
|
|
33
34
|
this.filename = filename || 'CHANGELOG.md';
|
|
34
|
-
this.strictLatest = strictLatest === undefined ? true : Boolean(strictLatest);
|
|
35
35
|
this.addUnreleased = addUnreleased === undefined ? false : Boolean(addUnreleased);
|
|
36
36
|
this.keepUnreleased = keepUnreleased === undefined ? false : Boolean(keepUnreleased);
|
|
37
37
|
this.addVersionUrl = addVersionUrl === undefined ? false : Boolean(addVersionUrl);
|
|
@@ -56,15 +56,41 @@ class KeepAChangelog extends Plugin {
|
|
|
56
56
|
const { changelog } = this.getContext();
|
|
57
57
|
if (changelog) return changelog;
|
|
58
58
|
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
const unreleasedTitleRawList = Array.from(this.changelogContent.matchAll(/(?<=## \[)Unreleased(?=])/g)).map(ver => ver?.[0])
|
|
60
|
+
if(unreleasedTitleRawList.length > 1) {
|
|
61
|
+
throw new Error(`Too many "Unreleased" sections in ${this.filename}: ${unreleasedTitleRawList.length}.`)
|
|
62
|
+
}
|
|
63
|
+
const versionTitleRawList = Array.from(this.changelogContent.matchAll(/(?<=## \[)((\d+\.){2}\d+[^\]]*)/g)).map(ver => ver?.[0])
|
|
64
|
+
const badTitleRawList = versionTitleRawList.filter(ver => {
|
|
65
|
+
// invalid: ver > latestVersion
|
|
66
|
+
return !semver.valid(ver) || semver.gt(ver, latestVersion);
|
|
67
|
+
})
|
|
68
|
+
if(badTitleRawList.length > 0) {
|
|
69
|
+
throw new Error(`Invalid versions in ${this.filename}: ${badTitleRawList.join(', ')}. Current: ${latestVersion}.`)
|
|
70
|
+
}
|
|
71
|
+
const unorderedTitleRawList = versionTitleRawList.filter((ver, veri) => {
|
|
72
|
+
const previous = versionTitleRawList[veri - 1];
|
|
73
|
+
const next = versionTitleRawList[veri + 1];
|
|
74
|
+
|
|
75
|
+
if(previous) {
|
|
76
|
+
// bad order: ver > previous
|
|
77
|
+
return semver.gt(ver, previous)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if(next) {
|
|
81
|
+
// bad order: ver < next
|
|
82
|
+
return semver.lt(ver, next)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return false
|
|
86
|
+
})
|
|
87
|
+
if(unorderedTitleRawList.length > 0) {
|
|
88
|
+
throw new Error(`Invalid sections order in ${this.filename}: ${unorderedTitleRawList.join(', ')}.`)
|
|
64
89
|
}
|
|
65
90
|
|
|
66
91
|
const { isIncrement } = this.config;
|
|
67
|
-
|
|
92
|
+
// use unreleased if initial release
|
|
93
|
+
const titleToFind = isIncrement || versionTitleRawList.length === 0 ? this.unreleasedTitleRaw : latestVersion;
|
|
68
94
|
const changelogContent = this.getChangelogEntryContent(titleToFind);
|
|
69
95
|
|
|
70
96
|
this.setContext({ changelog: changelogContent });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@release-it/keep-a-changelog",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.0",
|
|
4
4
|
"description": "Keep-a-changelog plugin for release-it",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./index.js",
|
|
@@ -30,20 +30,22 @@
|
|
|
30
30
|
"url": "https://webpro.nl"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"detect-newline": "^4.0.
|
|
33
|
+
"detect-newline": "^4.0.1",
|
|
34
|
+
"semver": "^7.6.3",
|
|
34
35
|
"string-template": "^1.0.0"
|
|
35
36
|
},
|
|
36
37
|
"devDependencies": {
|
|
37
38
|
"bron": "^2.0.3",
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
39
|
+
"fs-monkey": "^1.0.6",
|
|
40
|
+
"memfs": "^4.15.3",
|
|
41
|
+
"release-it": "^18.0.0",
|
|
42
|
+
"sinon": "^19.0.2"
|
|
41
43
|
},
|
|
42
44
|
"peerDependencies": {
|
|
43
|
-
"release-it": "^
|
|
45
|
+
"release-it": "^18.0.0"
|
|
44
46
|
},
|
|
45
47
|
"engines": {
|
|
46
|
-
"node": ">=
|
|
48
|
+
"node": ">=18"
|
|
47
49
|
},
|
|
48
50
|
"release-it": {
|
|
49
51
|
"github": {
|
package/test.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { strict as assert } from 'assert';
|
|
1
|
+
import { strict as assert } from 'node:assert';
|
|
2
2
|
import test from 'bron';
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { vol } from 'memfs';
|
|
5
|
+
import { patchFs } from 'fs-monkey';
|
|
5
6
|
import { factory, runTasks } from 'release-it/test/util/index.js';
|
|
6
7
|
import sinon from 'sinon';
|
|
7
8
|
import Plugin from './index.js';
|
|
@@ -9,13 +10,13 @@ import Plugin from './index.js';
|
|
|
9
10
|
const initialDryRunFileContents =
|
|
10
11
|
'\n\n## [Unreleased]\n\n* Item A\n* Item B\n\n## [1.0.0] - 2020-05-02\n\n* Item C\n* Item D';
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
vol.fromJSON({
|
|
13
14
|
'./CHANGELOG-FOO.md': '## [FOO]\n\n* Item A\n* Item B',
|
|
14
|
-
'./CHANGELOG-
|
|
15
|
+
'./CHANGELOG-MULTIPLE_UNRELEASED.md': '## [Unreleased]\n* Item A\n\n## [Unreleased]* Item B\nB\n* Item C\n* Item D',
|
|
16
|
+
'./CHANGELOG-INVALID_ORDER.md': '## [Unreleased]\n\n## [0.2.0]\n* Item A\n\n## [0.3.0]* Item B\nB\n\n## [0.1.0]\n* Item C\n* Item D',
|
|
15
17
|
'./CHANGELOG-EMPTY.md': '## [Unreleased]\n\n\n\n## [1.0.0]\n\n* Item A\n* Item B',
|
|
16
18
|
'./CHANGELOG-FULL.md':
|
|
17
19
|
'# Changelog\n\n## [Unreleased]\n\n* Item A\n* Item B\n\n## [1.0.0] - 2020-05-02\n\n* Item C\n* Item D',
|
|
18
|
-
'./CHANGELOG-NO-STRICT.md': '## [Unreleased]\n\n* Item A\n* Item B\n\n## [1.0.0] - 2020-05-02\n\n* Item C\n* Item D',
|
|
19
20
|
'./CHANGELOG-DRYRUN.md': initialDryRunFileContents,
|
|
20
21
|
'./CHANGELOG-LESS_NEW_LINES.md': '## [Unreleased]\n* Item A\n* Item B\n## [1.0.0] - 2020-05-02\n* Item C\n* Item D',
|
|
21
22
|
'./CHANGELOG-EOL.md':
|
|
@@ -34,6 +35,8 @@ mock({
|
|
|
34
35
|
'./CHANGELOG-VERSION_URL_NEW.md': '## [Unreleased]\n\n* Item A\n* Item B\n'
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
patchFs(vol);
|
|
39
|
+
|
|
37
40
|
const readFile = file => fs.readFileSync(file).toString();
|
|
38
41
|
|
|
39
42
|
const namespace = 'keep-a-changelog';
|
|
@@ -62,10 +65,15 @@ test('should throw for missing "unreleased" section when no-increment flag is se
|
|
|
62
65
|
await assert.rejects(runTasks(plugin), /Missing "Unreleased" section in CHANGELOG-FOO.md/);
|
|
63
66
|
});
|
|
64
67
|
|
|
65
|
-
test('should throw for
|
|
66
|
-
const options = {
|
|
68
|
+
test('should throw for multiple "unreleased" sections', async t => {
|
|
69
|
+
const options = { [namespace]: { filename: 'CHANGELOG-MULTIPLE_UNRELEASED.md' } };
|
|
70
|
+
const plugin = factory(Plugin, { namespace, options });
|
|
71
|
+
await assert.rejects(runTasks(plugin), /Too many "Unreleased" sections in CHANGELOG-MULTIPLE_UNRELEASED.md: \d+./);
|
|
72
|
+
});
|
|
73
|
+
test('should throw for invalid order', async t => {
|
|
74
|
+
const options = { [namespace]: { filename: 'CHANGELOG-INVALID_ORDER.md' } };
|
|
67
75
|
const plugin = factory(Plugin, { namespace, options });
|
|
68
|
-
await assert.rejects(runTasks(plugin), /
|
|
76
|
+
await assert.rejects(runTasks(plugin), /Invalid sections order in CHANGELOG-INVALID_ORDER.md: 0.2.0, 0.3.0./);
|
|
69
77
|
});
|
|
70
78
|
|
|
71
79
|
test('should find "1.0.0" section when no-increment flag is set when items under version', async t => {
|
|
@@ -82,12 +90,6 @@ test('should find "1.0.0" section when no-increment flag is set when items under
|
|
|
82
90
|
assert.equal(plugin.getChangelog('1.0.0'), '* Item C\n* Item D');
|
|
83
91
|
});
|
|
84
92
|
|
|
85
|
-
test('should throw for missing section for previous release', async t => {
|
|
86
|
-
const options = { [namespace]: { filename: 'CHANGELOG-MISSING.md' } };
|
|
87
|
-
const plugin = factory(Plugin, { namespace, options });
|
|
88
|
-
await assert.rejects(runTasks(plugin), /Missing section for previous release \("1\.0\.0"\) in CHANGELOG-MISSING\.md/);
|
|
89
|
-
});
|
|
90
|
-
|
|
91
93
|
test('should find very first changelog with disabled strict latest option', async t => {
|
|
92
94
|
const options = { [namespace]: { filename: 'CHANGELOG-UNRELEASED.md', strictLatest: false } };
|
|
93
95
|
const plugin = factory(Plugin, { namespace, options });
|
|
@@ -106,17 +108,6 @@ test('should write changelog', async t => {
|
|
|
106
108
|
);
|
|
107
109
|
});
|
|
108
110
|
|
|
109
|
-
test('should write changelog even with `strictLatest: false`', async t => {
|
|
110
|
-
const options = { [namespace]: { filename: 'CHANGELOG-NO-STRICT.md', strictLatest: false } };
|
|
111
|
-
const plugin = factory(Plugin, { namespace, options });
|
|
112
|
-
await runTasks(plugin);
|
|
113
|
-
assert.equal(plugin.getChangelog(), '* Item A\n* Item B');
|
|
114
|
-
assert.match(
|
|
115
|
-
readFile('./CHANGELOG-NO-STRICT.md'),
|
|
116
|
-
/## \[1\.0\.1] - [0-9]{4}-[0-9]{2}-[0-9]{2}\n\n\* Item A\n\* Item B\n\n## \[1\.0\.0] - 2020-05-02\n\n\* Item C\n*\* Item D/
|
|
117
|
-
);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
111
|
test('should not write changelog in dry run', async t => {
|
|
121
112
|
const options = { 'dry-run': true, [namespace]: { filename: 'CHANGELOG-DRYRUN.md' } };
|
|
122
113
|
const plugin = factory(Plugin, { namespace, options });
|