@williamthorsen/release-kit 14.1.0 → 14.2.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/.meta/changelog.json +136 -0
- package/.readyup/kits/default.js +63 -6
- package/.readyup/kits/npm-auto-publish.js +47 -35
- package/.readyup/manifest.json +12 -7
- package/CHANGELOG.md +39 -0
- package/README.md +2 -16
- package/dist/esm/assertTaggedBaseline.d.ts +2 -2
- package/dist/esm/attachChangelogDiagnostics.d.ts +2 -2
- package/dist/esm/bin/release-kit.js +5 -4
- package/dist/esm/buildChangelogEntries.d.ts +3 -3
- package/dist/esm/buildChangelogEntries.js +3 -3
- package/dist/esm/buildDependencyGraph.d.ts +11 -11
- package/dist/esm/buildDependencyGraph.js +13 -13
- package/dist/esm/buildReleaseSummary.d.ts +3 -3
- package/dist/esm/buildReleaseSummary.js +4 -12
- package/dist/esm/changelogArchive.d.ts +14 -0
- package/dist/esm/changelogArchive.js +21 -0
- package/dist/esm/changelogJsonFile.d.ts +2 -2
- package/dist/esm/changelogOverrides.d.ts +9 -8
- package/dist/esm/changelogOverrides.js +3 -6
- package/dist/esm/createGithubReleaseCommand.d.ts +2 -1
- package/dist/esm/createGithubReleaseCommand.js +7 -4
- package/dist/esm/deriveDirectoryConfig.d.ts +13 -0
- package/dist/esm/deriveDirectoryConfig.js +59 -0
- package/dist/esm/deriveWorkspaceConfig.js +0 -1
- package/dist/esm/discoverWorkspaces.d.ts +3 -3
- package/dist/esm/findTransitiveDependents.js +3 -3
- package/dist/esm/findUnroutedEntryScopes.d.ts +2 -2
- package/dist/esm/formatDirectoryPublishSkip.d.ts +3 -0
- package/dist/esm/formatDirectoryPublishSkip.js +3 -0
- package/dist/esm/formatExcludedSkip.d.ts +5 -2
- package/dist/esm/formatExcludedSkip.js +2 -2
- package/dist/esm/formatProjectPublishSkip.d.ts +3 -0
- package/dist/esm/formatProjectPublishSkip.js +3 -0
- package/dist/esm/generateChangelogs.d.ts +7 -7
- package/dist/esm/generateChangelogs.js +2 -2
- package/dist/esm/help/prepareHelp.d.ts +1 -1
- package/dist/esm/help/prepareHelp.js +7 -5
- package/dist/esm/init/templates.js +1 -1
- package/dist/esm/loadConfig.d.ts +12 -12
- package/dist/esm/loadConfig.js +79 -30
- package/dist/esm/prepareCommand.d.ts +1 -1
- package/dist/esm/prepareCommand.js +47 -46
- package/dist/esm/prepareTarget.d.ts +103 -0
- package/dist/esm/prepareTarget.js +274 -0
- package/dist/esm/previewTagPrefixes.d.ts +5 -3
- package/dist/esm/previewTagPrefixes.js +58 -13
- package/dist/esm/propagateBumps.js +1 -1
- package/dist/esm/publishCommand.js +13 -1
- package/dist/esm/readChangelogSections.d.ts +6 -4
- package/dist/esm/readChangelogSections.js +18 -4
- package/dist/esm/releasePlan.d.ts +4 -2
- package/dist/esm/releasePlan.js +34 -12
- package/dist/esm/releasePrepareTargets.d.ts +47 -0
- package/dist/esm/releasePrepareTargets.js +335 -0
- package/dist/esm/renderChangelogMarkdown.d.ts +23 -0
- package/dist/esm/renderChangelogMarkdown.js +47 -11
- package/dist/esm/reportPrepare.d.ts +2 -3
- package/dist/esm/reportPrepare.js +24 -12
- package/dist/esm/resolveCommandTags.d.ts +3 -1
- package/dist/esm/resolveCommandTags.js +15 -22
- package/dist/esm/resolveReleaseDeclaration.d.ts +45 -0
- package/dist/esm/resolveReleaseDeclaration.js +153 -0
- package/dist/esm/resolveReleaseTags.d.ts +14 -3
- package/dist/esm/resolveReleaseTags.js +36 -15
- package/dist/esm/resolveReleaseTargets.d.ts +29 -0
- package/dist/esm/resolveReleaseTargets.js +131 -0
- package/dist/esm/sync-labels/generateCommand.js +2 -1
- package/dist/esm/sync-labels/resolveLabels.d.ts +4 -3
- package/dist/esm/sync-labels/resolveLabels.js +30 -1
- package/dist/esm/sync-labels/types.d.ts +2 -0
- package/dist/esm/types.d.ts +251 -167
- package/dist/esm/types.js +22 -0
- package/dist/esm/validateConfig.js +80 -21
- package/dist/esm/validateOverridesCommand.d.ts +1 -1
- package/dist/esm/validateOverridesCommand.js +17 -28
- package/docs/api.md +25 -58
- package/docs/changelogs.md +26 -6
- package/docs/configuration.md +79 -16
- package/docs/labels.md +25 -2
- package/docs/project-releases.md +42 -2
- package/docs/publishing.md +3 -3
- package/docs/releasing.md +4 -4
- package/docs/work-types.md +1 -1
- package/package.json +3 -3
- package/presets/labels/common.yaml +11 -0
- package/dist/esm/releasePrepare.d.ts +0 -36
- package/dist/esm/releasePrepare.js +0 -230
- package/dist/esm/releasePrepareMono.d.ts +0 -29
- package/dist/esm/releasePrepareMono.js +0 -503
- package/dist/esm/releasePrepareProject.d.ts +0 -47
- package/dist/esm/releasePrepareProject.js +0 -164
package/dist/esm/loadConfig.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { MonorepoReleaseConfig,
|
|
1
|
+
import type { MonorepoReleaseConfig, ReleaseKitConfig, ReleaseSettings, WorkTypeConfig } from './types.js';
|
|
2
2
|
/** Path of the root `package.json` consulted when validating a `project` block. */
|
|
3
3
|
export declare const ROOT_PACKAGE_JSON_PATH = "package.json";
|
|
4
4
|
/**
|
|
@@ -38,26 +38,26 @@ export interface RootPackageInfo {
|
|
|
38
38
|
* Resolves a final monorepo config from discovered workspaces and an optional user config overlay.
|
|
39
39
|
*
|
|
40
40
|
* Merging rules:
|
|
41
|
-
* -
|
|
42
|
-
*
|
|
41
|
+
* - Release targets: `resolveReleaseDeclaration` projects the `releases` block, or the `workspaces` overrides and the
|
|
42
|
+
* `project` block, onto the discovered workspaces. Excluded workspaces move to `excludedWorkspaces`, and the
|
|
43
|
+
* directories that `releases` declares resolve through `deriveDirectoryConfig` into `directories`, which must not
|
|
44
|
+
* share a version file with any other target.
|
|
43
45
|
* - `workTypes`: Shallow merge; consumer entries override or add to defaults by key.
|
|
44
46
|
* - `versionPatterns`: Consumer value replaces defaults entirely.
|
|
45
47
|
* - Pass-through fields (see `applyOptionalPassthroughFields`): Consumer value wins.
|
|
46
|
-
* - `project`: Present iff
|
|
47
|
-
* `
|
|
48
|
-
*
|
|
49
|
-
* throws otherwise. The resolved prefix is included in the strict-prefix collision check
|
|
48
|
+
* - `project`: Present iff the config declares a composite. Resolves `tagPrefix` to `DEFAULT_PROJECT_TAG_PREFIX` and
|
|
49
|
+
* `paths` to the union of its members' `paths` when omitted. Requires `rootPackage` to be passed and to contain a
|
|
50
|
+
* valid `version` field; throws otherwise. The resolved prefix is included in the strict-prefix collision check
|
|
50
51
|
* across all workspace and retired-package prefixes.
|
|
51
52
|
*/
|
|
52
53
|
export declare function mergeMonorepoConfig(discoveredPaths: string[], userConfig: ReleaseKitConfig | undefined, rootPackage?: RootPackageInfo): MonorepoReleaseConfig;
|
|
53
54
|
/**
|
|
54
|
-
* Resolves a
|
|
55
|
+
* Resolves the release settings of a single-package repo from an optional user config overlay.
|
|
55
56
|
*
|
|
56
|
-
* Rejects a configured `project` block:
|
|
57
|
-
*
|
|
58
|
-
* single-package repo.
|
|
57
|
+
* Rejects a configured `project` or `releases` block: Both declare the targets of a monorepo, and a single-package
|
|
58
|
+
* repo has one target, its root package.
|
|
59
59
|
*/
|
|
60
|
-
export declare function mergeSinglePackageConfig(userConfig: ReleaseKitConfig | undefined):
|
|
60
|
+
export declare function mergeSinglePackageConfig(userConfig: ReleaseKitConfig | undefined): ReleaseSettings;
|
|
61
61
|
/**
|
|
62
62
|
* Merges consumer work-type overrides onto `DEFAULT_WORK_TYPES`.
|
|
63
63
|
*
|
package/dist/esm/loadConfig.js
CHANGED
|
@@ -11,7 +11,9 @@ import path from 'node:path';
|
|
|
11
11
|
import { pathToFileURL } from 'node:url';
|
|
12
12
|
import { chainError } from '@williamthorsen/toolbelt.errors/candidate';
|
|
13
13
|
import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_PROJECT_TAG_PREFIX, DEFAULT_RELEASE_NOTES_CONFIG, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES, } from "./defaults.js";
|
|
14
|
+
import { deriveDirectoryConfig } from "./deriveDirectoryConfig.js";
|
|
14
15
|
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
16
|
+
import { getTargetName, normalizeWorkspacePath, resolveReleaseDeclaration, } from "./resolveReleaseDeclaration.js";
|
|
15
17
|
import { isRecord } from "./typeGuards.js";
|
|
16
18
|
export const ROOT_PACKAGE_JSON_PATH = 'package.json';
|
|
17
19
|
export function readRootPackageVersion() {
|
|
@@ -55,33 +57,25 @@ export async function loadConfig(configPath) {
|
|
|
55
57
|
return resolved;
|
|
56
58
|
}
|
|
57
59
|
export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
|
|
58
|
-
|
|
59
|
-
assertUniqueTagPrefixes(
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
67
|
-
.map((w) => {
|
|
68
|
-
const override = overrides.get(w.dir);
|
|
69
|
-
if (override?.legacyIdentities === undefined) {
|
|
70
|
-
return w;
|
|
71
|
-
}
|
|
72
|
-
assertLegacyIdentityDoesNotMatchCurrent(w.dir, w.name, w.tagPrefix, override.legacyIdentities);
|
|
73
|
-
return { ...w, legacyIdentities: override.legacyIdentities.map((identity) => ({ ...identity })) };
|
|
74
|
-
});
|
|
60
|
+
const discovered = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
|
|
61
|
+
assertUniqueTagPrefixes(discovered);
|
|
62
|
+
const { retained: workspaces, excluded, directories: declaredDirectories, composite, } = resolveReleaseDeclaration(discovered, userConfig);
|
|
63
|
+
const directories = declaredDirectories.map((declared) => deriveDirectoryConfig(declared));
|
|
64
|
+
for (const workspace of workspaces) {
|
|
65
|
+
if (workspace.legacyIdentities !== undefined) {
|
|
66
|
+
assertLegacyIdentityDoesNotMatchCurrent(workspace.dir, workspace.name, workspace.tagPrefix, workspace.legacyIdentities);
|
|
67
|
+
}
|
|
75
68
|
}
|
|
76
69
|
if (userConfig?.retiredPackages !== undefined) {
|
|
77
70
|
assertRetiredPackagesDoNotCollideWithActive(workspaces, userConfig.retiredPackages);
|
|
78
71
|
}
|
|
79
|
-
const project = resolveProjectConfig(
|
|
72
|
+
const project = resolveProjectConfig(composite, rootPackage, workspaces);
|
|
80
73
|
const workTypes = resolveWorkTypes(userConfig?.workTypes);
|
|
81
74
|
const versionPatterns = userConfig?.versionPatterns === undefined ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
|
|
82
75
|
const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
|
|
83
76
|
const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
|
|
84
|
-
|
|
77
|
+
assertNoSharedVersionFiles(workspaces, directories, project);
|
|
78
|
+
assertNoTagPrefixCollisions(workspaces, directories, userConfig?.retiredPackages, project);
|
|
85
79
|
const result = {
|
|
86
80
|
workspaces,
|
|
87
81
|
workTypes,
|
|
@@ -89,6 +83,12 @@ export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
|
|
|
89
83
|
changelogJson,
|
|
90
84
|
releaseNotes,
|
|
91
85
|
};
|
|
86
|
+
if (excluded.length > 0) {
|
|
87
|
+
result.excludedWorkspaces = excluded;
|
|
88
|
+
}
|
|
89
|
+
if (directories.length > 0) {
|
|
90
|
+
result.directories = directories;
|
|
91
|
+
}
|
|
92
92
|
if (project !== undefined) {
|
|
93
93
|
result.project = project;
|
|
94
94
|
}
|
|
@@ -96,6 +96,9 @@ export function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
|
|
|
96
96
|
return result;
|
|
97
97
|
}
|
|
98
98
|
function applyOptionalPassthroughFields(result, userConfig) {
|
|
99
|
+
if (userConfig?.changelogMarkdown !== undefined) {
|
|
100
|
+
result.changelogMarkdown = userConfig.changelogMarkdown;
|
|
101
|
+
}
|
|
99
102
|
if (userConfig?.formatCommand !== undefined) {
|
|
100
103
|
result.formatCommand = userConfig.formatCommand;
|
|
101
104
|
}
|
|
@@ -110,14 +113,14 @@ export function mergeSinglePackageConfig(userConfig) {
|
|
|
110
113
|
if (userConfig?.project !== undefined) {
|
|
111
114
|
throw new Error('project block is not supported in single-package mode');
|
|
112
115
|
}
|
|
116
|
+
if (userConfig?.releases !== undefined) {
|
|
117
|
+
throw new Error('releases block is not supported in single-package mode');
|
|
118
|
+
}
|
|
113
119
|
const workTypes = resolveWorkTypes(userConfig?.workTypes);
|
|
114
120
|
const versionPatterns = userConfig?.versionPatterns === undefined ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
|
|
115
121
|
const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
|
|
116
122
|
const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
|
|
117
123
|
const result = {
|
|
118
|
-
tagPrefix: 'v',
|
|
119
|
-
packageFiles: ['package.json'],
|
|
120
|
-
changelogPaths: ['.'],
|
|
121
124
|
workTypes,
|
|
122
125
|
versionPatterns,
|
|
123
126
|
changelogJson,
|
|
@@ -161,22 +164,52 @@ function assertRetiredPackagesDoNotCollideWithActive(workspaces, retiredPackages
|
|
|
161
164
|
}
|
|
162
165
|
}
|
|
163
166
|
}
|
|
164
|
-
function resolveProjectConfig(
|
|
165
|
-
if (
|
|
167
|
+
function resolveProjectConfig(composite, rootPackage, workspaces) {
|
|
168
|
+
if (composite === undefined) {
|
|
166
169
|
return undefined;
|
|
167
170
|
}
|
|
171
|
+
const subject = composite.name === undefined ? 'project block' : `composite release '${composite.name}'`;
|
|
168
172
|
if (rootPackage === undefined || !rootPackage.exists) {
|
|
169
|
-
throw new Error(
|
|
173
|
+
throw new Error(`${subject} requires a root ${ROOT_PACKAGE_JSON_PATH}; create one with a 'version' field at the repo root`);
|
|
170
174
|
}
|
|
171
175
|
if (rootPackage.version === undefined) {
|
|
172
|
-
throw new Error(
|
|
176
|
+
throw new Error(`${subject} requires root ${ROOT_PACKAGE_JSON_PATH} to have a 'version' field; add a 'version' field to the root package.json`);
|
|
173
177
|
}
|
|
174
178
|
return {
|
|
175
|
-
paths:
|
|
176
|
-
tagPrefix:
|
|
179
|
+
paths: composite.paths ?? workspaces.flatMap((workspace) => workspace.paths),
|
|
180
|
+
tagPrefix: composite.tagPrefix ?? DEFAULT_PROJECT_TAG_PREFIX,
|
|
181
|
+
...(composite.name !== undefined && { name: composite.name }),
|
|
182
|
+
...(composite.members !== undefined && { members: composite.members }),
|
|
177
183
|
};
|
|
178
184
|
}
|
|
179
|
-
function
|
|
185
|
+
function assertNoSharedVersionFiles(workspaces, directories, project) {
|
|
186
|
+
const owners = [
|
|
187
|
+
...directories.map((directory) => ({
|
|
188
|
+
label: `directory release '${directory.name}'`,
|
|
189
|
+
versionFiles: [directory.versionFile],
|
|
190
|
+
})),
|
|
191
|
+
...workspaces.map((workspace) => ({
|
|
192
|
+
label: `workspace '${getTargetName(workspace)}'`,
|
|
193
|
+
versionFiles: workspace.packageFiles.map(normalizeWorkspacePath),
|
|
194
|
+
})),
|
|
195
|
+
...(project === undefined
|
|
196
|
+
? []
|
|
197
|
+
: [
|
|
198
|
+
{
|
|
199
|
+
label: project.name === undefined ? 'the project release' : `composite release '${project.name}'`,
|
|
200
|
+
versionFiles: [ROOT_PACKAGE_JSON_PATH],
|
|
201
|
+
},
|
|
202
|
+
]),
|
|
203
|
+
];
|
|
204
|
+
for (const [index, directory] of directories.entries()) {
|
|
205
|
+
const peer = owners.slice(index + 1).find((owner) => owner.versionFiles.includes(directory.versionFile));
|
|
206
|
+
if (peer !== undefined) {
|
|
207
|
+
throw new Error(`directory release '${directory.name}' and ${peer.label} share the version file '${directory.versionFile}', ` +
|
|
208
|
+
"so each release would overwrite the other's version and changelog; declare only one of them");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
function assertNoTagPrefixCollisions(workspaces, directories, retiredPackages, project) {
|
|
180
213
|
const sources = [];
|
|
181
214
|
for (const workspace of workspaces) {
|
|
182
215
|
const owner = `ws:${workspace.dir}`;
|
|
@@ -190,6 +223,18 @@ function assertNoTagPrefixCollisions(workspaces, retiredPackages, project) {
|
|
|
190
223
|
});
|
|
191
224
|
}
|
|
192
225
|
}
|
|
226
|
+
for (const directory of directories) {
|
|
227
|
+
const owner = `dir:${directory.path}`;
|
|
228
|
+
sources.push({ prefix: directory.tagPrefix, label: `directory release '${directory.name}'`, owner });
|
|
229
|
+
const legacyIdentities = directory.legacyIdentities ?? [];
|
|
230
|
+
for (const identity of legacyIdentities) {
|
|
231
|
+
sources.push({
|
|
232
|
+
prefix: identity.tagPrefix,
|
|
233
|
+
label: `directory release '${directory.name}' legacyIdentities entry (name='${identity.name}')`,
|
|
234
|
+
owner,
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
193
238
|
const retiredEntries = (retiredPackages ?? []).entries();
|
|
194
239
|
for (const [index, retired] of retiredEntries) {
|
|
195
240
|
sources.push({
|
|
@@ -199,7 +244,11 @@ function assertNoTagPrefixCollisions(workspaces, retiredPackages, project) {
|
|
|
199
244
|
});
|
|
200
245
|
}
|
|
201
246
|
if (project !== undefined) {
|
|
202
|
-
sources.push({
|
|
247
|
+
sources.push({
|
|
248
|
+
prefix: project.tagPrefix,
|
|
249
|
+
label: project.name === undefined ? 'project' : `composite release '${project.name}'`,
|
|
250
|
+
owner: 'project',
|
|
251
|
+
});
|
|
203
252
|
}
|
|
204
253
|
for (let i = 0; i < sources.length; i++) {
|
|
205
254
|
for (let j = i + 1; j < sources.length; j++) {
|
|
@@ -49,7 +49,7 @@ export declare function parseArgs(argv: string[]): {
|
|
|
49
49
|
};
|
|
50
50
|
/**
|
|
51
51
|
* Orchestrates the CLI `prepare` command: checks the working tree, loads and validates the config, discovers the
|
|
52
|
-
* workspaces, and prepares the release
|
|
52
|
+
* workspaces, and prepares the release over the targets of the repo shape.
|
|
53
53
|
*
|
|
54
54
|
* A relative `--config` resolves against `invocationDir`.
|
|
55
55
|
*/
|
|
@@ -7,14 +7,13 @@ import { configFlagSchema } from "./configFlagSchema.js";
|
|
|
7
7
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
8
8
|
import { dim } from "./format.js";
|
|
9
9
|
import { RELEASE_GLYPHS } from "./glyphs.js";
|
|
10
|
-
import { mergeMonorepoConfig, mergeSinglePackageConfig, readRootPackageVersion } from "./loadConfig.js";
|
|
11
10
|
import { loadValidatedConfig, reportConfigProblem, reportConfigWarnings } from "./loadValidatedConfig.js";
|
|
12
11
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE } from "./releaseFiles.js";
|
|
13
12
|
import { applyReleasePlan } from "./releasePlan.js";
|
|
14
|
-
import {
|
|
15
|
-
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
13
|
+
import { releasePrepareTargets } from "./releasePrepareTargets.js";
|
|
16
14
|
import { reportPrepare } from "./reportPrepare.js";
|
|
17
15
|
import { resolveConfigFlag } from "./resolveConfigFlag.js";
|
|
16
|
+
import { isSinglePackageTargets, PROJECT_TARGET_ID, resolveReleaseTargets, SINGLE_PACKAGE_TARGET_ID, } from "./resolveReleaseTargets.js";
|
|
18
17
|
const VALID_BUMP_TYPES = ['major', 'minor', 'patch'];
|
|
19
18
|
const CANONICAL_SEMVER_PATTERN = /^\d+\.\d+\.\d+$/;
|
|
20
19
|
function isReleaseType(value) {
|
|
@@ -100,68 +99,70 @@ export async function prepareCommand(argv, styles, invocationDir) {
|
|
|
100
99
|
reportError(`No workspace package to release. ${describeEmptyWorkspace(workspace)}`);
|
|
101
100
|
process.exit(1);
|
|
102
101
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function runSinglePackageMode(userConfig, options, only, setVersion, dryRun, style) {
|
|
111
|
-
if (only !== undefined) {
|
|
112
|
-
reportError('--only is only supported for monorepo configurations');
|
|
113
|
-
process.exit(1);
|
|
114
|
-
}
|
|
115
|
-
if (setVersion !== undefined && !CANONICAL_SEMVER_PATTERN.test(setVersion)) {
|
|
116
|
-
reportError(`Invalid --set-version value "${setVersion}". Must be canonical semver (N.N.N, no pre-release suffix).`);
|
|
117
|
-
process.exit(1);
|
|
118
|
-
}
|
|
119
|
-
const config = mergeSinglePackageConfig(userConfig);
|
|
102
|
+
const config = resolveTargetsOrExit(userConfig, workspace, only, setVersion);
|
|
103
|
+
const targetOptions = {
|
|
104
|
+
...options,
|
|
105
|
+
...(only !== undefined && { only }),
|
|
106
|
+
...(setVersion !== undefined && { setVersions: resolveSetVersions(setVersion, config.targets) }),
|
|
107
|
+
};
|
|
120
108
|
runAndReport(() => {
|
|
121
|
-
const plan =
|
|
122
|
-
assertReleasesOnNpm(plan
|
|
109
|
+
const plan = releasePrepareTargets(config, targetOptions);
|
|
110
|
+
assertReleasesOnNpm(findReleasedPackagePaths(plan, config.targets));
|
|
123
111
|
return plan;
|
|
124
|
-
}, dryRun,
|
|
112
|
+
}, dryRun, styles.stdout);
|
|
113
|
+
}
|
|
114
|
+
function findReleasedPackagePaths(plan, targets) {
|
|
115
|
+
const releasedIds = new Set(plan.targets.flatMap((result) => result.status === 'released' && result.kind === 'package' ? [result.targetId] : []));
|
|
116
|
+
return targets.flatMap((target) => target.kind === 'package' && releasedIds.has(target.id) ? [target.packagePath] : []);
|
|
125
117
|
}
|
|
126
|
-
function
|
|
118
|
+
function resolveTargetsOrExit(userConfig, discovery, only, setVersion) {
|
|
119
|
+
if (discovery.kind === 'single-package') {
|
|
120
|
+
if (only !== undefined) {
|
|
121
|
+
reportError('--only is only supported for monorepo configurations');
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
if (setVersion !== undefined && !CANONICAL_SEMVER_PATTERN.test(setVersion)) {
|
|
125
|
+
reportError(`Invalid --set-version value "${setVersion}". Must be canonical semver (N.N.N, no pre-release suffix).`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
return resolveReleaseTargets(userConfig, discovery);
|
|
129
|
+
}
|
|
127
130
|
let config;
|
|
128
131
|
try {
|
|
129
|
-
|
|
130
|
-
config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
|
|
132
|
+
config = resolveReleaseTargets(userConfig, discovery);
|
|
131
133
|
}
|
|
132
134
|
catch (error) {
|
|
133
135
|
reportError(`Failed to resolve workspaces: ${describeError(error)}`);
|
|
134
136
|
process.exit(1);
|
|
135
137
|
}
|
|
136
|
-
if (setVersion !== undefined && config.
|
|
138
|
+
if (setVersion !== undefined && config.targets.some((target) => target.kind === 'composite')) {
|
|
137
139
|
reportError('--set-version cannot be combined with a project release. ' +
|
|
138
140
|
"--set-version sets individual workspaces' versions; a project release rolls up every " +
|
|
139
|
-
'contributing workspace. To use --set-version, run on a config without a `project` block.');
|
|
141
|
+
'contributing workspace. To use --set-version, run on a config without a `project` block or a `releases` composite.');
|
|
140
142
|
process.exit(1);
|
|
141
143
|
}
|
|
142
|
-
const knownNames = config.
|
|
144
|
+
const knownNames = listSelectableIds(config.targets);
|
|
143
145
|
const onlyNames = only ?? [];
|
|
144
146
|
for (const name of onlyNames) {
|
|
145
147
|
if (knownNames.includes(name)) {
|
|
146
148
|
continue;
|
|
147
149
|
}
|
|
148
|
-
reportError(`Unknown
|
|
150
|
+
reportError(`Unknown release target "${name}". Known targets: ${knownNames.join(', ')}`);
|
|
149
151
|
process.exit(1);
|
|
150
152
|
}
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
153
|
+
return config;
|
|
154
|
+
}
|
|
155
|
+
function resolveSetVersions(setVersion, targets) {
|
|
156
|
+
if (isSinglePackageTargets(targets)) {
|
|
157
|
+
return new Map([[SINGLE_PACKAGE_TARGET_ID, setVersion]]);
|
|
158
|
+
}
|
|
159
|
+
return parseSetVersions(setVersion, listMemberIds(targets));
|
|
160
|
+
}
|
|
161
|
+
function listSelectableIds(targets) {
|
|
162
|
+
return targets.flatMap((target) => (target.id === PROJECT_TARGET_ID ? [] : [target.id]));
|
|
161
163
|
}
|
|
162
|
-
function
|
|
163
|
-
|
|
164
|
-
return workspaces.filter((workspace) => releasedDirs.has(workspace.dir)).map((workspace) => workspace.workspacePath);
|
|
164
|
+
function listMemberIds(targets) {
|
|
165
|
+
return targets.flatMap((target) => (target.kind === 'composite' ? [] : [target.id]));
|
|
165
166
|
}
|
|
166
167
|
function parseSetVersions(value, knownNames) {
|
|
167
168
|
const setVersions = new Map();
|
|
@@ -170,16 +171,16 @@ function parseSetVersions(value, knownNames) {
|
|
|
170
171
|
const name = entry.slice(0, Math.max(0, separatorIndex));
|
|
171
172
|
const version = entry.slice(separatorIndex + 1);
|
|
172
173
|
if (separatorIndex <= 0 || !CANONICAL_SEMVER_PATTERN.test(version)) {
|
|
173
|
-
reportError(`Invalid --set-version entry "${entry}". In monorepo mode, each entry must be <
|
|
174
|
+
reportError(`Invalid --set-version entry "${entry}". In monorepo mode, each entry must be <target>@N.N.N ` +
|
|
174
175
|
'(canonical semver, no pre-release suffix), e.g. --set-version=arrays@1.0.0.');
|
|
175
176
|
process.exit(1);
|
|
176
177
|
}
|
|
177
178
|
if (!knownNames.includes(name)) {
|
|
178
|
-
reportError(`Unknown
|
|
179
|
+
reportError(`Unknown release target "${name}" in --set-version. Known targets: ${knownNames.join(', ')}`);
|
|
179
180
|
process.exit(1);
|
|
180
181
|
}
|
|
181
182
|
if (setVersions.has(name)) {
|
|
182
|
-
reportError(`--set-version names
|
|
183
|
+
reportError(`--set-version names target "${name}" more than once`);
|
|
183
184
|
process.exit(1);
|
|
184
185
|
}
|
|
185
186
|
setVersions.set(name, version);
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { type UntaggedBaseline } from './assertTaggedBaseline.js';
|
|
2
|
+
import { type ReleaseHistory } from './buildChangelogEntries.js';
|
|
3
|
+
import { type OverrideContext } from './changelogOverrides.js';
|
|
4
|
+
import { type GenerateChangelogOptions } from './generateChangelogs.js';
|
|
5
|
+
import type { PlannedWrite } from './releasePlan.js';
|
|
6
|
+
import type { DirectoryTarget, PropagationSource, ReleasedTargetResult, ReleaseSettings, ReleaseTarget, ReleaseType, TargetPrepareResult } from './types.js';
|
|
7
|
+
/** The shared settings that the prepare step reads, which do not include the release-notes settings. */
|
|
8
|
+
export type TargetPrepareSettings = Omit<ReleaseSettings, 'releaseNotes'>;
|
|
9
|
+
/** One read of a target's release windows, and the untagged baseline that it found, if any. */
|
|
10
|
+
export interface TargetReading {
|
|
11
|
+
history: ReleaseHistory;
|
|
12
|
+
untagged: UntaggedBaseline | undefined;
|
|
13
|
+
}
|
|
14
|
+
/** Outcome of {@link decideTargetRelease}: a release at a decided level or at an explicit version, or a skip. */
|
|
15
|
+
export type TargetDecision = {
|
|
16
|
+
outcome: 'release';
|
|
17
|
+
releaseType: ReleaseType;
|
|
18
|
+
bumpOverride: ReleaseType | undefined;
|
|
19
|
+
setVersion: undefined;
|
|
20
|
+
} | {
|
|
21
|
+
outcome: 'release';
|
|
22
|
+
releaseType: undefined;
|
|
23
|
+
bumpOverride: undefined;
|
|
24
|
+
setVersion: string;
|
|
25
|
+
} | {
|
|
26
|
+
outcome: 'skip';
|
|
27
|
+
skipReason: string;
|
|
28
|
+
};
|
|
29
|
+
/** The release level that a target takes, whether its own commits or a dependency's bump decided it. */
|
|
30
|
+
export interface TargetRelease {
|
|
31
|
+
releaseType: ReleaseType;
|
|
32
|
+
/** Dependencies whose bumps this release records; present for a propagated or mixed release. */
|
|
33
|
+
propagatedFrom?: PropagationSource[];
|
|
34
|
+
}
|
|
35
|
+
/** Inputs to {@link planTargetRelease}. */
|
|
36
|
+
export interface PlanTargetReleaseArgs {
|
|
37
|
+
target: ReleaseTarget;
|
|
38
|
+
settings: TargetPrepareSettings;
|
|
39
|
+
/** The level of the release and, for a propagated release, the dependencies that set it. */
|
|
40
|
+
release: TargetRelease;
|
|
41
|
+
/**
|
|
42
|
+
* The decision behind a direct release; undefined for a release that propagation alone set, whose own window called
|
|
43
|
+
* for none.
|
|
44
|
+
*/
|
|
45
|
+
decision: Extract<TargetDecision, {
|
|
46
|
+
outcome: 'release';
|
|
47
|
+
}> | undefined;
|
|
48
|
+
/** The window that the target's read produced. */
|
|
49
|
+
history: ReleaseHistory;
|
|
50
|
+
overrideContext: OverrideContext;
|
|
51
|
+
/** Section titles in priority order. */
|
|
52
|
+
sectionOrder: string[];
|
|
53
|
+
/** Header of the section under which a propagation-only entry lists its dependencies. */
|
|
54
|
+
dependenciesHeader: string;
|
|
55
|
+
/** True when release-notes previews are requested and `changelogJson.enabled` is true. */
|
|
56
|
+
shouldPlanPreviews: boolean;
|
|
57
|
+
/** The release date, as `YYYY-MM-DD`. */
|
|
58
|
+
today: string;
|
|
59
|
+
}
|
|
60
|
+
/** A target's planned release: its result and the tag, writes, and modified files that the run collects. */
|
|
61
|
+
export interface PlannedTargetRelease {
|
|
62
|
+
result: ReleasedTargetResult;
|
|
63
|
+
tag: string;
|
|
64
|
+
writes: PlannedWrite[];
|
|
65
|
+
/** Archive files of the target's `CHANGELOG.md` that the release removes. */
|
|
66
|
+
removals: string[];
|
|
67
|
+
/**
|
|
68
|
+
* Files that the format command covers: the version files, the `CHANGELOG.md` and its archive files, and any
|
|
69
|
+
* changelog JSON.
|
|
70
|
+
*/
|
|
71
|
+
modifiedFiles: string[];
|
|
72
|
+
/** Warnings raised while planning the release-notes previews. */
|
|
73
|
+
warnings: string[];
|
|
74
|
+
}
|
|
75
|
+
/** Reads a target's release windows under its tag prefixes and commit window, and finds its untagged baseline. */
|
|
76
|
+
export declare function readTargetHistory(target: ReleaseTarget, settings: TargetPrepareSettings): TargetReading;
|
|
77
|
+
/**
|
|
78
|
+
* Returns the bounds of a target's history read: its current and legacy tag prefixes, its commit window, and, for a
|
|
79
|
+
* workspace or a directory other than the repo root, the scope to which change-record entries route, which is its name.
|
|
80
|
+
*/
|
|
81
|
+
export declare function describeHistoryWindow(target: ReleaseTarget): GenerateChangelogOptions;
|
|
82
|
+
/**
|
|
83
|
+
* Decides a target's direct release from its window: `setVersion` releases at that version, and otherwise the
|
|
84
|
+
* window's bump, `force`, and `bumpOverride` decide as {@link decideRelease} states.
|
|
85
|
+
*/
|
|
86
|
+
export declare function decideTargetRelease(target: ReleaseTarget, history: ReleaseHistory, options: {
|
|
87
|
+
force: boolean;
|
|
88
|
+
bumpOverride: ReleaseType | undefined;
|
|
89
|
+
setVersion: string | undefined;
|
|
90
|
+
}): TargetDecision;
|
|
91
|
+
/**
|
|
92
|
+
* Plans a target's release: the version bump, the changelogs, and the release-notes previews, and builds its result.
|
|
93
|
+
*
|
|
94
|
+
* The changelog entries come from the target's release windows, with the synthetic "Forced version bump." entry for a
|
|
95
|
+
* window that doesn't yield any item, or, for a release that propagation alone set, from one synthetic entry listing
|
|
96
|
+
* the dependencies. Overrides apply before `changelog.json` and `CHANGELOG.md` render from the merged set, and
|
|
97
|
+
* `CHANGELOG.md` keeps the existing sections whose versions the merged set lacks.
|
|
98
|
+
*/
|
|
99
|
+
export declare function planTargetRelease(args: PlanTargetReleaseArgs): PlannedTargetRelease;
|
|
100
|
+
/** Builds the result of a target that skips, from the window that its read produced. */
|
|
101
|
+
export declare function buildSkippedTargetResult(target: ReleaseTarget, history: ReleaseHistory, skipReason: string): TargetPrepareResult;
|
|
102
|
+
/** Reports whether a directory target is the repo root, which reads the whole repo and renders every entry. */
|
|
103
|
+
export declare function isRepoRoot(target: DirectoryTarget): boolean;
|