@williamthorsen/release-kit 10.9.0 → 12.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/.meta/changelog.json +119 -2
- package/.readyup/kits/default.js +1 -17
- package/.readyup/kits/npm-auto-publish.js +42 -22
- package/.readyup/manifest.json +7 -7
- package/CHANGELOG.md +88 -0
- package/README.md +32 -32
- package/dist/esm/attachChangelogDiagnostics.d.ts +4 -0
- package/dist/esm/attachChangelogDiagnostics.js +11 -0
- package/dist/esm/bin/release-kit.js +34 -25
- package/dist/esm/buildChangelogEntries.d.ts +59 -17
- package/dist/esm/buildChangelogEntries.js +195 -111
- package/dist/esm/buildReleaseSummary.js +2 -2
- package/dist/esm/changelogOverrides.d.ts +31 -13
- package/dist/esm/changelogOverrides.js +138 -58
- package/dist/esm/classifyChangelogCommit.d.ts +38 -0
- package/dist/esm/classifyChangelogCommit.js +33 -0
- package/dist/esm/configFlagSchema.d.ts +12 -0
- package/dist/esm/configFlagSchema.js +3 -0
- package/dist/esm/createGithubReleaseCommand.js +6 -2
- package/dist/esm/decideRelease.d.ts +11 -37
- package/dist/esm/decideRelease.js +5 -27
- package/dist/esm/defaults.d.ts +1 -1
- package/dist/esm/defaults.js +3 -0
- package/dist/esm/determineBumpType.d.ts +11 -11
- package/dist/esm/determineBumpType.js +6 -7
- package/dist/esm/discoverWorkspaces.d.ts +53 -8
- package/dist/esm/discoverWorkspaces.js +57 -37
- package/dist/esm/enumerateReleaseWindows.d.ts +50 -0
- package/dist/esm/enumerateReleaseWindows.js +124 -0
- package/dist/esm/generateChangelogs.d.ts +5 -23
- package/dist/esm/generateChangelogs.js +0 -14
- package/dist/esm/glyphs.d.ts +1 -1
- package/dist/esm/help/prepareHelp.d.ts +1 -1
- package/dist/esm/help/prepareHelp.js +5 -6
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/init/initCommand.js +1 -1
- package/dist/esm/init/scaffold.d.ts +0 -2
- package/dist/esm/init/scaffold.js +2 -30
- package/dist/esm/loadConfig.d.ts +8 -5
- package/dist/esm/loadConfig.js +7 -7
- package/dist/esm/loadValidatedConfig.d.ts +46 -5
- package/dist/esm/loadValidatedConfig.js +32 -15
- package/dist/esm/parseChangeRecordBlock.d.ts +37 -0
- package/dist/esm/parseChangeRecordBlock.js +134 -0
- package/dist/esm/parseCommitMessage.d.ts +31 -6
- package/dist/esm/parseCommitMessage.js +6 -5
- package/dist/esm/prepareCommand.d.ts +5 -0
- package/dist/esm/prepareCommand.js +30 -47
- package/dist/esm/previewTagPrefixes.d.ts +9 -6
- package/dist/esm/previewTagPrefixes.js +12 -24
- package/dist/esm/publishCommand.d.ts +2 -2
- package/dist/esm/publishCommand.js +8 -2
- package/dist/esm/pushCommand.d.ts +1 -1
- package/dist/esm/pushCommand.js +2 -2
- package/dist/esm/releasePrepare.d.ts +7 -8
- package/dist/esm/releasePrepare.js +53 -73
- package/dist/esm/releasePrepareMono.js +48 -85
- package/dist/esm/releasePrepareProject.d.ts +1 -1
- package/dist/esm/releasePrepareProject.js +19 -37
- package/dist/esm/reportPrepare.js +45 -30
- package/dist/esm/resolveCommandTags.d.ts +3 -2
- package/dist/esm/resolveCommandTags.js +10 -6
- package/dist/esm/resolveReleaseNotesConfig.d.ts +9 -11
- package/dist/esm/resolveReleaseNotesConfig.js +9 -28
- package/dist/esm/showTagPrefixesCommand.d.ts +4 -2
- package/dist/esm/showTagPrefixesCommand.js +17 -3
- package/dist/esm/sync-labels/generateCommand.d.ts +16 -8
- package/dist/esm/sync-labels/generateCommand.js +12 -11
- package/dist/esm/sync-labels/initCommand.d.ts +9 -4
- package/dist/esm/sync-labels/initCommand.js +21 -15
- package/dist/esm/sync-labels/retiredConfig.d.ts +4 -2
- package/dist/esm/sync-labels/retiredConfig.js +2 -2
- package/dist/esm/types.d.ts +99 -55
- package/dist/esm/types.js +1 -1
- package/dist/esm/validateConfig.js +4 -0
- package/dist/esm/validateOverridesCommand.d.ts +16 -9
- package/dist/esm/validateOverridesCommand.js +39 -54
- package/docs/changelogs.md +64 -25
- package/docs/configuration.md +14 -3
- package/docs/editorial-overrides.md +75 -16
- package/docs/labels.md +2 -0
- package/docs/project-releases.md +4 -4
- package/docs/releasing.md +13 -8
- package/docs/work-types.md +13 -9
- package/package.json +5 -7
- package/schemas/changelog-overrides.json +39 -0
- package/cliff.toml.template +0 -65
- package/dist/esm/collectPolicyViolations.d.ts +0 -15
- package/dist/esm/collectPolicyViolations.js +0 -12
- package/dist/esm/determineBumpFromCommits.d.ts +0 -17
- package/dist/esm/determineBumpFromCommits.js +0 -28
- package/dist/esm/getCommitsSinceTarget.d.ts +0 -21
- package/dist/esm/getCommitsSinceTarget.js +0 -66
- package/dist/esm/resolveCliffConfigPath.d.ts +0 -11
- package/dist/esm/resolveCliffConfigPath.js +0 -20
- package/dist/esm/runGitCliff.d.ts +0 -38
- package/dist/esm/runGitCliff.js +0 -25
|
@@ -2,43 +2,40 @@ import { formatErrorLine, formatStatusLine } from '@williamthorsen/nmr-core';
|
|
|
2
2
|
import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
3
3
|
import { buildChangelogEntries } from "./buildChangelogEntries.js";
|
|
4
4
|
import { resolveOverridePath, validateAllChangelogOverrides, } from "./changelogOverrides.js";
|
|
5
|
-
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
5
|
+
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
6
|
+
import { getAllTagPrefixes } from "./generateChangelogs.js";
|
|
7
|
+
import { mergeMonorepoConfig, mergeSinglePackageConfig, readRootPackageVersion } from "./loadConfig.js";
|
|
8
|
+
import { loadValidatedConfig } from "./loadValidatedConfig.js";
|
|
9
9
|
const SYNTHETIC_VALIDATE_TAG = 'validate-only';
|
|
10
|
-
export async function validateOverridesCommand(styles, dependencies = {}) {
|
|
10
|
+
export async function validateOverridesCommand(styles, configPath, dependencies = {}) {
|
|
11
11
|
const discover = dependencies.discoverWorkspaces ?? discoverWorkspaces;
|
|
12
|
-
const load = dependencies.
|
|
12
|
+
const load = dependencies.loadValidatedConfig ?? (() => loadValidatedConfig(configPath));
|
|
13
13
|
const buildEntries = dependencies.buildEntries ?? defaultBuildEntries;
|
|
14
14
|
const validate = dependencies.validate ?? validateAllChangelogOverrides;
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
return { exitCode: 2, message: formatErrorLine(`Failed to load config: ${describeError(error)}`) };
|
|
21
|
-
}
|
|
22
|
-
let userConfig;
|
|
23
|
-
try {
|
|
24
|
-
userConfig = validateLoadedConfig(rawConfig);
|
|
25
|
-
}
|
|
26
|
-
catch (error) {
|
|
27
|
-
return { exitCode: 2, message: describeError(error) };
|
|
15
|
+
const configResult = await load();
|
|
16
|
+
if (configResult.status === 'invalid') {
|
|
17
|
+
return { exitCode: 2, message: formatConfigProblem(configResult.problem) };
|
|
28
18
|
}
|
|
29
|
-
|
|
19
|
+
const userConfig = configResult.status === 'ok' ? configResult.config : undefined;
|
|
20
|
+
let workspace;
|
|
30
21
|
try {
|
|
31
|
-
|
|
22
|
+
workspace = discover();
|
|
32
23
|
}
|
|
33
24
|
catch (error) {
|
|
34
25
|
return { exitCode: 2, message: formatErrorLine(`Failed to discover workspaces: ${describeError(error)}`) };
|
|
35
26
|
}
|
|
27
|
+
if (workspace.kind === 'empty') {
|
|
28
|
+
return {
|
|
29
|
+
exitCode: 2,
|
|
30
|
+
message: formatErrorLine(`No workspace package to validate. ${describeEmptyWorkspace(workspace)}`),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
36
33
|
let inputs;
|
|
37
34
|
try {
|
|
38
35
|
inputs =
|
|
39
|
-
|
|
36
|
+
workspace.kind === 'single-package'
|
|
40
37
|
? buildSinglePackageInputs(userConfig, buildEntries)
|
|
41
|
-
: buildMonorepoInputs(
|
|
38
|
+
: buildMonorepoInputs(workspace.packageDirs, userConfig, buildEntries);
|
|
42
39
|
}
|
|
43
40
|
catch (error) {
|
|
44
41
|
return { exitCode: 2, message: formatErrorLine(`Failed to resolve overrides scope: ${describeError(error)}`) };
|
|
@@ -71,54 +68,42 @@ function formatSummaryLine(errorCount, warningCount) {
|
|
|
71
68
|
function pluralize(count, noun) {
|
|
72
69
|
return count === 1 ? `${count} ${noun}` : `${count} ${noun}s`;
|
|
73
70
|
}
|
|
74
|
-
function defaultBuildEntries(config,
|
|
75
|
-
|
|
76
|
-
if (tagPattern !== undefined) {
|
|
77
|
-
options.tagPattern = tagPattern;
|
|
78
|
-
}
|
|
79
|
-
if (includePaths !== undefined) {
|
|
80
|
-
options.includePaths = includePaths;
|
|
81
|
-
}
|
|
82
|
-
return buildChangelogEntries(config, SYNTHETIC_VALIDATE_TAG, options);
|
|
71
|
+
function defaultBuildEntries(config, options) {
|
|
72
|
+
return buildChangelogEntries(config, SYNTHETIC_VALIDATE_TAG, options).entries;
|
|
83
73
|
}
|
|
84
|
-
function
|
|
85
|
-
const
|
|
74
|
+
function flattenEntriesToItems(entries) {
|
|
75
|
+
const items = [];
|
|
86
76
|
for (const entry of entries) {
|
|
87
77
|
for (const section of entry.sections) {
|
|
88
|
-
for (const
|
|
89
|
-
if (
|
|
90
|
-
|
|
91
|
-
}
|
|
78
|
+
for (const { hash, entry: position } of section.items) {
|
|
79
|
+
if (hash === undefined)
|
|
80
|
+
continue;
|
|
81
|
+
items.push(position === undefined ? { hash } : { hash, entry: position });
|
|
92
82
|
}
|
|
93
83
|
}
|
|
94
84
|
}
|
|
95
|
-
return
|
|
85
|
+
return items;
|
|
96
86
|
}
|
|
97
|
-
function
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const { config, errors } = validateConfig(rawConfig);
|
|
102
|
-
if (errors.length > 0) {
|
|
103
|
-
throw new Error(`Invalid config:\n - ${errors.join('\n - ')}`);
|
|
104
|
-
}
|
|
105
|
-
return config;
|
|
87
|
+
function formatConfigProblem(problem) {
|
|
88
|
+
return problem.kind === 'load'
|
|
89
|
+
? formatErrorLine(`Failed to load config: ${problem.message}`)
|
|
90
|
+
: `Invalid config:\n - ${problem.errors.join('\n - ')}`;
|
|
106
91
|
}
|
|
107
92
|
function buildSinglePackageInputs(userConfig, buildEntries) {
|
|
108
93
|
const config = mergeSinglePackageConfig(userConfig);
|
|
109
|
-
const
|
|
94
|
+
const items = flattenEntriesToItems(buildEntries(config, { tagPrefixes: [config.tagPrefix] }));
|
|
110
95
|
return {
|
|
111
|
-
project: { filePath: resolveOverridePath('.'),
|
|
96
|
+
project: { filePath: resolveOverridePath('.'), items },
|
|
112
97
|
};
|
|
113
98
|
}
|
|
114
99
|
function buildMonorepoInputs(discoveredPaths, userConfig, buildEntries) {
|
|
115
100
|
const rootPackage = readRootPackageVersion();
|
|
116
101
|
const config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
|
|
117
102
|
const workspaces = config.workspaces.map((workspace) => {
|
|
118
|
-
const
|
|
103
|
+
const options = { tagPrefixes: getAllTagPrefixes(workspace), paths: workspace.paths };
|
|
119
104
|
return {
|
|
120
105
|
filePath: resolveOverridePath(workspace.workspacePath),
|
|
121
|
-
|
|
106
|
+
items: flattenEntriesToItems(buildEntries(config, options)),
|
|
122
107
|
};
|
|
123
108
|
});
|
|
124
109
|
const project = config.project;
|
|
@@ -126,8 +111,8 @@ function buildMonorepoInputs(discoveredPaths, userConfig, buildEntries) {
|
|
|
126
111
|
filePath: resolveOverridePath('.'),
|
|
127
112
|
};
|
|
128
113
|
if (project !== undefined) {
|
|
129
|
-
const
|
|
130
|
-
projectScope.
|
|
114
|
+
const options = { tagPrefixes: [project.tagPrefix], paths: project.paths };
|
|
115
|
+
projectScope.items = flattenEntriesToItems(buildEntries(config, options));
|
|
131
116
|
}
|
|
132
117
|
return { project: projectScope, workspaces };
|
|
133
118
|
}
|
package/docs/changelogs.md
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
# Changelogs
|
|
2
2
|
|
|
3
|
-
What reaches a published tarball, what each `.meta/changelog.json` item contains, how
|
|
3
|
+
What reaches a published tarball, what each `.meta/changelog.json` item contains, which commits reach a changelog, how a merge commit's change-record block becomes items, and how release notes reach a README.
|
|
4
4
|
|
|
5
5
|
Neither `CHANGELOG.md` nor `.meta/changelog.json` reaches a published tarball on its own. npm stopped including `CHANGELOG` automatically in npm 7, so a package that declares a `files` field ships a changelog only where that field names `CHANGELOG.md` and `.meta/changelog.json`. release-kit's readyup kit reports a publishable workspace whose `files` field omits either.
|
|
6
6
|
|
|
7
7
|
## `changelog.json` item schema
|
|
8
8
|
|
|
9
|
-
Each item under a section in `.meta/changelog.json` carries one required field and
|
|
9
|
+
Each item under a section in `.meta/changelog.json` carries one required field and five optional ones:
|
|
10
10
|
|
|
11
|
-
| Field | Type | Meaning
|
|
12
|
-
| ------------- | --------- |
|
|
13
|
-
| `description` | `string` | The bullet headline, taken from the commit subject with the ticket ID and type prefix stripped.
|
|
14
|
-
| `body` | `string` | The commit body, with trailing trailer metadata stripped.
|
|
15
|
-
| `breaking` | `boolean` | Present and `true` where the commit subject carried the `!` prefix and the
|
|
16
|
-
| `migration` | `string` | The migration step for a consumer. See below.
|
|
17
|
-
| `hash` | `string` | The full commit SHA,
|
|
11
|
+
| Field | Type | Meaning |
|
|
12
|
+
| ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
13
|
+
| `description` | `string` | The bullet headline, taken from the commit subject with the ticket ID and type prefix stripped, or from a change-record entry's `text` (see [Change-record blocks](#change-record-blocks)). |
|
|
14
|
+
| `body` | `string` | The commit body, with trailing trailer metadata and any `change-record` block stripped. Absent on an item derived from a change-record entry. |
|
|
15
|
+
| `breaking` | `boolean` | Present and `true` where the commit subject carried the `!` prefix, or the change-record entry set `breaking: true`, and the type permits it. See [`!` (breaking change) policy](work-types.md#-breaking-change-policy). |
|
|
16
|
+
| `migration` | `string` | The migration step for a consumer. See below. |
|
|
17
|
+
| `hash` | `string` | The full commit SHA, which an override key matches by prefix. Absent on synthetic propagation entries. |
|
|
18
|
+
| `entry` | `number` | The 1-based position of the change-record entry from which the item derives, which an override key `<hash>:<n>` matches. Absent on an item derived from the commit's title. |
|
|
18
19
|
|
|
19
20
|
Every optional field is omitted rather than emitted as `null`, so a consumer tests for presence.
|
|
20
21
|
|
|
21
22
|
### The `migration` field
|
|
22
23
|
|
|
23
|
-
A commit body states a migration step as a paragraph opening with the literal label `Migration:`. Where one is present, `migration` carries that paragraph with the label stripped
|
|
24
|
+
A commit body states a migration step as a paragraph opening with the literal label `Migration:`. Where one is present, `migration` carries that paragraph with the label stripped. An item derived from a change-record entry takes `migration` from the entry instead, and has no `body`.
|
|
25
|
+
|
|
26
|
+
A title-derived item looks like this:
|
|
24
27
|
|
|
25
28
|
```jsonc
|
|
26
29
|
{
|
|
@@ -34,30 +37,66 @@ Three properties are worth knowing:
|
|
|
34
37
|
|
|
35
38
|
- **It is independent of `breaking`.** A `deprecate` cannot carry `!` under the default breaking policy, and a `deprecate` still calls for a migration. Filter on `migration` to find every step; filter on `breaking` to find every breaking change.
|
|
36
39
|
- **`body` keeps the paragraph.** The field is an extraction, not a move, so `CHANGELOG.md` and `.meta/changelog.json` go on agreeing.
|
|
37
|
-
- **
|
|
40
|
+
- **An override cannot set it.** `migration` is not a field an override file can set (see [File shape](editorial-overrides.md#file-shape)). On a title-derived item it is re-derived from whatever `body` an override installs, and cleared where that body carries no labeled paragraph, so overriding `body` changes the migration text. An entry-derived item keeps its entry's `migration` whatever `body` an override installs.
|
|
38
41
|
|
|
39
42
|
The label match is exact: `migration:` and `**Migration:**` are not recognized.
|
|
40
43
|
|
|
41
|
-
##
|
|
44
|
+
## What reaches a changelog
|
|
45
|
+
|
|
46
|
+
release-kit splits the history reachable from `HEAD` into one window per release tag that matches the scope's tag prefixes, restricted to commits that touch the scope's `paths`. The bump reads the same items: Its level is the highest that the unreleased window's items call for, so a commit that bumps is one that the changelog lists.
|
|
47
|
+
|
|
48
|
+
A commit whose last `change-record` block records entries yields its items from those entries, as [Change-record blocks](#change-record-blocks) describes. Any other commit in a window reaches the changelog when its subject passes three checks:
|
|
49
|
+
|
|
50
|
+
- **A ticket-ID prefix**, such as `#42 `, `TOOL-123 `, or `## `.
|
|
51
|
+
- **A declared work type**, resolved against the merged [work types](work-types.md).
|
|
52
|
+
- **A type not excluded from the changelog** by `excludedFromChangelog: true`.
|
|
53
|
+
|
|
54
|
+
`release:` commits and merge commits whose subject git wrote (`Merge …`) never reach a changelog, whether or not they carry a block. A commit that fails a check yields no item and raises no bump, and a release left with no surviving commit gets no changelog entry. The prepare report lists a commit of the unreleased window that fails the first or the second check as unparseable, unless a malformed-block warning already reports it; it lists none that fails the third. The commit's type decides the section under which its item appears.
|
|
55
|
+
|
|
56
|
+
Every item of a commit reaches every workspace whose window contains the commit, because windows are selected by the paths that the commit touches.
|
|
57
|
+
|
|
58
|
+
## Change-record blocks
|
|
59
|
+
|
|
60
|
+
A squash-merge commit composed from a pull request can end with a fenced block whose info string is exactly `change-record`. Its payload is YAML, with the grammar that codeassembly's `change-record.md` specifies. release-kit reads these keys and ignores any other:
|
|
61
|
+
|
|
62
|
+
| Key | Type | Meaning |
|
|
63
|
+
| --------------------- | -------- | ------------------------------------------------------------------------------- |
|
|
64
|
+
| `entries` | list | The change entries, one per outcome of the change, in the order that they read. |
|
|
65
|
+
| `entries[].type` | `string` | A work type, resolved through the declared types and their aliases. Required. |
|
|
66
|
+
| `entries[].text` | `string` | The sentence that reports the outcome. Required. |
|
|
67
|
+
| `entries[].breaking` | boolean | Whether the outcome breaks consumers. |
|
|
68
|
+
| `entries[].scopes` | list | The scopes that the outcome touched, each a string. Validated but not yet used. |
|
|
69
|
+
| `entries[].migration` | `string` | The migration step for a consumer. |
|
|
70
|
+
| `pr_number` | integer | The merged pull request's number, a positive integer. |
|
|
71
|
+
| `ticket_ref` | `string` | The ticket that the change serves. Validated but not yet used. |
|
|
72
|
+
|
|
73
|
+
release-kit reads the **last** `change-record` fence in the message. A key whose value is null reads as absent.
|
|
74
|
+
|
|
75
|
+
### Items from entries
|
|
76
|
+
|
|
77
|
+
Each entry yields one item, whether or not the commit subject carries a ticket-ID prefix or a declared type:
|
|
78
|
+
|
|
79
|
+
- **Section**: the header of the entry's `type`, so the section order and the audience follow as for a title.
|
|
80
|
+
- **`description`**: the entry's `text` as written, followed by ` (#N)` when the block records `pr_number`.
|
|
81
|
+
- **`breaking`**: set when the entry's `breaking` is `true` and the type's breaking policy permits it.
|
|
82
|
+
- **`migration`**: the entry's `migration`, when present. There is no `body`.
|
|
83
|
+
- **`hash`** and **`entry`**: the commit's hash, and the entry's 1-based position among all the block's entries, including those that yield no item.
|
|
84
|
+
|
|
85
|
+
An entry whose type is excluded from the changelog yields no item. An override targets one of these items by the key `<hash>:<n>`, where `n` is the item's `entry`. A bare `<hash>` key matches every item of the commit, and it fails when it sets `description` or `body` on a commit with several items; see [Keys](editorial-overrides.md#keys).
|
|
42
86
|
|
|
43
|
-
|
|
87
|
+
### Fallback to the title
|
|
44
88
|
|
|
45
|
-
|
|
46
|
-
| -------- | ----------------------------- | ----------------------------------------------- |
|
|
47
|
-
| 1 | `cliffConfigPath` in config | Explicit path, returned without existence check |
|
|
48
|
-
| 2 | `.config/git-cliff.toml` | Project-level override |
|
|
49
|
-
| 3 | `cliff.toml` | Repo root fallback |
|
|
50
|
-
| 4 | Bundled `cliff.toml.template` | Automatic fallback |
|
|
89
|
+
A commit is read from its title, as [What reaches a changelog](#what-reaches-a-changelog) describes, when it has no block, when the block's `entries` is absent or empty, or when the block is malformed. A block is malformed when it never closes, when its payload is not valid YAML or not a mapping, when `entries` is not a list, when an entry is not a mapping, when an entry's `type` or `text` is missing, blank, or not a string, when `breaking` is not a boolean, when `scopes` is not a list of strings, when `migration` or `ticket_ref` is not a string, or when `pr_number` is not a positive integer. One defect makes the whole block malformed.
|
|
51
90
|
|
|
52
|
-
|
|
91
|
+
### Reported diagnostics
|
|
53
92
|
|
|
54
|
-
-
|
|
55
|
-
- Handles both `type: description` and `workspace|type: description` commit formats
|
|
56
|
-
- Groups commits by work type via `[git].commit_parsers`
|
|
93
|
+
`release-kit prepare` reports these as warnings for the release being prepared, from the commits that no earlier release contains:
|
|
57
94
|
|
|
58
|
-
|
|
95
|
+
- **A malformed block**, with the defect that stopped the read.
|
|
96
|
+
- **An entry whose type is not declared**, with its position. It yields no item.
|
|
97
|
+
- **An entry that violates its type's breaking policy**, as a policy violation at the entry's position. Its item is not marked breaking.
|
|
59
98
|
|
|
60
|
-
|
|
99
|
+
The result that `prepare` returns carries these as `malformedBlocks`, `undeclaredEntryTypes`, and `policyViolations` entries whose `surface` is `'entry'`, whether the target releases or is skipped. A block whose entries are all undeclared or excluded yields no item, so the commit raises no bump.
|
|
61
100
|
|
|
62
101
|
## Release-notes injection
|
|
63
102
|
|
package/docs/configuration.md
CHANGED
|
@@ -4,11 +4,20 @@ The fields of `.config/release-kit.config.ts`, how a workspace declares its prio
|
|
|
4
4
|
|
|
5
5
|
A minimal config file is in the [README](../README.md#configuration).
|
|
6
6
|
|
|
7
|
+
## Config file location
|
|
8
|
+
|
|
9
|
+
release-kit reads `.config/release-kit.config.ts`, resolved against the working directory. A run reads a different file when the subcommand is given `--config <path>`, which is also resolved against the working directory.
|
|
10
|
+
|
|
11
|
+
The flag is accepted by every subcommand that reads a config: `prepare`, `publish`, `create-github-release`, `show-tag-prefixes`, `overrides validate`, `sync-labels init`, and `sync-labels generate`. `commit`, `tag`, `push`, and `sync-labels sync` read no config and reject it.
|
|
12
|
+
|
|
13
|
+
The two paths differ in what an absent file means. An absent default path means the repo declares no config, and the run proceeds on discovered defaults. An absent `--config` path fails the command with an error naming the resolved path, because a caller who names a file expects that file to be read. A file that exists and fails validation is an error on both paths alike, and so is one that exists and fails to load.
|
|
14
|
+
|
|
15
|
+
`--config` names a file to read, never a write target. `release-kit init --with-config` scaffolds to `.config/release-kit.config.ts` and takes no `--config`, and `sync-labels init` writes its seeded `repoLabels` block to that same path.
|
|
16
|
+
|
|
7
17
|
## `ReleaseKitConfig` reference
|
|
8
18
|
|
|
9
19
|
| Field | Type | Description |
|
|
10
20
|
| ------------------ | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
11
|
-
| `cliffConfigPath` | `string` | Explicit path to cliff config. If omitted, resolved automatically: `.config/git-cliff.toml` → `cliff.toml` → bundled template |
|
|
12
21
|
| `workspaces` | `WorkspaceOverride[]` | Override or exclude discovered workspaces (matched by `dir`) |
|
|
13
22
|
| `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
|
|
14
23
|
| `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
|
|
@@ -79,7 +88,7 @@ Tag prefixes from distinct owners must not be identical or be a strict prefix of
|
|
|
79
88
|
- A `retiredPackages[]` entry (one owner per entry).
|
|
80
89
|
- The `project` block, when configured.
|
|
81
90
|
|
|
82
|
-
release-kit
|
|
91
|
+
release-kit matches a tag to a prefix when the tag name starts with the prefix and continues with a digit, so a strict-prefix overlap between distinct owners can match the wrong owner's tags. For example, a project prefix of `v` collides with a workspace prefix of `v11y-check-v`, since `v11y-check-v1.0.0` starts with `v` followed by a digit. The rule rejects every strict-prefix overlap, including one such as `v` and `vue-helpers-v` that no tag of the longer prefix can reach.
|
|
83
92
|
|
|
84
93
|
The rule is enforced at config load; the resulting error identifies both colliding declarations.
|
|
85
94
|
|
|
@@ -89,11 +98,13 @@ Print a per-workspace table of derived tag prefixes, tag counts, and declared le
|
|
|
89
98
|
|
|
90
99
|
Exits `0` when every workspace derives a prefix and there are no cross-workspace collisions; exits `1` on any derivation failure or collision. Undeclared candidates do not affect the exit code — they surface as a warning via the `legacy tag prefixes are declared` readyup check.
|
|
91
100
|
|
|
101
|
+
A config file that exists and either fails to load or fails schema validation reports the failure on stderr and exits `1`, printing no preview, because a config that cannot be read leaves the declared-versus-undeclared verdict wrong in the one dimension the command exists to report on. That holds on the default path and under `--config`, and in single-package mode as well as monorepo mode. An absent default config is a supported state and still previews against derived defaults.
|
|
102
|
+
|
|
92
103
|
In single-package mode, prints a single row with `workspacePath = .` and `derivedPrefix = v`; legacy entries and undeclared-candidate scanning are not applicable.
|
|
93
104
|
|
|
94
105
|
## `VersionPatterns`
|
|
95
106
|
|
|
96
|
-
Defines which
|
|
107
|
+
Defines which work types trigger major or minor bumps. `prepare` applies it to each changelog item of the unreleased window and takes the highest level. An item of a type not listed bumps patch.
|
|
97
108
|
|
|
98
109
|
```typescript
|
|
99
110
|
interface VersionPatterns {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Editorial overrides
|
|
2
2
|
|
|
3
|
-
How to correct a generated changelog entry without rewriting history: where override files live, how they compose, what an entry may set, and how to validate the files.
|
|
3
|
+
How to correct a generated changelog entry without rewriting history: where override files live, how they compose, how a key targets items, what an entry may set, and how to validate the files.
|
|
4
4
|
|
|
5
5
|
Generated changelogs occasionally need editorial correction — typos, redacted scope, reworded entries, or historical commits whose bodies carry verbatim PR-template scaffolding (`## What`, `## Why`, etc.) that renders as literal text in user-facing release notes. Rewriting git history is not viable, and any in-place edit to `CHANGELOG.md` or `.meta/changelog.json` is overwritten on the next release because release-kit regenerates both artifacts from scratch.
|
|
6
6
|
|
|
7
|
-
Override files are the supported escape hatch. Drop a checked-in JSON file at the conventional path for the scope you want to influence, keyed by commit hash, and `release-kit prepare` applies the overrides between `buildChangelogEntries` and serialization. Both `CHANGELOG.md` and `.meta/changelog.json` reflect the post-override view, so downstream consumers (the GitHub Release body, the in-app release-notes page, etc.) see the same content.
|
|
7
|
+
Override files are the supported escape hatch. Drop a checked-in JSON file at the conventional path for the scope you want to influence, keyed by commit hash or by commit hash and entry position, and `release-kit prepare` applies the overrides between `buildChangelogEntries` and serialization. Both `CHANGELOG.md` and `.meta/changelog.json` reflect the post-override view, so downstream consumers (the GitHub Release body, the in-app release-notes page, etc.) see the same content.
|
|
8
8
|
|
|
9
9
|
## File-location convention
|
|
10
10
|
|
|
@@ -22,8 +22,8 @@ When a workspace's changelog is rendered, both files are consulted:
|
|
|
22
22
|
|
|
23
23
|
- The root file's overrides apply globally.
|
|
24
24
|
- The workspace file's overrides apply only to that workspace.
|
|
25
|
-
- When the **same
|
|
26
|
-
-
|
|
25
|
+
- When the **same key** (string-equal, byte-for-byte) appears in both files, the **workspace entry wins entirely** for that workspace's changelog — no field-level merge, the workspace entry replaces the root entry.
|
|
26
|
+
- Non-identical keys that match the same item, such as a root `abc1234` beside a workspace `abc1234:2`, or two prefixes of one commit, do **not** shadow. The run fails with the [overlapping-keys error](#conflicting-keys), which names both keys.
|
|
27
27
|
- Other keys in the root file still apply for that workspace.
|
|
28
28
|
|
|
29
29
|
The project-level changelog applies only the root file. Per-workspace files describe per-workspace editorial intent and have no meaning at the aggregated project tier.
|
|
@@ -39,9 +39,13 @@ A key that doesn't match any commit gets a stale-reference warning. The warning'
|
|
|
39
39
|
|
|
40
40
|
```json
|
|
41
41
|
{
|
|
42
|
+
"$schema": "https://github.com/williamthorsen/node-monorepo-tools/raw/release-kit-v<version>/packages/release-kit/schemas/changelog-overrides.json",
|
|
42
43
|
"82962311": {
|
|
43
44
|
"audience": "skip"
|
|
44
45
|
},
|
|
46
|
+
"3f0c9a1:2": {
|
|
47
|
+
"description": "Reworded headline for the second change-record entry"
|
|
48
|
+
},
|
|
45
49
|
"abc1234d": {
|
|
46
50
|
"body": "Cleaned-up prose without the original PR-template scaffolding."
|
|
47
51
|
},
|
|
@@ -52,7 +56,7 @@ A key that doesn't match any commit gets a stale-reference warning. The warning'
|
|
|
52
56
|
}
|
|
53
57
|
```
|
|
54
58
|
|
|
55
|
-
Per-entry fields are all optional, but at least one must be present per entry:
|
|
59
|
+
The top-level `$schema` key is optional and names the [published JSON Schema](#published-json-schema) for editor support; it is not an override. Per-entry fields are all optional, but at least one must be present per entry:
|
|
56
60
|
|
|
57
61
|
| Field | Type | Effect |
|
|
58
62
|
| ------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
@@ -61,15 +65,44 @@ Per-entry fields are all optional, but at least one must be present per entry:
|
|
|
61
65
|
| `body` | `string` | Replaces the entry's body (the prose that renders below the bullet). Other fields are preserved. |
|
|
62
66
|
| `breaking` | `boolean` | Toggles the `🚨 **Breaking:** ` marker on the bullet. |
|
|
63
67
|
|
|
64
|
-
There is no `migration` key: the field is derived from `body`, so an override that replaces `body` re-derives it
|
|
68
|
+
There is no `migration` key: On a title-derived item the field is derived from `body`, so an override that replaces `body` re-derives it, and an item derived from a change-record entry keeps its entry's `migration`. See [The `migration` field](changelogs.md#the-migration-field).
|
|
69
|
+
|
|
70
|
+
## Keys
|
|
71
|
+
|
|
72
|
+
A key takes one of two forms. In both, the hash part is lowercase hex and matches as a prefix of the commit hash.
|
|
73
|
+
|
|
74
|
+
| Key | Targets |
|
|
75
|
+
| ------------ | ---------------------------------------------------------------- |
|
|
76
|
+
| `<hash>` | Every item of the commit |
|
|
77
|
+
| `<hash>:<n>` | The one item derived from the commit's `n`th change-record entry |
|
|
78
|
+
|
|
79
|
+
`n` counts from 1 over the `entries` of the commit's [change-record block](changelogs.md#change-record-blocks), in the order in which the PR lists them, including entries that yield no item. An item derived from the commit's title has no entry position, so `<hash>:<n>` never matches it. Any other key fails loading.
|
|
80
|
+
|
|
81
|
+
The hash part can be the full 40-character SHA or a prefix. `prepare` resolves it against the distinct commit hashes in the changelog:
|
|
82
|
+
|
|
83
|
+
- **One commit** — the override applies to the items that the key targets. A 7-character prefix is usually unambiguous within a single repo's history; longer prefixes are always safe.
|
|
84
|
+
- **No commit** — the override is treated as a stale reference (probably from a rebase or branch deletion) and prepare reports a warning. The release continues. An ordinal key whose commit has no item at position `n` is stale in the same way.
|
|
85
|
+
- **Several commits** — the release aborts with an error naming the key and the matching hashes. Lengthen the prefix or use the full SHA.
|
|
86
|
+
|
|
87
|
+
### Conflicting keys
|
|
65
88
|
|
|
66
|
-
|
|
89
|
+
Two further conditions abort the release. Each counts the items within one changelog.
|
|
67
90
|
|
|
68
|
-
|
|
91
|
+
- **A bare key that sets `description` or `body` on a commit with several items.** One headline or body cannot replace several different ones, so the error names the ordinal keys to use instead: `Override key 'abc1234' sets description on a commit with several items; use abc1234:1, abc1234:2`. `audience` and `breaking` on a bare key still apply to every item.
|
|
92
|
+
- **Two non-identical keys that match the same item.** The error names both keys, such as `Override keys 'abc1234' and 'abc1234:2' both match the item at abc1234:2; keep one`. Neither override applies to the item.
|
|
93
|
+
|
|
94
|
+
### Example: one commit, several items
|
|
95
|
+
|
|
96
|
+
Suppose commit `3f0c9a1` merged a PR whose change record lists three entries. Its changelog contains three items, and each key below targets one of them:
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"3f0c9a1:2": { "description": "Reworded headline for the second entry" },
|
|
101
|
+
"3f0c9a1:3": { "audience": "skip" }
|
|
102
|
+
}
|
|
103
|
+
```
|
|
69
104
|
|
|
70
|
-
|
|
71
|
-
- **No matches (0 hits)** — the override is treated as a stale reference (probably from a rebase or branch deletion) and prepare reports a warning. The release continues.
|
|
72
|
-
- **Ambiguous prefix (2+ hits)** — the release aborts with an error naming the key and the matching hashes. Lengthen the prefix or use the full SHA.
|
|
105
|
+
A bare `"3f0c9a1": { "audience": "skip" }` would skip all three items instead. Adding it beside the keys above fails the run, because it overlaps each of them.
|
|
73
106
|
|
|
74
107
|
Override application errors abort the run with a non-zero exit; warnings (zero-match keys) are non-fatal and surface on `PrepareResult.warnings`.
|
|
75
108
|
|
|
@@ -79,12 +112,16 @@ The override file is validated when `release-kit prepare` loads it. Each error n
|
|
|
79
112
|
|
|
80
113
|
- Missing file → empty map, no error (the no-op default — projects that do not need overrides skip the file entirely).
|
|
81
114
|
- Malformed JSON → error.
|
|
115
|
+
- A key that is neither `<hash>` nor `<hash>:<n>` (uppercase hex, non-hex characters, `:0`, a leading zero in `n`) → error.
|
|
116
|
+
- A top-level `$schema` that is not a string → error.
|
|
82
117
|
- Wrong top-level shape (e.g., array, primitive) → error.
|
|
83
118
|
- Unknown fields on an entry → error.
|
|
84
119
|
- Wrong field types (e.g., `description` as a number) → error.
|
|
85
120
|
- An entry with no fields set → error (a copy-paste mistake more often than not).
|
|
86
121
|
- `audience: 'all'` or `audience: 'dev'` → error in the current release: only `'skip'` is supported (see below).
|
|
87
122
|
|
|
123
|
+
Applying the overrides then aborts the run on an ambiguous prefix and on either of the [conflicting-key conditions](#conflicting-keys): a bare key setting `description` or `body` on a commit with several items, and two keys matching the same item.
|
|
124
|
+
|
|
88
125
|
### Standalone validation: `release-kit overrides validate`
|
|
89
126
|
|
|
90
127
|
For a focused overrides-only health check (locally or as a CI gate), run:
|
|
@@ -95,16 +132,38 @@ pnpm exec release-kit overrides validate
|
|
|
95
132
|
|
|
96
133
|
This walks every `.meta/changelog-overrides.json` file across the project tier and per-workspace tier, reporting three classes of finding:
|
|
97
134
|
|
|
98
|
-
| Class | Examples
|
|
99
|
-
| ------------------- |
|
|
100
|
-
| Schema/parse errors | malformed JSON, unknown fields, wrong field types, no-field entries, unsupported `audience` | `2` |
|
|
101
|
-
| Ambiguous-prefix | an override key resolves to 2+ commit hashes
|
|
102
|
-
|
|
|
135
|
+
| Class | Examples | Exit code |
|
|
136
|
+
| ------------------- | ----------------------------------------------------------------------------------------------------------- | --------- |
|
|
137
|
+
| Schema/parse errors | malformed JSON, malformed keys, unknown fields, wrong field types, no-field entries, unsupported `audience` | `2` |
|
|
138
|
+
| Ambiguous-prefix | an override key resolves to 2+ commit hashes | `2` |
|
|
139
|
+
| Conflicting keys | a bare key sets `description` or `body` on a commit with several items; two keys match the same item | `2` |
|
|
140
|
+
| Stale-key warnings | an override key resolves to no item in its applicable scope | `1` |
|
|
103
141
|
|
|
104
142
|
Tier-aware stale-key semantics match `release-kit prepare`'s match-set exactly: a workspace-tier key is stale if it does not match in its own workspace's history; a root-tier key is stale only if it matches in **no** scope (no workspace AND not the project release window).
|
|
105
143
|
|
|
144
|
+
Conflicting keys are found within one file and across the root and workspace files, as `prepare` composes them. An overlap between a root key and a workspace key is reported against the workspace file.
|
|
145
|
+
|
|
106
146
|
The same logic is also exposed programmatically via the `validateAllChangelogOverrides` function exported from `@williamthorsen/release-kit`, for callers that want to integrate the check into their own tooling.
|
|
107
147
|
|
|
148
|
+
## Published JSON Schema
|
|
149
|
+
|
|
150
|
+
release-kit publishes a JSON Schema for the override file. The schema lives at `packages/release-kit/schemas/changelog-overrides.json` in this repo and is reachable via the stable raw URL:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
https://github.com/williamthorsen/node-monorepo-tools/raw/release-kit-v<version>/packages/release-kit/schemas/changelog-overrides.json
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
An override file references it from its top-level `$schema` key:
|
|
157
|
+
|
|
158
|
+
```json
|
|
159
|
+
{
|
|
160
|
+
"$schema": "https://github.com/williamthorsen/node-monorepo-tools/raw/release-kit-v<version>/packages/release-kit/schemas/changelog-overrides.json",
|
|
161
|
+
"82962311": { "audience": "skip" }
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
The schema states the key form and the entry fields. It accepts `audience` values `'all'` and `'dev'`, which the file format declares; the loader still rejects them (see below).
|
|
166
|
+
|
|
108
167
|
## Audience semantics: v1 supports `'skip'` only
|
|
109
168
|
|
|
110
169
|
The on-disk format declares the full `'all' | 'dev' | 'skip'` audience vocabulary so the file format will not need to change when the v2 reclassification feature ships. In the current release, only `'skip'` is supported at runtime; `'all'` and `'dev'` are rejected with an explicit "not yet supported" error.
|
package/docs/labels.md
CHANGED
|
@@ -8,6 +8,8 @@ Manage GitHub label definitions via the `repoLabels` block of `.config/release-k
|
|
|
8
8
|
|
|
9
9
|
`init` scaffolds the `.github/workflows/sync-labels.yaml` caller workflow and seeds a `repoLabels` block with scope labels discovered from workspaces and declared `retiredPackages`. When `.config/release-kit.config.ts` does not exist, `init` writes it; when it does, `init` prints the block for manual paste — a hand-authored config is never rewritten. `generate` resolves the block and writes `.github/labels.yaml`; with `--check` it regenerates in memory and exits non-zero if the committed file is stale or missing, writing nothing. `sync` triggers the workflow remotely — it requires the `gh` CLI and an existing workflow file.
|
|
10
10
|
|
|
11
|
+
`init` and `generate` accept `--config <path>` to read a config elsewhere; `sync` reads none and rejects the flag. Under `--config`, `init` reads the named file and prints the block for manual paste, and fails when that file does not exist rather than scaffolding one, so its write target stays `.config/release-kit.config.ts`. See [Config file location](configuration.md#config-file-location).
|
|
12
|
+
|
|
11
13
|
Every `sync-labels` subcommand refuses to run while the retired `.config/sync-labels.config.ts` is present, so custom labels cannot be silently dropped mid-migration, and the refusal names what to move and where.
|
|
12
14
|
|
|
13
15
|
### Label configuration
|
package/docs/project-releases.md
CHANGED
|
@@ -19,7 +19,7 @@ When configured, each `release-kit prepare` run additionally:
|
|
|
19
19
|
|
|
20
20
|
- Computes commits since the last project tag (`<tagPrefix><version>`), filtered to `paths` (by default, the union of every contributing workspace's paths).
|
|
21
21
|
- Bumps the root `package.json`'s `version` field using the same bump-derivation rules as workspaces (or the `--bump=...` override).
|
|
22
|
-
- Regenerates the root `./CHANGELOG.md` from the structured `ChangelogEntry[]`
|
|
22
|
+
- Regenerates the root `./CHANGELOG.md` from the structured `ChangelogEntry[]` built from the project's release history (scoped to the project's `tagPrefix` and the same `paths`) and any matching editorial overrides.
|
|
23
23
|
- Emits `./.meta/changelog.json` (when `changelogJson.enabled`).
|
|
24
24
|
- With `--with-release-notes`, additionally emits `./docs/RELEASE_NOTES.v<version>.md`.
|
|
25
25
|
- Appends the project tag to `tmp/.release-tags` so `release-kit commit` and `release-kit tag` pick it up alongside per-workspace tags.
|
|
@@ -42,7 +42,7 @@ interface ProjectConfig {
|
|
|
42
42
|
|
|
43
43
|
By default the contributing workspaces are implicit: every non-excluded discovered workspace contributes its `<dir>/**` glob, and the project release considers commits under their union.
|
|
44
44
|
|
|
45
|
-
Declare `paths` to choose the window yourself. Each entry
|
|
45
|
+
Declare `paths` to choose the window yourself. Each entry is a git pathspec, which both the bump and the changelog read. A declared value **replaces** the workspace union rather than extending it: `paths: ['docs/**']` drops every workspace commit from the project release.
|
|
46
46
|
|
|
47
47
|
A repo whose content lives at the root — where no commit matches any `<dir>/**` glob, so the project release would skip every run — declares the whole tree:
|
|
48
48
|
|
|
@@ -54,13 +54,13 @@ const config: ReleaseKitConfig = {
|
|
|
54
54
|
|
|
55
55
|
`'**'` is the whole-tree pattern, matching root-level files, dotfiles, and nested paths alike.
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
A bare directory such as `'aws'` selects its whole subtree, as `'aws/**'` does.
|
|
58
58
|
|
|
59
59
|
Validation rules:
|
|
60
60
|
|
|
61
61
|
- The root `package.json` must exist and declare a `version` field. release-kit reports an error at config-load time if either is missing.
|
|
62
62
|
- The `project` block is rejected in single-package mode (the package's own release already covers the whole repo, so a project tier would only duplicate it).
|
|
63
|
-
- `paths`, when declared, must hold at least one non-empty string. An empty array is rejected: it reaches git
|
|
63
|
+
- `paths`, when declared, must hold at least one non-empty string. An empty array is rejected: it reaches git as no filter at all, which is the inverse of how it reads.
|
|
64
64
|
- Unknown fields inside `project` are rejected.
|
|
65
65
|
|
|
66
66
|
CLI flag interactions:
|
package/docs/releasing.md
CHANGED
|
@@ -14,7 +14,6 @@ Scaffolded files:
|
|
|
14
14
|
- `.github/workflows/publish.yaml` — workflow that delegates to a reusable publish workflow
|
|
15
15
|
- `.github/workflows/release.yaml` — workflow that delegates to a reusable release workflow
|
|
16
16
|
- `.config/release-kit.config.ts` — starter config with commented-out customization examples (with `--with-config`)
|
|
17
|
-
- `.config/git-cliff.toml` — copied from the bundled template (with `--with-config`)
|
|
18
17
|
|
|
19
18
|
## `release-kit prepare`
|
|
20
19
|
|
|
@@ -22,22 +21,28 @@ Run release preparation with automatic workspace discovery.
|
|
|
22
21
|
|
|
23
22
|
Workspace names for `--only` match the package directory name (e.g., `arrays`, `release-kit`).
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
`prepare` reads each target's history once and builds the changelog items of its unreleased commits. The bump is the highest level that those items call for, under [`versionPatterns`](configuration.md#versionpatterns), so the bump and the changelog describe the same release: A `feat` entry in a [change-record block](changelogs.md#change-record-blocks) bumps minor under a `docs:` title, and a breaking item bumps major.
|
|
25
|
+
|
|
26
|
+
- A commit that yields no item bumps nothing: one without a ticket-ID prefix, one of a type excluded from the changelog (such as `fmt`), and one whose change-record entries are all undeclared or excluded.
|
|
27
|
+
- A `BREAKING CHANGE:` footer raises no major bump. Only a subject's `!` and an entry's `breaking: true` mark an item breaking; on a type whose policy forbids `!`, the footer is reported as a [policy violation](work-types.md#policy-enforcement).
|
|
28
|
+
- A target whose commits yield no item is skipped. The prepare report lists the commits that could not be parsed and the change-record diagnostics, skipped targets included.
|
|
29
|
+
|
|
30
|
+
`--bump` and `--force` are orthogonal in every mode, single-package included: `--bump` is purely a level chooser; `--force` is purely a release trigger. Examples:
|
|
26
31
|
|
|
27
32
|
```sh
|
|
28
33
|
# Release every target at its natural bump level (no flags).
|
|
29
34
|
release-kit prepare
|
|
30
35
|
|
|
31
|
-
# Force a release even when no
|
|
32
|
-
# per target, with each target keeping its natural bump if
|
|
36
|
+
# Force a release even when no commit yields a changelog item; defaults to
|
|
37
|
+
# patch per target, with each target keeping its natural bump if it has one.
|
|
33
38
|
release-kit prepare --force
|
|
34
39
|
|
|
35
40
|
# Force a release at a uniform level across every releasing target.
|
|
36
41
|
release-kit prepare --force --bump=minor
|
|
37
42
|
|
|
38
|
-
# --bump=X alone is a level chooser, NOT a trigger. If a target
|
|
39
|
-
#
|
|
40
|
-
#
|
|
43
|
+
# --bump=X alone is a level chooser, NOT a trigger. If no commit of a target
|
|
44
|
+
# yields a changelog item, it skips with a "Pass --force..." reason. Otherwise
|
|
45
|
+
# the override applies.
|
|
41
46
|
release-kit prepare --bump=minor
|
|
42
47
|
```
|
|
43
48
|
|
|
@@ -64,7 +69,7 @@ docs/*.v*.md
|
|
|
64
69
|
|
|
65
70
|
### Setting an explicit version with `--set-version`
|
|
66
71
|
|
|
67
|
-
The `--set-version` flag is a first-class escape hatch for the cases where
|
|
72
|
+
The `--set-version` flag is a first-class escape hatch for the cases where the derived bump produces the wrong version — most notably, promoting a pre-1.0 package to 1.0.0. Pre-1.0 packages collapse a `feat!` breaking change to a minor bump (matching semantic-release's `initialMajor: false` and release-please's `bump-minor-pre-major`), so a deliberate promotion to 1.0.0 must be requested explicitly.
|
|
68
73
|
|
|
69
74
|
The flag validates that:
|
|
70
75
|
|