calver-bump 0.1.8 → 1.0.1
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/README.md +32 -6
- package/bin/calver-bump.js +64 -16
- package/package.json +1 -1
- package/src/changelog.js +26 -15
- package/src/files.js +4 -4
- package/src/index.js +33 -18
package/README.md
CHANGED
|
@@ -2,16 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Release CLI for applications and internal tools that use readable CalVer versions.
|
|
4
4
|
|
|
5
|
-
Default version
|
|
5
|
+
Default version format:
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
8
|
YY.MMDD
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Default git tag format:
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
vYY.MMDD
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Example package version and git tag:
|
|
12
18
|
|
|
13
19
|
```text
|
|
14
20
|
26.0529
|
|
21
|
+
v26.0529
|
|
15
22
|
```
|
|
16
23
|
|
|
17
24
|
## What it does
|
|
@@ -29,6 +36,8 @@ Example:
|
|
|
29
36
|
npx calver-bump
|
|
30
37
|
```
|
|
31
38
|
|
|
39
|
+
By default, `calver-bump` updates release files, creates a release commit, and creates an annotated `v`-prefixed git tag.
|
|
40
|
+
|
|
32
41
|
Preview the planned release without writing files:
|
|
33
42
|
|
|
34
43
|
```bash
|
|
@@ -59,10 +68,10 @@ Use long CalVer instead:
|
|
|
59
68
|
npx calver-bump --format long
|
|
60
69
|
```
|
|
61
70
|
|
|
62
|
-
Create
|
|
71
|
+
Create an unprefixed git tag while keeping the default package version unchanged:
|
|
63
72
|
|
|
64
73
|
```bash
|
|
65
|
-
npx calver-bump --tag-prefix
|
|
74
|
+
npx calver-bump --tag-prefix ""
|
|
66
75
|
```
|
|
67
76
|
|
|
68
77
|
Push the release commit and annotated tag:
|
|
@@ -107,7 +116,19 @@ Update only `CHANGELOG.md`:
|
|
|
107
116
|
npx calver-bump --changelog-only
|
|
108
117
|
```
|
|
109
118
|
|
|
110
|
-
|
|
119
|
+
Create a release commit after updating files:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
npx calver-bump --commit
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Create a release commit and annotated tag after updating files:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
npx calver-bump --tag
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Explicitly update files without creating a release commit or tag:
|
|
111
132
|
|
|
112
133
|
```bash
|
|
113
134
|
npx calver-bump --skip-commit
|
|
@@ -152,7 +173,12 @@ The package includes a manual GitHub Actions publish workflow. Run the `Publish`
|
|
|
152
173
|
- Changelog entries fall back to commit hash links for GitHub and GitLab-style remotes when no pull/merge request reference is found.
|
|
153
174
|
- Release entries include a `Full Changelog` section with a deduped list of pull/merge requests found in the release range, including the local commit title when available.
|
|
154
175
|
- Later releases prepend only commits since the previous nearest reachable tag.
|
|
176
|
+
- Plain `calver-bump` creates the release commit and annotated tag by default.
|
|
177
|
+
- Use `--commit` to create only the release commit.
|
|
178
|
+
- Use `--tag` to create the release commit and annotated tag.
|
|
179
|
+
- Use `--skip-commit` to update files only without creating a release commit or tag.
|
|
180
|
+
- Use `--push` to create the release commit, create the annotated tag, and push both.
|
|
155
181
|
- Release tags are annotated so `git push --follow-tags <remote> <branch>` pushes them.
|
|
156
|
-
-
|
|
182
|
+
- Unrelated dirty working-tree files do not block a release; only release files are staged.
|
|
157
183
|
- Existing release tags are rejected before files are written.
|
|
158
184
|
- If tag creation fails after the release commit, the CLI undoes its own commit and leaves the file changes in the working tree for inspection or recovery.
|
package/bin/calver-bump.js
CHANGED
|
@@ -10,6 +10,7 @@ const execFile = promisify(execFileCallback);
|
|
|
10
10
|
const args = process.argv.slice(2);
|
|
11
11
|
const KNOWN_OPTIONS = new Set([
|
|
12
12
|
'--changelog-only',
|
|
13
|
+
'--commit',
|
|
13
14
|
'--dry-run',
|
|
14
15
|
'--format',
|
|
15
16
|
'--from',
|
|
@@ -18,6 +19,7 @@ const KNOWN_OPTIONS = new Set([
|
|
|
18
19
|
'--push',
|
|
19
20
|
'--remote',
|
|
20
21
|
'--skip-commit',
|
|
22
|
+
'--tag',
|
|
21
23
|
'--tag-prefix',
|
|
22
24
|
'--types',
|
|
23
25
|
'--version',
|
|
@@ -49,19 +51,17 @@ try {
|
|
|
49
51
|
for (const action of result.actions) {
|
|
50
52
|
console.log(`- ${action}`);
|
|
51
53
|
}
|
|
52
|
-
if (options.push && !options.dryRun &&
|
|
54
|
+
if (options.push && !options.dryRun && createsTag(options)) {
|
|
53
55
|
const pushArgs = ['push', '--follow-tags', result.remote, result.branch];
|
|
54
56
|
console.log('');
|
|
55
57
|
console.log(`Running: git ${pushArgs.join(' ')}`);
|
|
56
58
|
await execFile('git', pushArgs, { encoding: 'utf8' });
|
|
57
|
-
} else if (!options.dryRun
|
|
59
|
+
} else if (!options.dryRun) {
|
|
58
60
|
console.log('');
|
|
59
61
|
console.log('Next steps:');
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
console.log(` git push --follow-tags ${result.remote} ${result.branch}`);
|
|
64
|
-
console.log('3. Trigger or verify your deployment pipeline.');
|
|
62
|
+
for (const instruction of nextStepInstructions(result, options)) {
|
|
63
|
+
console.log(instruction);
|
|
64
|
+
}
|
|
65
65
|
}
|
|
66
66
|
} catch (error) {
|
|
67
67
|
console.error(error.message);
|
|
@@ -73,21 +73,33 @@ async function releaseOptions(args) {
|
|
|
73
73
|
const config = await readConfig();
|
|
74
74
|
const versionOnly = flag(args, '--version-only') ?? config.versionOnly ?? false;
|
|
75
75
|
const changelogOnly = flag(args, '--changelog-only') ?? config.changelogOnly ?? false;
|
|
76
|
+
const skipCommit = flag(args, '--skip-commit') ?? config.skipCommit ?? false;
|
|
77
|
+
const push = flag(args, '--push') ?? config.push ?? false;
|
|
78
|
+
const commitFlag = flag(args, '--commit');
|
|
79
|
+
const tagFlag = flag(args, '--tag');
|
|
80
|
+
const writesOnly = skipCommit || versionOnly || changelogOnly;
|
|
81
|
+
const tag = !writesOnly && (push || (tagFlag ?? config.tag ?? (commitFlag || config.commit ? false : true)));
|
|
82
|
+
const commit = tag || (!writesOnly && (commitFlag ?? config.commit ?? false));
|
|
76
83
|
if (versionOnly && changelogOnly) {
|
|
77
84
|
throw new Error('--version-only cannot be combined with --changelog-only.');
|
|
78
85
|
}
|
|
86
|
+
if (skipCommit && (commitFlag || tagFlag || config.commit || config.tag || push)) {
|
|
87
|
+
throw new Error('--skip-commit cannot be combined with --commit, --tag, or --push.');
|
|
88
|
+
}
|
|
79
89
|
return {
|
|
80
90
|
...config,
|
|
81
91
|
dryRun: flag(args, '--dry-run') ?? config.dryRun ?? false,
|
|
82
92
|
noFetch: flag(args, '--no-fetch') ?? config.noFetch ?? false,
|
|
83
|
-
|
|
84
|
-
|
|
93
|
+
commit,
|
|
94
|
+
tag,
|
|
95
|
+
push,
|
|
96
|
+
skipCommit,
|
|
85
97
|
versionOnly,
|
|
86
98
|
changelogOnly,
|
|
87
99
|
from: value(args, '--from') ?? config.from,
|
|
88
100
|
format: value(args, '--format') ?? config.format ?? 'short',
|
|
89
101
|
remote: value(args, '--remote') ?? config.remote ?? 'origin',
|
|
90
|
-
tagPrefix: value(args, '--tag-prefix') ?? config.tagPrefix ?? '',
|
|
102
|
+
tagPrefix: value(args, '--tag-prefix') ?? config.tagPrefix ?? 'v',
|
|
91
103
|
types: parseTypes(value(args, '--types')) ?? config.types,
|
|
92
104
|
changelogSections: config.changelogSections,
|
|
93
105
|
};
|
|
@@ -134,7 +146,7 @@ function rejectUnknownOptions(args) {
|
|
|
134
146
|
|
|
135
147
|
function printResult(result, options) {
|
|
136
148
|
console.log(`Release version: ${result.version}`);
|
|
137
|
-
console.log(`Git tag: ${
|
|
149
|
+
console.log(`Git tag: ${result.createdTag ?? '(not created)'}`);
|
|
138
150
|
console.log(`Branch: ${result.branch}`);
|
|
139
151
|
console.log(`Remote: ${result.remote}`);
|
|
140
152
|
if (!options.versionOnly) {
|
|
@@ -151,8 +163,42 @@ function printResult(result, options) {
|
|
|
151
163
|
console.log(options.dryRun ? 'Planned actions:' : 'Completed actions:');
|
|
152
164
|
}
|
|
153
165
|
|
|
154
|
-
function
|
|
155
|
-
return !options.skipCommit && !options.versionOnly && !options.changelogOnly;
|
|
166
|
+
function createsCommit(options) {
|
|
167
|
+
return Boolean(options.commit || options.tag || options.push) && !options.skipCommit && !options.versionOnly && !options.changelogOnly;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function createsTag(options) {
|
|
171
|
+
return Boolean(options.tag || options.push) && createsCommit(options);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function nextStepInstructions(result, options) {
|
|
175
|
+
if (options.versionOnly || options.changelogOnly) {
|
|
176
|
+
return ['Review the generated file changes.'];
|
|
177
|
+
}
|
|
178
|
+
if (!createsCommit(options)) {
|
|
179
|
+
return [
|
|
180
|
+
'Review CHANGELOG.md, then run:',
|
|
181
|
+
`git add ${result.files.join(' ')}`,
|
|
182
|
+
`git commit -m "chore(release): ${result.version}"`,
|
|
183
|
+
`git tag -a ${result.tag} -m "Release ${result.version}"`,
|
|
184
|
+
`git push --follow-tags ${result.remote} ${result.branch}`,
|
|
185
|
+
];
|
|
186
|
+
}
|
|
187
|
+
if (!createsTag(options)) {
|
|
188
|
+
return [
|
|
189
|
+
'Review the release commit, then run:',
|
|
190
|
+
'git show --stat HEAD',
|
|
191
|
+
`git tag -a ${result.tag} -m "Release ${result.version}"`,
|
|
192
|
+
`git push --follow-tags ${result.remote} ${result.branch}`,
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
return [
|
|
196
|
+
'Review the release commit:',
|
|
197
|
+
'git show --stat HEAD',
|
|
198
|
+
'Push the release commit and tag:',
|
|
199
|
+
`git push --follow-tags ${result.remote} ${result.branch}`,
|
|
200
|
+
'Trigger or verify your deployment pipeline.',
|
|
201
|
+
];
|
|
156
202
|
}
|
|
157
203
|
|
|
158
204
|
function helpText() {
|
|
@@ -161,14 +207,16 @@ function helpText() {
|
|
|
161
207
|
Options:
|
|
162
208
|
--dry-run Preview the release without writing files.
|
|
163
209
|
--format <name> Version format: short, compact, or long.
|
|
164
|
-
--tag-prefix <prefix> Prefix the git tag without changing package.json.
|
|
210
|
+
--tag-prefix <prefix> Prefix the git tag without changing package.json (default: v).
|
|
165
211
|
--types <list> Comma-separated conventional commit types to include.
|
|
166
212
|
--from <tag> Use an explicit changelog base tag.
|
|
167
213
|
--no-fetch Use local tags only; do not fetch remote tags.
|
|
168
214
|
--version-only Update package.json and supported npm lockfiles only.
|
|
169
215
|
--changelog-only Update CHANGELOG.md only.
|
|
170
|
-
--
|
|
171
|
-
--
|
|
216
|
+
--commit Create the release commit after updating files.
|
|
217
|
+
--tag Create the release commit and annotated tag.
|
|
218
|
+
--skip-commit Explicitly update files without creating a release commit or tag.
|
|
219
|
+
--push Create, tag, and push the release.
|
|
172
220
|
--remote <name> Remote used for fetch, links, and push.
|
|
173
221
|
--version Print the calver-bump package version.
|
|
174
222
|
--help Show this help.
|
package/package.json
CHANGED
package/src/changelog.js
CHANGED
|
@@ -21,6 +21,7 @@ export async function releaseNotes(cwd, options = {}) {
|
|
|
21
21
|
const conventionalCommits = dedupeConventionalChanges(commits
|
|
22
22
|
.map((commit) => ({ ...commit, subject: conventionalSubjectForCommit(commit) ?? commit.subject }))
|
|
23
23
|
.filter((commit) => isConventionalCommit(commit.subject))
|
|
24
|
+
.filter((commit) => !isReleaseCommit(commit.subject))
|
|
24
25
|
.filter((commit) => allowedTypes.includes(conventionalType(commit.subject)))
|
|
25
26
|
.filter((commit) => !isCommitInChangelog(commit, options.existingChangelog ?? ''))
|
|
26
27
|
.map((commit) => ({
|
|
@@ -28,7 +29,10 @@ export async function releaseNotes(cwd, options = {}) {
|
|
|
28
29
|
request: requestForCommit(commit, requestUrlBuilder),
|
|
29
30
|
url: commitUrlBuilder ? commitUrlBuilder(commit.hash) : null,
|
|
30
31
|
})));
|
|
31
|
-
const requests = uniqueRequests(commits
|
|
32
|
+
const requests = uniqueRequests(commits
|
|
33
|
+
.filter((commit) => !isReleaseCommit(conventionalSubjectForCommit(commit) ?? commit.subject))
|
|
34
|
+
.map((commit) => requestForCommit(commit, requestUrlBuilder))
|
|
35
|
+
.filter(Boolean));
|
|
32
36
|
return {
|
|
33
37
|
previousTag: latestTag,
|
|
34
38
|
range: latestTag ? `${latestTag}..HEAD` : 'HEAD',
|
|
@@ -39,10 +43,7 @@ export async function releaseNotes(cwd, options = {}) {
|
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
export function formatReleaseHeading({ version, previousTag, tag, compareUrlBuilder }) {
|
|
42
|
-
|
|
43
|
-
? `[${version}](${compareUrlBuilder(previousTag, tag)})`
|
|
44
|
-
: version;
|
|
45
|
-
return `## ${label}`;
|
|
46
|
+
return `## ${version}`;
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export function formatReleaseNotes(changes, sectionConfig = {}) {
|
|
@@ -72,11 +73,15 @@ export function formatReleaseNotes(changes, sectionConfig = {}) {
|
|
|
72
73
|
.join('\n\n');
|
|
73
74
|
}
|
|
74
75
|
|
|
75
|
-
export function formatFullChangelog(requests) {
|
|
76
|
-
|
|
76
|
+
export function formatFullChangelog(requests, compareUrl = null) {
|
|
77
|
+
const entries = [
|
|
78
|
+
...requests.map((request) => formatRequestEntry(request)),
|
|
79
|
+
...(compareUrl ? [`[Compare changes](${compareUrl})`] : []),
|
|
80
|
+
];
|
|
81
|
+
if (entries.length === 0) {
|
|
77
82
|
return '';
|
|
78
83
|
}
|
|
79
|
-
return `\n\n### Full Changelog\n\n${
|
|
84
|
+
return `\n\n### Full Changelog\n\n${entries.map((entry) => `- ${entry}`).join('\n')}`;
|
|
80
85
|
}
|
|
81
86
|
|
|
82
87
|
async function latestReleaseTag(cwd, changelog) {
|
|
@@ -88,7 +93,7 @@ async function latestReleaseTag(cwd, changelog) {
|
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
function latestChangelogCompareTarget(changelog) {
|
|
91
|
-
const match =
|
|
96
|
+
const match = /\[[^\]]+\]\([^)]*\/(?:-\/)?compare\/[^)]*?\.{3}(?<tag>[^)\s]+)\)/m.exec(changelog);
|
|
92
97
|
return match?.groups.tag ?? null;
|
|
93
98
|
}
|
|
94
99
|
|
|
@@ -101,6 +106,10 @@ function isConventionalCommit(subject) {
|
|
|
101
106
|
return /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([^)]+\))?!?: .+/.test(subject);
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
function isReleaseCommit(subject) {
|
|
110
|
+
return /^chore\(release\):\s.+/.test(subject);
|
|
111
|
+
}
|
|
112
|
+
|
|
104
113
|
function conventionalSubjectForCommit(commit) {
|
|
105
114
|
return commitLines(commit).find((line) => isConventionalCommit(line)) ?? null;
|
|
106
115
|
}
|
|
@@ -132,14 +141,16 @@ function conventionalType(subject) {
|
|
|
132
141
|
|
|
133
142
|
function formatCommitEntry(commit) {
|
|
134
143
|
const shortHash = commit.hash.slice(0, 7);
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
144
|
+
const links = [
|
|
145
|
+
shortHash,
|
|
146
|
+
commit.request ? formatRequestLink(commit.request) : null,
|
|
147
|
+
].filter(Boolean);
|
|
148
|
+
const suffix = ` (${links.join(', ')})`;
|
|
138
149
|
return `${commit.subject}${suffix}`;
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
function formatRequestLink(request) {
|
|
142
|
-
return request.
|
|
153
|
+
return request.label;
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
function formatRequestEntry(request) {
|
|
@@ -220,7 +231,7 @@ function requestTitleForCommit(commit) {
|
|
|
220
231
|
}
|
|
221
232
|
|
|
222
233
|
function parseRequestReference(message) {
|
|
223
|
-
const gitlabMerge = /(
|
|
234
|
+
const gitlabMerge = /(?:^|[\s(])(?:See merge request\s+\S+!|!)(?<number>\d+)(?=\D|$)/i.exec(message);
|
|
224
235
|
if (gitlabMerge) {
|
|
225
236
|
return { provider: 'gitlab', number: gitlabMerge.groups.number, label: `!${gitlabMerge.groups.number}` };
|
|
226
237
|
}
|
|
@@ -257,7 +268,7 @@ function parseGitRemote(remote) {
|
|
|
257
268
|
return sshMatch.groups;
|
|
258
269
|
}
|
|
259
270
|
|
|
260
|
-
const httpsMatch = /^https
|
|
271
|
+
const httpsMatch = /^https?:\/\/(?<host>[^/]+)\/(?<repo>.+?)(?:\.git)?$/.exec(remote);
|
|
261
272
|
if (httpsMatch) {
|
|
262
273
|
return httpsMatch.groups;
|
|
263
274
|
}
|
package/src/files.js
CHANGED
|
@@ -32,18 +32,18 @@ export async function updatePackageLock(cwd, version) {
|
|
|
32
32
|
|
|
33
33
|
export async function releaseFiles(cwd, options = {}) {
|
|
34
34
|
const candidates = [];
|
|
35
|
+
const files = [];
|
|
35
36
|
if (options.version !== false) {
|
|
36
37
|
candidates.push('package.json', 'package-lock.json', 'npm-shrinkwrap.json');
|
|
37
38
|
}
|
|
38
|
-
if (options.changelog !== false) {
|
|
39
|
-
candidates.push('CHANGELOG.md');
|
|
40
|
-
}
|
|
41
|
-
const files = [];
|
|
42
39
|
for (const candidate of candidates) {
|
|
43
40
|
if (await fileExists(path.join(cwd, candidate))) {
|
|
44
41
|
files.push(candidate);
|
|
45
42
|
}
|
|
46
43
|
}
|
|
44
|
+
if (options.changelog !== false) {
|
|
45
|
+
files.push('CHANGELOG.md');
|
|
46
|
+
}
|
|
47
47
|
return files;
|
|
48
48
|
}
|
|
49
49
|
|
package/src/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
updatePackageVersion,
|
|
9
9
|
writeChangelog,
|
|
10
10
|
} from './files.js';
|
|
11
|
-
import {
|
|
11
|
+
import { assertTagAvailable, currentBranch, git, gitLines } from './git.js';
|
|
12
12
|
|
|
13
13
|
export async function planRelease(options = {}) {
|
|
14
14
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -18,7 +18,7 @@ export async function planRelease(options = {}) {
|
|
|
18
18
|
existingTags,
|
|
19
19
|
format: options.format ?? 'short',
|
|
20
20
|
});
|
|
21
|
-
const tag = `${options.tagPrefix ?? ''}${version}`;
|
|
21
|
+
const tag = `${options.tagPrefix ?? 'v'}${version}`;
|
|
22
22
|
const notes = options.versionOnly
|
|
23
23
|
? null
|
|
24
24
|
: await releaseNotes(cwd, { ...options, tag, existingChangelog: await readChangelog(cwd) });
|
|
@@ -42,7 +42,8 @@ export async function planRelease(options = {}) {
|
|
|
42
42
|
actions: releaseActions({
|
|
43
43
|
version,
|
|
44
44
|
tag,
|
|
45
|
-
|
|
45
|
+
commit: createsCommit(options),
|
|
46
|
+
tagRelease: createsTag(options),
|
|
46
47
|
versionOnly: options.versionOnly,
|
|
47
48
|
changelogOnly: options.changelogOnly,
|
|
48
49
|
files,
|
|
@@ -58,8 +59,7 @@ export async function runRelease(options = {}) {
|
|
|
58
59
|
return plan;
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
if (!options.skipCommit && !options.versionOnly && !options.changelogOnly) {
|
|
62
|
+
if (createsTag(options)) {
|
|
63
63
|
await assertTagAvailable(cwd, plan.tag);
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -71,23 +71,25 @@ export async function runRelease(options = {}) {
|
|
|
71
71
|
await prependChangelog(cwd, plan.version, options);
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
if (options
|
|
75
|
-
return { ...plan,
|
|
74
|
+
if (!createsCommit(options)) {
|
|
75
|
+
return { ...plan, createdTag: null };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
await git(cwd, ['add', ...await releaseFiles(cwd)]);
|
|
79
79
|
await git(cwd, ['commit', '-m', `chore(release): ${plan.version}`]);
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
80
|
+
if (createsTag(options)) {
|
|
81
|
+
try {
|
|
82
|
+
await git(cwd, ['tag', '-a', plan.tag, '-m', `Release ${plan.version}`]);
|
|
83
|
+
} catch (error) {
|
|
84
|
+
await git(cwd, ['reset', '--soft', 'HEAD~1']);
|
|
85
|
+
throw new Error(`Failed to create git tag ${plan.tag}; release commit was undone and file changes were left in the working tree. ${error.message}`);
|
|
86
|
+
}
|
|
85
87
|
}
|
|
86
88
|
|
|
87
|
-
return plan;
|
|
89
|
+
return createsTag(options) ? { ...plan, createdTag: plan.tag } : { ...plan, createdTag: null };
|
|
88
90
|
}
|
|
89
91
|
|
|
90
|
-
function releaseActions({ version, tag,
|
|
92
|
+
function releaseActions({ version, tag, commit = false, tagRelease = false, versionOnly = false, changelogOnly = false, files = [] }) {
|
|
91
93
|
const actions = [];
|
|
92
94
|
if (!changelogOnly) {
|
|
93
95
|
actions.push(`update package.json version to ${version}`);
|
|
@@ -98,28 +100,41 @@ function releaseActions({ version, tag, skipCommit = false, versionOnly = false,
|
|
|
98
100
|
if (!versionOnly) {
|
|
99
101
|
actions.push(`prepend CHANGELOG.md entry for ${version}`);
|
|
100
102
|
}
|
|
101
|
-
if (
|
|
103
|
+
if (commit) {
|
|
102
104
|
actions.push(`create git commit chore(release): ${version}`);
|
|
105
|
+
}
|
|
106
|
+
if (tagRelease) {
|
|
103
107
|
actions.push(`create git tag ${tag}`);
|
|
104
108
|
}
|
|
105
109
|
return actions;
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
function createsCommit(options) {
|
|
113
|
+
return Boolean(options.commit || options.tag || options.push) && !options.skipCommit && !options.versionOnly && !options.changelogOnly;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function createsTag(options) {
|
|
117
|
+
return Boolean(options.tag || options.push) && createsCommit(options);
|
|
118
|
+
}
|
|
119
|
+
|
|
108
120
|
async function prependChangelog(cwd, version, options = {}) {
|
|
109
121
|
const existing = await readChangelog(cwd);
|
|
110
122
|
|
|
111
123
|
const notes = await releaseNotes(cwd, {
|
|
112
124
|
...options,
|
|
113
125
|
existingChangelog: existing,
|
|
114
|
-
tag: `${options.tagPrefix ?? ''}${version}`,
|
|
126
|
+
tag: `${options.tagPrefix ?? 'v'}${version}`,
|
|
115
127
|
});
|
|
116
128
|
const heading = formatReleaseHeading({
|
|
117
129
|
version,
|
|
118
130
|
previousTag: notes.previousTag,
|
|
119
|
-
tag: options.tagPrefix
|
|
131
|
+
tag: `${options.tagPrefix ?? 'v'}${version}`,
|
|
120
132
|
compareUrlBuilder: notes.compareUrlBuilder,
|
|
121
133
|
});
|
|
122
|
-
const
|
|
134
|
+
const compareUrl = notes.previousTag && notes.compareUrlBuilder
|
|
135
|
+
? notes.compareUrlBuilder(notes.previousTag, `${options.tagPrefix ?? 'v'}${version}`)
|
|
136
|
+
: null;
|
|
137
|
+
const entry = `${heading}\n\n${formatReleaseNotes(notes.changes, options.changelogSections)}${formatFullChangelog(notes.requests, compareUrl)}\n`;
|
|
123
138
|
|
|
124
139
|
const body = existing.trim().startsWith('# Changelog')
|
|
125
140
|
? existing.replace(/^# Changelog\s*/, `# Changelog\n\n${entry}\n`)
|