@williamthorsen/release-kit 11.0.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 +62 -2
- package/.readyup/kits/default.js +1 -17
- package/.readyup/manifest.json +4 -4
- package/CHANGELOG.md +40 -0
- package/README.md +25 -44
- package/dist/esm/attachChangelogDiagnostics.d.ts +4 -0
- package/dist/esm/attachChangelogDiagnostics.js +11 -0
- package/dist/esm/bin/release-kit.js +4 -3
- package/dist/esm/buildChangelogEntries.d.ts +59 -17
- package/dist/esm/buildChangelogEntries.js +194 -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/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/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 +4 -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 +1 -1
- package/dist/esm/loadConfig.js +1 -4
- 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.js +4 -12
- 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 +39 -28
- 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 +6 -5
- package/dist/esm/validateOverridesCommand.js +16 -23
- package/docs/changelogs.md +64 -25
- package/docs/configuration.md +2 -3
- package/docs/editorial-overrides.md +75 -16
- package/docs/project-releases.md +4 -4
- package/docs/releasing.md +13 -8
- package/docs/work-types.md +13 -9
- package/package.json +1 -2
- 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
|
@@ -8,12 +8,11 @@ import type { ReleaseConfig, ReleaseType } from './types.js';
|
|
|
8
8
|
*/
|
|
9
9
|
export interface ReleasePrepareOptions {
|
|
10
10
|
/**
|
|
11
|
-
* Release even when no commits or no bump-worthy commits exist since the last tag
|
|
12
|
-
*
|
|
13
|
-
* the release falls back to `patch`.
|
|
11
|
+
* Release even when no commits or no bump-worthy commits exist since the last tag.
|
|
12
|
+
* Orthogonal to `bumpOverride`: when `bumpOverride` is not given, the release falls back to `patch`.
|
|
14
13
|
*/
|
|
15
14
|
force?: boolean;
|
|
16
|
-
/**
|
|
15
|
+
/** Choose the level of a release instead of the level that the changelog items call for; triggers no release. */
|
|
17
16
|
bumpOverride?: ReleaseType;
|
|
18
17
|
/**
|
|
19
18
|
* Explicit target version (canonical `N.N.N`) that bypasses commit-derived bump logic.
|
|
@@ -39,11 +38,11 @@ export interface ReleasePrepareOptions {
|
|
|
39
38
|
/**
|
|
40
39
|
* Orchestrate the release preparation workflow for a single package.
|
|
41
40
|
*
|
|
42
|
-
* 1.
|
|
43
|
-
* 2.
|
|
41
|
+
* 1. Reads the release history once.
|
|
42
|
+
* 2. Decides the release from the history's bump, `--force`, and `--bump` (or takes `--set-version`).
|
|
44
43
|
* 3. Bumps all configured package.json version fields.
|
|
45
|
-
* 4. Generates changelogs
|
|
46
|
-
* 5.
|
|
44
|
+
* 4. Generates changelogs from the same history.
|
|
45
|
+
* 5. Renders the optional format command.
|
|
47
46
|
*
|
|
48
47
|
* Returns a structured `PrepareResult` with all data needed for presentation.
|
|
49
48
|
*/
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { join as joinPath } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { attachChangelogDiagnostics } from "./attachChangelogDiagnostics.js";
|
|
3
|
+
import { readReleaseHistory, toChangelogEntries } from "./buildChangelogEntries.js";
|
|
3
4
|
import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
|
|
4
5
|
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
5
6
|
import { mergeChangelogEntriesWithDisk, renderChangelogJson, resolveChangelogJsonPath } from "./changelogJsonFile.js";
|
|
6
7
|
import { applyChangelogOverrides, formatStaleOverrideKeyWarning, loadOverridesForScopes, } from "./changelogOverrides.js";
|
|
7
|
-
import {
|
|
8
|
-
import { DEFAULT_BREAKING_POLICIES, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
9
|
-
import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
|
|
10
|
-
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
8
|
+
import { decideRelease } from "./decideRelease.js";
|
|
11
9
|
import { hasPrettierConfig } from "./hasPrettierConfig.js";
|
|
12
10
|
import { resolveWorkTypes } from "./loadConfig.js";
|
|
13
11
|
import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
|
|
@@ -15,46 +13,32 @@ import { planVersionBump, planVersionSet } from "./planVersionBump.js";
|
|
|
15
13
|
import { renderChangelogMarkdown } from "./renderChangelogMarkdown.js";
|
|
16
14
|
import { deriveSectionOrder } from "./resolveReleaseNotesConfig.js";
|
|
17
15
|
export function releasePrepare(config, options) {
|
|
18
|
-
const { bumpOverride, setVersion, withReleaseNotes } = options;
|
|
16
|
+
const { bumpOverride, force, setVersion, withReleaseNotes } = options;
|
|
19
17
|
const writes = [];
|
|
20
|
-
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
21
|
-
const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
|
|
22
|
-
const breakingPolicies = config.breakingPolicies ?? DEFAULT_BREAKING_POLICIES;
|
|
23
18
|
const overridesResult = loadOverridesForScopes({ project: '.' });
|
|
24
19
|
if (overridesResult.errors.length > 0) {
|
|
25
20
|
throw new Error(`Failed to load changelog overrides:\n - ${overridesResult.errors.join('\n - ')}`);
|
|
26
21
|
}
|
|
27
22
|
const overrides = overridesResult.project;
|
|
28
|
-
const
|
|
23
|
+
const history = readReleaseHistory(config, { tagPrefixes: [config.tagPrefix] });
|
|
24
|
+
const { commits } = history.unreleased;
|
|
25
|
+
const tag = history.previousTag;
|
|
26
|
+
const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
|
|
29
27
|
let releaseType;
|
|
30
|
-
let parsedCommitCount;
|
|
31
|
-
let unparseableCommits;
|
|
32
|
-
const collector = createPolicyViolationCollector();
|
|
33
28
|
let bump;
|
|
34
|
-
if (setVersion
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
else {
|
|
48
|
-
releaseType = bumpOverride;
|
|
49
|
-
}
|
|
50
|
-
if (releaseType === undefined) {
|
|
51
|
-
const skipped = buildSkippedSinglePackage({
|
|
52
|
-
commitCount: commits.length,
|
|
53
|
-
previousTag: tag,
|
|
54
|
-
parsedCommitCount,
|
|
55
|
-
unparseableCommits,
|
|
56
|
-
policyViolations: collector.violations.length > 0 ? collector.violations : undefined,
|
|
57
|
-
});
|
|
29
|
+
if (setVersion === undefined) {
|
|
30
|
+
const decision = decideRelease({
|
|
31
|
+
naturalBump: history.unreleased.bump,
|
|
32
|
+
commitCount: commits.length,
|
|
33
|
+
force,
|
|
34
|
+
bumpOverride,
|
|
35
|
+
skipReasons: {
|
|
36
|
+
noCommits: `No commits ${since}. Pass --force to release at patch. Skipping.`,
|
|
37
|
+
noBumpWorthy: `No bump-worthy commits ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`,
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
if (decision.outcome === 'skip') {
|
|
41
|
+
const skipped = buildSkippedSinglePackage(history, decision.skipReason);
|
|
58
42
|
return {
|
|
59
43
|
workspaces: [skipped],
|
|
60
44
|
tags: [],
|
|
@@ -63,14 +47,18 @@ export function releasePrepare(config, options) {
|
|
|
63
47
|
formatCommand: undefined,
|
|
64
48
|
};
|
|
65
49
|
}
|
|
50
|
+
releaseType = decision.releaseType;
|
|
66
51
|
bump = planVersionBump(config.packageFiles, releaseType);
|
|
67
52
|
}
|
|
53
|
+
else {
|
|
54
|
+
bump = planVersionSet(config.packageFiles, setVersion);
|
|
55
|
+
}
|
|
68
56
|
writes.push(...bump.writes);
|
|
69
57
|
const newTag = `${config.tagPrefix}${bump.newVersion}`;
|
|
70
58
|
const planWarnings = [];
|
|
71
59
|
const changelogs = planSinglePackageChangelogs({
|
|
72
60
|
config,
|
|
73
|
-
|
|
61
|
+
history,
|
|
74
62
|
newTag,
|
|
75
63
|
newVersion: bump.newVersion,
|
|
76
64
|
overrides,
|
|
@@ -90,15 +78,12 @@ export function releasePrepare(config, options) {
|
|
|
90
78
|
formatCommand = { command: `${formatCommandStr} ${modifiedFiles.join(' ')}`, files: modifiedFiles };
|
|
91
79
|
}
|
|
92
80
|
const released = buildReleasedSinglePackage({
|
|
93
|
-
|
|
81
|
+
history,
|
|
94
82
|
bump,
|
|
95
83
|
newTag,
|
|
96
84
|
changelogFiles,
|
|
97
|
-
previousTag: tag,
|
|
98
|
-
parsedCommitCount,
|
|
99
85
|
releaseType,
|
|
100
|
-
|
|
101
|
-
policyViolations: collector.violations.length > 0 ? collector.violations : undefined,
|
|
86
|
+
bumpOverride: setVersion === undefined ? bumpOverride : undefined,
|
|
102
87
|
setVersion,
|
|
103
88
|
previewFiles: previewWrites.map((write) => write.path),
|
|
104
89
|
});
|
|
@@ -114,28 +99,26 @@ export function releasePrepare(config, options) {
|
|
|
114
99
|
}
|
|
115
100
|
return plan;
|
|
116
101
|
}
|
|
117
|
-
function buildSkippedSinglePackage(
|
|
102
|
+
function buildSkippedSinglePackage(history, skipReason) {
|
|
103
|
+
const { unreleased } = history;
|
|
118
104
|
const skipped = {
|
|
119
105
|
status: 'skipped',
|
|
120
|
-
commitCount:
|
|
121
|
-
|
|
106
|
+
commitCount: unreleased.commits.length,
|
|
107
|
+
parsedCommitCount: unreleased.parsedCommitCount,
|
|
108
|
+
skipReason,
|
|
122
109
|
};
|
|
123
|
-
if (
|
|
124
|
-
skipped.previousTag =
|
|
125
|
-
}
|
|
126
|
-
if (args.parsedCommitCount !== undefined) {
|
|
127
|
-
skipped.parsedCommitCount = args.parsedCommitCount;
|
|
110
|
+
if (history.previousTag !== undefined) {
|
|
111
|
+
skipped.previousTag = history.previousTag;
|
|
128
112
|
}
|
|
129
|
-
if (
|
|
130
|
-
skipped.unparseableCommits =
|
|
131
|
-
}
|
|
132
|
-
if (args.policyViolations !== undefined) {
|
|
133
|
-
skipped.policyViolations = args.policyViolations;
|
|
113
|
+
if (unreleased.unparseableCommits !== undefined) {
|
|
114
|
+
skipped.unparseableCommits = unreleased.unparseableCommits;
|
|
134
115
|
}
|
|
116
|
+
attachChangelogDiagnostics(skipped, unreleased.diagnostics);
|
|
135
117
|
return skipped;
|
|
136
118
|
}
|
|
137
119
|
function buildReleasedSinglePackage(args) {
|
|
138
|
-
const {
|
|
120
|
+
const { history, bump, newTag, changelogFiles, releaseType, bumpOverride, setVersion } = args;
|
|
121
|
+
const { commits, diagnostics, parsedCommitCount, unparseableCommits } = history.unreleased;
|
|
139
122
|
const released = {
|
|
140
123
|
status: 'released',
|
|
141
124
|
commitCount: commits.length,
|
|
@@ -146,21 +129,20 @@ function buildReleasedSinglePackage(args) {
|
|
|
146
129
|
changelogFiles,
|
|
147
130
|
commits,
|
|
148
131
|
};
|
|
149
|
-
if (previousTag !== undefined) {
|
|
150
|
-
released.previousTag = previousTag;
|
|
151
|
-
}
|
|
152
|
-
if (parsedCommitCount !== undefined) {
|
|
153
|
-
released.parsedCommitCount = parsedCommitCount;
|
|
132
|
+
if (history.previousTag !== undefined) {
|
|
133
|
+
released.previousTag = history.previousTag;
|
|
154
134
|
}
|
|
155
135
|
if (releaseType !== undefined) {
|
|
156
136
|
released.releaseType = releaseType;
|
|
137
|
+
released.parsedCommitCount = parsedCommitCount;
|
|
138
|
+
if (unparseableCommits !== undefined) {
|
|
139
|
+
released.unparseableCommits = unparseableCommits;
|
|
140
|
+
}
|
|
157
141
|
}
|
|
158
|
-
if (
|
|
159
|
-
released.
|
|
160
|
-
}
|
|
161
|
-
if (policyViolations !== undefined) {
|
|
162
|
-
released.policyViolations = policyViolations;
|
|
142
|
+
if (bumpOverride !== undefined) {
|
|
143
|
+
released.bumpOverride = bumpOverride;
|
|
163
144
|
}
|
|
145
|
+
attachChangelogDiagnostics(released, diagnostics);
|
|
164
146
|
if (setVersion !== undefined) {
|
|
165
147
|
released.setVersion = setVersion;
|
|
166
148
|
}
|
|
@@ -170,13 +152,11 @@ function buildReleasedSinglePackage(args) {
|
|
|
170
152
|
return released;
|
|
171
153
|
}
|
|
172
154
|
function planSinglePackageChangelogs(args) {
|
|
173
|
-
const { config,
|
|
174
|
-
const isEmptyRange = commits.length === 0;
|
|
155
|
+
const { config, history, newTag, newVersion, overrides, overrideWarnings } = args;
|
|
156
|
+
const isEmptyRange = history.unreleased.commits.length === 0;
|
|
175
157
|
const today = new Date().toISOString().slice(0, 10);
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
: buildChangelogEntries(config, newTag);
|
|
179
|
-
const applied = applyChangelogOverrides(baseEntries, overrides);
|
|
158
|
+
const builtEntries = isEmptyRange ? [buildEmptyReleaseEntry(newVersion, today)] : toChangelogEntries(history, newTag);
|
|
159
|
+
const applied = applyChangelogOverrides(builtEntries, overrides);
|
|
180
160
|
if (applied.errors.length > 0) {
|
|
181
161
|
throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
|
|
182
162
|
}
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { join as joinPath } from 'node:path';
|
|
2
2
|
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
3
|
-
import {
|
|
3
|
+
import { attachChangelogDiagnostics } from "./attachChangelogDiagnostics.js";
|
|
4
|
+
import { readReleaseHistory, toChangelogEntries } from "./buildChangelogEntries.js";
|
|
4
5
|
import { buildDependencyGraph } from "./buildDependencyGraph.js";
|
|
5
6
|
import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
|
|
6
7
|
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
7
8
|
import { buildSyntheticChangelogEntry } from "./buildSyntheticChangelogEntry.js";
|
|
8
9
|
import { mergeChangelogEntriesWithDisk, renderChangelogJson, resolveChangelogJsonPath } from "./changelogJsonFile.js";
|
|
9
10
|
import { applyWorkspaceOverrides, createOverrideContext, formatStaleOverrideKeyWarning, } from "./changelogOverrides.js";
|
|
10
|
-
import { createPolicyViolationCollector } from "./collectPolicyViolations.js";
|
|
11
11
|
import { decideRelease } from "./decideRelease.js";
|
|
12
|
-
import { DEFAULT_BREAKING_POLICIES, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
13
12
|
import { detectUndeclaredTagPrefixes } from "./detectUndeclaredTagPrefixes.js";
|
|
14
|
-
import {
|
|
15
|
-
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
13
|
+
import { getAllTagPrefixes } from "./generateChangelogs.js";
|
|
16
14
|
import { hasPrettierConfig } from "./hasPrettierConfig.js";
|
|
17
15
|
import { resolveWorkTypes } from "./loadConfig.js";
|
|
18
16
|
import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
|
|
@@ -33,10 +31,10 @@ export function releasePrepareMono(config, options) {
|
|
|
33
31
|
const { directBumps, directResults, skippedResults } = determineDirectBumps(config, options);
|
|
34
32
|
const previousTags = new Map();
|
|
35
33
|
for (const result of directResults.values()) {
|
|
36
|
-
previousTags.set(result.workspace.dir, result.
|
|
34
|
+
previousTags.set(result.workspace.dir, result.history.previousTag);
|
|
37
35
|
}
|
|
38
36
|
for (const skipped of skippedResults) {
|
|
39
|
-
previousTags.set(skipped.workspace.dir, skipped.
|
|
37
|
+
previousTags.set(skipped.workspace.dir, skipped.history.previousTag);
|
|
40
38
|
}
|
|
41
39
|
const graph = buildDependencyGraph(config.workspaces);
|
|
42
40
|
const fullReleaseSet = propagateBumps(directBumps, graph);
|
|
@@ -111,9 +109,6 @@ function determineDirectBumps(config, options) {
|
|
|
111
109
|
if (setVersion !== undefined && config.workspaces.length !== 1) {
|
|
112
110
|
throw new Error(`--set-version requires exactly one workspace; received ${config.workspaces.length}`);
|
|
113
111
|
}
|
|
114
|
-
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
115
|
-
const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
|
|
116
|
-
const breakingPolicies = config.breakingPolicies ?? DEFAULT_BREAKING_POLICIES;
|
|
117
112
|
const directBumps = new Map();
|
|
118
113
|
const directResults = new Map();
|
|
119
114
|
const skippedResults = [];
|
|
@@ -122,7 +117,8 @@ function determineDirectBumps(config, options) {
|
|
|
122
117
|
for (const workspace of config.workspaces) {
|
|
123
118
|
const name = workspace.dir;
|
|
124
119
|
const stageLabel = workspaceStageLabel(workspace.dir);
|
|
125
|
-
const
|
|
120
|
+
const history = tryStage(stageLabel, () => readReleaseHistory(config, { tagPrefixes: getAllTagPrefixes(workspace), paths: workspace.paths }));
|
|
121
|
+
const tag = history.previousTag;
|
|
126
122
|
const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
|
|
127
123
|
if (tag === undefined) {
|
|
128
124
|
maybeEmitBaselineHint(workspace, knownPrefixes, hintState);
|
|
@@ -131,54 +127,32 @@ function determineDirectBumps(config, options) {
|
|
|
131
127
|
directBumps.set(workspace.dir, { releaseType: 'patch', newVersionOverride: setVersion });
|
|
132
128
|
directResults.set(workspace.dir, {
|
|
133
129
|
workspace,
|
|
134
|
-
|
|
135
|
-
commits,
|
|
130
|
+
history,
|
|
136
131
|
releaseType: undefined,
|
|
137
|
-
parsedCommitCount: undefined,
|
|
138
|
-
unparseableCommits: undefined,
|
|
139
|
-
policyViolations: undefined,
|
|
140
132
|
bumpOverride: undefined,
|
|
141
133
|
setVersion,
|
|
142
134
|
});
|
|
143
135
|
continue;
|
|
144
136
|
}
|
|
145
|
-
const
|
|
146
|
-
|
|
147
|
-
commits,
|
|
137
|
+
const decision = decideRelease({
|
|
138
|
+
naturalBump: history.unreleased.bump,
|
|
139
|
+
commitCount: history.unreleased.commits.length,
|
|
148
140
|
force,
|
|
149
141
|
bumpOverride,
|
|
150
|
-
workTypes,
|
|
151
|
-
versionPatterns,
|
|
152
|
-
scopeAliases: config.scopeAliases,
|
|
153
|
-
breakingPolicies,
|
|
154
|
-
onPolicyViolation: collector.onPolicyViolation,
|
|
155
142
|
skipReasons: {
|
|
156
143
|
noCommits: `No commits for ${name} ${since}. Pass --force to release at patch. Skipping.`,
|
|
157
144
|
noBumpWorthy: `No bump-worthy commits for ${name} ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`,
|
|
158
145
|
},
|
|
159
|
-
})
|
|
160
|
-
const policyViolations = collector.violations.length > 0 ? collector.violations : undefined;
|
|
146
|
+
});
|
|
161
147
|
if (decision.outcome === 'skip') {
|
|
162
|
-
skippedResults.push({
|
|
163
|
-
workspace,
|
|
164
|
-
tag,
|
|
165
|
-
commitCount: commits.length,
|
|
166
|
-
parsedCommitCount: decision.parsedCommitCount,
|
|
167
|
-
unparseableCommits: decision.unparseableCommits,
|
|
168
|
-
policyViolations,
|
|
169
|
-
skipReason: decision.skipReason,
|
|
170
|
-
});
|
|
148
|
+
skippedResults.push({ workspace, history, skipReason: decision.skipReason });
|
|
171
149
|
continue;
|
|
172
150
|
}
|
|
173
151
|
directBumps.set(workspace.dir, { releaseType: decision.releaseType });
|
|
174
152
|
directResults.set(workspace.dir, {
|
|
175
153
|
workspace,
|
|
176
|
-
|
|
177
|
-
commits,
|
|
154
|
+
history,
|
|
178
155
|
releaseType: decision.releaseType,
|
|
179
|
-
parsedCommitCount: decision.parsedCommitCount,
|
|
180
|
-
unparseableCommits: decision.unparseableCommits,
|
|
181
|
-
policyViolations,
|
|
182
156
|
bumpOverride,
|
|
183
157
|
});
|
|
184
158
|
}
|
|
@@ -190,24 +164,21 @@ function collectSkippedWorkspaces(skippedResults, fullReleaseSet) {
|
|
|
190
164
|
if (fullReleaseSet.has(skipped.workspace.dir)) {
|
|
191
165
|
continue;
|
|
192
166
|
}
|
|
167
|
+
const { previousTag, unreleased } = skipped.history;
|
|
193
168
|
const result = {
|
|
194
169
|
name: skipped.workspace.dir,
|
|
195
170
|
status: 'skipped',
|
|
196
|
-
commitCount:
|
|
171
|
+
commitCount: unreleased.commits.length,
|
|
172
|
+
parsedCommitCount: unreleased.parsedCommitCount,
|
|
197
173
|
skipReason: skipped.skipReason,
|
|
198
174
|
};
|
|
199
|
-
if (
|
|
200
|
-
result.previousTag =
|
|
201
|
-
}
|
|
202
|
-
if (skipped.parsedCommitCount !== undefined) {
|
|
203
|
-
result.parsedCommitCount = skipped.parsedCommitCount;
|
|
175
|
+
if (previousTag !== undefined) {
|
|
176
|
+
result.previousTag = previousTag;
|
|
204
177
|
}
|
|
205
|
-
if (
|
|
206
|
-
result.unparseableCommits =
|
|
207
|
-
}
|
|
208
|
-
if (skipped.policyViolations !== undefined) {
|
|
209
|
-
result.policyViolations = skipped.policyViolations;
|
|
178
|
+
if (unreleased.unparseableCommits !== undefined) {
|
|
179
|
+
result.unparseableCommits = unreleased.unparseableCommits;
|
|
210
180
|
}
|
|
181
|
+
attachChangelogDiagnostics(result, unreleased.diagnostics);
|
|
211
182
|
workspaces.push(result);
|
|
212
183
|
}
|
|
213
184
|
return workspaces;
|
|
@@ -256,15 +227,13 @@ function executeWorkspaceRelease(args) {
|
|
|
256
227
|
tags.push(newTag);
|
|
257
228
|
writes.push(...bump.writes);
|
|
258
229
|
modifiedFiles.push(...workspace.packageFiles, ...workspace.changelogPaths.map((changelogPath) => joinPath(changelogPath, 'CHANGELOG.md')));
|
|
259
|
-
const
|
|
260
|
-
const isEmptyRange = directResult !== undefined && directResult.commits.length === 0;
|
|
230
|
+
const directCommits = directResult?.history.unreleased.commits;
|
|
261
231
|
const { changelogFiles, previewFiles } = generateWorkspaceChangelogs({
|
|
262
232
|
workspace,
|
|
263
233
|
releaseEntry,
|
|
264
234
|
newTag,
|
|
265
235
|
newVersion: bump.newVersion,
|
|
266
|
-
|
|
267
|
-
isEmptyRange,
|
|
236
|
+
history: directResult?.history,
|
|
268
237
|
config,
|
|
269
238
|
today,
|
|
270
239
|
modifiedFiles,
|
|
@@ -277,7 +246,7 @@ function executeWorkspaceRelease(args) {
|
|
|
277
246
|
const released = {
|
|
278
247
|
name: dir,
|
|
279
248
|
status: 'released',
|
|
280
|
-
commitCount:
|
|
249
|
+
commitCount: directCommits?.length ?? 0,
|
|
281
250
|
currentVersion: bump.currentVersion,
|
|
282
251
|
newVersion: bump.newVersion,
|
|
283
252
|
tag: newTag,
|
|
@@ -286,7 +255,7 @@ function executeWorkspaceRelease(args) {
|
|
|
286
255
|
...(previewFiles.length > 0 && { previewFiles }),
|
|
287
256
|
};
|
|
288
257
|
attachReleasedWorkspaceOptionals(released, {
|
|
289
|
-
previousTag: directResult?.
|
|
258
|
+
previousTag: directResult?.history.previousTag ?? previousTags.get(dir),
|
|
290
259
|
directResult,
|
|
291
260
|
releaseEntry,
|
|
292
261
|
setVersionTarget,
|
|
@@ -298,20 +267,11 @@ function attachReleasedWorkspaceOptionals(released, args) {
|
|
|
298
267
|
if (previousTag !== undefined) {
|
|
299
268
|
released.previousTag = previousTag;
|
|
300
269
|
}
|
|
301
|
-
if (directResult?.parsedCommitCount !== undefined) {
|
|
302
|
-
released.parsedCommitCount = directResult.parsedCommitCount;
|
|
303
|
-
}
|
|
304
270
|
if (setVersionTarget === undefined) {
|
|
305
271
|
released.releaseType = releaseEntry.releaseType;
|
|
306
272
|
}
|
|
307
|
-
if (directResult
|
|
308
|
-
released.
|
|
309
|
-
}
|
|
310
|
-
if (directResult?.unparseableCommits !== undefined) {
|
|
311
|
-
released.unparseableCommits = directResult.unparseableCommits;
|
|
312
|
-
}
|
|
313
|
-
if (directResult?.policyViolations !== undefined) {
|
|
314
|
-
released.policyViolations = directResult.policyViolations;
|
|
273
|
+
if (directResult !== undefined) {
|
|
274
|
+
attachDirectHistory(released, directResult.history.unreleased, setVersionTarget === undefined);
|
|
315
275
|
}
|
|
316
276
|
if (releaseEntry.propagatedFrom !== undefined) {
|
|
317
277
|
released.propagatedFrom = releaseEntry.propagatedFrom;
|
|
@@ -323,19 +283,20 @@ function attachReleasedWorkspaceOptionals(released, args) {
|
|
|
323
283
|
released.setVersion = setVersionTarget;
|
|
324
284
|
}
|
|
325
285
|
}
|
|
286
|
+
function attachDirectHistory(released, unreleased, hasDecidedBump) {
|
|
287
|
+
released.commits = unreleased.commits;
|
|
288
|
+
if (hasDecidedBump) {
|
|
289
|
+
released.parsedCommitCount = unreleased.parsedCommitCount;
|
|
290
|
+
if (unreleased.unparseableCommits !== undefined) {
|
|
291
|
+
released.unparseableCommits = unreleased.unparseableCommits;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
attachChangelogDiagnostics(released, unreleased.diagnostics);
|
|
295
|
+
}
|
|
326
296
|
function generateWorkspaceChangelogs(args) {
|
|
327
|
-
const { workspace, releaseEntry, newTag, newVersion,
|
|
328
|
-
const
|
|
329
|
-
|
|
330
|
-
releaseEntry,
|
|
331
|
-
newTag,
|
|
332
|
-
newVersion,
|
|
333
|
-
isPropagationOnly,
|
|
334
|
-
isEmptyRange,
|
|
335
|
-
config,
|
|
336
|
-
today,
|
|
337
|
-
});
|
|
338
|
-
const applied = applyWorkspaceOverrides(newEntries, workspace.workspacePath, overrideContext);
|
|
297
|
+
const { workspace, releaseEntry, newTag, newVersion, history, config, today, modifiedFiles, writes, warnings, previewOptions, overrideContext, sectionOrder, } = args;
|
|
298
|
+
const entries = buildWorkspaceEntries({ workspace, releaseEntry, newTag, newVersion, history, today });
|
|
299
|
+
const applied = applyWorkspaceOverrides(entries, workspace.workspacePath, overrideContext);
|
|
339
300
|
const changelogFiles = [];
|
|
340
301
|
let firstMergedEntries;
|
|
341
302
|
for (const changelogPath of workspace.changelogPaths) {
|
|
@@ -355,15 +316,17 @@ function generateWorkspaceChangelogs(args) {
|
|
|
355
316
|
return { changelogFiles, previewFiles: previews.map((write) => write.path) };
|
|
356
317
|
}
|
|
357
318
|
function buildWorkspaceEntries(args) {
|
|
358
|
-
const { workspace, releaseEntry, newTag, newVersion,
|
|
359
|
-
if (
|
|
319
|
+
const { workspace, releaseEntry, newTag, newVersion, history, today } = args;
|
|
320
|
+
if (history === undefined) {
|
|
321
|
+
if (releaseEntry.propagatedFrom === undefined) {
|
|
322
|
+
throw new Error(`Workspace '${workspace.dir}' has neither a direct release nor a propagation source`);
|
|
323
|
+
}
|
|
360
324
|
return [buildSyntheticChangelogEntry(releaseEntry.propagatedFrom, newVersion, today)];
|
|
361
325
|
}
|
|
362
|
-
if (
|
|
326
|
+
if (history.unreleased.commits.length === 0) {
|
|
363
327
|
return [buildEmptyReleaseEntry(newVersion, today)];
|
|
364
328
|
}
|
|
365
|
-
|
|
366
|
-
return buildChangelogEntries(config, newTag, { tagPattern, includePaths: workspace.paths });
|
|
329
|
+
return toChangelogEntries(history, newTag);
|
|
367
330
|
}
|
|
368
331
|
function planPreviews(workspace, newTag, entries, previewOptions, warnings) {
|
|
369
332
|
if (!previewOptions.enabled || entries === undefined) {
|
|
@@ -40,7 +40,7 @@ export interface ReleasePrepareProjectArgs {
|
|
|
40
40
|
/**
|
|
41
41
|
* Run the project-level release stage.
|
|
42
42
|
*
|
|
43
|
-
* Mirrors the per-workspace pipeline shape —
|
|
43
|
+
* Mirrors the per-workspace pipeline shape — read the history → decide the bump → bump version →
|
|
44
44
|
* regenerate CHANGELOG → optionally emit changelog.json and release-notes previews — but
|
|
45
45
|
* targets the root `package.json` and the root `CHANGELOG.md`. Contributing paths come from
|
|
46
46
|
* the resolved `project.paths`, which defaults to the union of every (already-filtered)
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { join as joinPath } from 'node:path';
|
|
2
|
-
import {
|
|
2
|
+
import { attachChangelogDiagnostics } from "./attachChangelogDiagnostics.js";
|
|
3
|
+
import { readReleaseHistory, toChangelogEntries } from "./buildChangelogEntries.js";
|
|
3
4
|
import { buildEmptyReleaseEntry } from "./buildEmptyReleaseEntry.js";
|
|
4
5
|
import { mergeChangelogEntriesWithDisk, renderChangelogJson, resolveChangelogJsonPath } from "./changelogJsonFile.js";
|
|
5
6
|
import { applyChangelogOverrides } from "./changelogOverrides.js";
|
|
6
|
-
import { createPolicyViolationCollector } from "./collectPolicyViolations.js";
|
|
7
7
|
import { decideRelease } from "./decideRelease.js";
|
|
8
|
-
import {
|
|
9
|
-
import { buildTagPattern } from "./generateChangelogs.js";
|
|
10
|
-
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
8
|
+
import { DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
11
9
|
import { planReleaseNotesPreviews } from "./planReleaseNotesPreviews.js";
|
|
12
10
|
import { planVersionBump } from "./planVersionBump.js";
|
|
13
11
|
import { renderChangelogMarkdown } from "./renderChangelogMarkdown.js";
|
|
@@ -23,54 +21,43 @@ export function releasePrepareProject(args) {
|
|
|
23
21
|
throw new Error('releasePrepareProject called without a configured project block');
|
|
24
22
|
}
|
|
25
23
|
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const { tag, commits } = getCommitsSinceTarget([project.tagPrefix], contributingPaths);
|
|
24
|
+
const history = readReleaseHistory(config, { tagPrefixes: [project.tagPrefix], paths: project.paths });
|
|
25
|
+
const { commits, diagnostics, parsedCommitCount, unparseableCommits } = history.unreleased;
|
|
26
|
+
const tag = history.previousTag;
|
|
30
27
|
const since = tag === undefined ? '(no previous release found)' : `since ${tag}`;
|
|
31
|
-
const collector = createPolicyViolationCollector();
|
|
32
28
|
const decision = decideRelease({
|
|
33
|
-
|
|
29
|
+
naturalBump: history.unreleased.bump,
|
|
30
|
+
commitCount: commits.length,
|
|
34
31
|
force,
|
|
35
32
|
bumpOverride,
|
|
36
|
-
workTypes,
|
|
37
|
-
versionPatterns,
|
|
38
|
-
scopeAliases: config.scopeAliases,
|
|
39
|
-
breakingPolicies,
|
|
40
|
-
onPolicyViolation: collector.onPolicyViolation,
|
|
41
33
|
skipReasons: {
|
|
42
34
|
noCommits: `No commits ${since}. Pass --force to release at patch. Skipping.`,
|
|
43
35
|
noBumpWorthy: `No bump-worthy commits ${since}. Pass --force to release at patch (or --force --bump=X for a different level). Skipping.`,
|
|
44
36
|
},
|
|
45
37
|
});
|
|
46
|
-
const policyViolations = collector.violations.length > 0 ? collector.violations : undefined;
|
|
47
38
|
if (decision.outcome === 'skip') {
|
|
48
39
|
const skipped = {
|
|
49
40
|
status: 'skipped',
|
|
50
41
|
commitCount: commits.length,
|
|
51
|
-
parsedCommitCount
|
|
42
|
+
parsedCommitCount,
|
|
52
43
|
skipReason: decision.skipReason,
|
|
53
44
|
};
|
|
54
45
|
if (tag !== undefined) {
|
|
55
46
|
skipped.previousTag = tag;
|
|
56
47
|
}
|
|
57
|
-
if (
|
|
58
|
-
skipped.unparseableCommits =
|
|
59
|
-
}
|
|
60
|
-
if (policyViolations !== undefined) {
|
|
61
|
-
skipped.policyViolations = policyViolations;
|
|
48
|
+
if (unparseableCommits !== undefined) {
|
|
49
|
+
skipped.unparseableCommits = unparseableCommits;
|
|
62
50
|
}
|
|
51
|
+
attachChangelogDiagnostics(skipped, diagnostics);
|
|
63
52
|
return skipped;
|
|
64
53
|
}
|
|
65
|
-
const { releaseType
|
|
54
|
+
const { releaseType } = decision;
|
|
66
55
|
const bump = planVersionBump([ROOT_PACKAGE_FILE], releaseType);
|
|
67
56
|
writes.push(...bump.writes);
|
|
68
57
|
const newTag = `${project.tagPrefix}${bump.newVersion}`;
|
|
69
58
|
const changelogs = planProjectChangelogs({
|
|
70
59
|
config,
|
|
71
|
-
|
|
72
|
-
commits,
|
|
73
|
-
contributingPaths,
|
|
60
|
+
history,
|
|
74
61
|
newTag,
|
|
75
62
|
newVersion: bump.newVersion,
|
|
76
63
|
rootOverrides,
|
|
@@ -114,12 +101,10 @@ export function releasePrepareProject(args) {
|
|
|
114
101
|
if (unparseableCommits !== undefined) {
|
|
115
102
|
result.unparseableCommits = unparseableCommits;
|
|
116
103
|
}
|
|
117
|
-
if (policyViolations !== undefined) {
|
|
118
|
-
result.policyViolations = policyViolations;
|
|
119
|
-
}
|
|
120
104
|
if (bumpOverride !== undefined) {
|
|
121
105
|
result.bumpOverride = bumpOverride;
|
|
122
106
|
}
|
|
107
|
+
attachChangelogDiagnostics(result, diagnostics);
|
|
123
108
|
return result;
|
|
124
109
|
}
|
|
125
110
|
function resolveOptionalOverrideArgs(args) {
|
|
@@ -131,14 +116,11 @@ function resolveOptionalOverrideArgs(args) {
|
|
|
131
116
|
};
|
|
132
117
|
}
|
|
133
118
|
function planProjectChangelogs(args) {
|
|
134
|
-
const { config,
|
|
135
|
-
const isEmptyRange = commits.length === 0;
|
|
119
|
+
const { config, history, newTag, newVersion, rootOverrides, overrideWarnings, globalMatchedRootKeys } = args;
|
|
120
|
+
const isEmptyRange = history.unreleased.commits.length === 0;
|
|
136
121
|
const today = new Date().toISOString().slice(0, 10);
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
? [buildEmptyReleaseEntry(newVersion, today)]
|
|
140
|
-
: buildChangelogEntries(config, newTag, { tagPattern, includePaths: contributingPaths });
|
|
141
|
-
const applied = applyChangelogOverrides(newEntries, rootOverrides);
|
|
122
|
+
const builtEntries = isEmptyRange ? [buildEmptyReleaseEntry(newVersion, today)] : toChangelogEntries(history, newTag);
|
|
123
|
+
const applied = applyChangelogOverrides(builtEntries, rootOverrides);
|
|
142
124
|
if (applied.errors.length > 0) {
|
|
143
125
|
throw new Error(`Changelog override application failed:\n - ${applied.errors.join('\n - ')}`);
|
|
144
126
|
}
|