@williamthorsen/release-kit 11.0.0 → 13.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 +274 -2
- package/.readyup/kits/default.js +18 -33
- package/.readyup/kits/npm-auto-publish.js +24 -23
- package/.readyup/manifest.json +16 -11
- package/CHANGELOG.md +98 -0
- package/README.md +57 -69
- package/bin/release-kit.js +1 -1
- package/dist/esm/assertCleanWorkingTree.d.ts +1 -5
- package/dist/esm/assertTaggedBaseline.d.ts +29 -0
- package/dist/esm/assertTaggedBaseline.js +41 -0
- package/dist/esm/attachChangelogDiagnostics.d.ts +4 -0
- package/dist/esm/attachChangelogDiagnostics.js +14 -0
- package/dist/esm/bin/release-kit.js +88 -24
- package/dist/esm/buildChangelogEntries.d.ts +69 -17
- package/dist/esm/buildChangelogEntries.js +222 -120
- package/dist/esm/buildDependencyGraph.d.ts +6 -6
- package/dist/esm/buildEmptyReleaseEntry.d.ts +1 -8
- package/dist/esm/buildReleaseSummary.d.ts +2 -8
- package/dist/esm/buildReleaseSummary.js +2 -2
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +3 -5
- package/dist/esm/buildSyntheticChangelogEntry.js +2 -2
- package/dist/esm/bumpVersion.d.ts +1 -8
- package/dist/esm/changelogJsonFile.d.ts +5 -7
- package/dist/esm/changelogJsonFile.js +2 -17
- package/dist/esm/changelogJsonUtils.d.ts +7 -10
- package/dist/esm/changelogOverrides.d.ts +79 -100
- package/dist/esm/changelogOverrides.js +138 -58
- package/dist/esm/classifyChangelogCommit.d.ts +36 -0
- package/dist/esm/classifyChangelogCommit.js +34 -0
- package/dist/esm/commitCommand.d.ts +2 -4
- package/dist/esm/compareVersions.d.ts +10 -4
- package/dist/esm/compareVersions.js +16 -0
- package/dist/esm/configFlagSchema.d.ts +1 -1
- package/dist/esm/createGithubRelease.d.ts +7 -19
- package/dist/esm/createGithubReleaseCommand.d.ts +5 -5
- package/dist/esm/createGithubReleaseCommand.js +9 -9
- package/dist/esm/createTags.d.ts +2 -3
- package/dist/esm/decideRelease.d.ts +11 -41
- package/dist/esm/decideRelease.js +5 -27
- package/dist/esm/defaults.d.ts +5 -24
- package/dist/esm/defaults.js +7 -5
- package/dist/esm/defineConfig.d.ts +1 -1
- package/dist/esm/deleteFileIfExists.d.ts +1 -1
- package/dist/esm/deriveReleaseNotesConfig.d.ts +20 -0
- package/dist/esm/deriveReleaseNotesConfig.js +21 -0
- package/dist/esm/deriveWorkspaceConfig.d.ts +3 -5
- package/dist/esm/detectPackageManager.d.ts +2 -4
- package/dist/esm/detectUndeclaredTagPrefixes.d.ts +4 -6
- package/dist/esm/detectUndeclaredTagPrefixes.js +1 -1
- package/dist/esm/determineBumpType.d.ts +11 -11
- package/dist/esm/determineBumpType.js +6 -7
- package/dist/esm/discoverWorkspaces.d.ts +14 -15
- package/dist/esm/discoverWorkspaces.js +12 -12
- package/dist/esm/enterRepoRoot.d.ts +12 -0
- package/dist/esm/enterRepoRoot.js +16 -0
- package/dist/esm/enumerateReleaseWindows.d.ts +50 -0
- package/dist/esm/enumerateReleaseWindows.js +124 -0
- package/dist/esm/extractMigration.d.ts +6 -8
- package/dist/esm/findUnroutedEntryScopes.d.ts +16 -0
- package/dist/esm/findUnroutedEntryScopes.js +57 -0
- package/dist/esm/formatExcludedSkip.d.ts +3 -0
- package/dist/esm/formatExcludedSkip.js +3 -0
- package/dist/esm/formatPrivateSkip.d.ts +1 -6
- package/dist/esm/generateChangelogs.d.ts +13 -26
- package/dist/esm/generateChangelogs.js +0 -14
- package/dist/esm/glyphs.d.ts +2 -2
- package/dist/esm/help/prepareHelp.d.ts +3 -4
- package/dist/esm/help/prepareHelp.js +7 -8
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/init/checks.d.ts +3 -3
- package/dist/esm/init/checks.js +4 -1
- package/dist/esm/init/detectRepoType.d.ts +2 -3
- package/dist/esm/init/hasProvenance.d.ts +2 -0
- package/dist/esm/init/hasProvenance.js +3 -0
- package/dist/esm/init/initCommand.d.ts +3 -6
- package/dist/esm/init/initCommand.js +11 -11
- package/dist/esm/init/parseJsonRecord.d.ts +1 -4
- package/dist/esm/init/scaffold.d.ts +1 -3
- package/dist/esm/init/scaffold.js +2 -30
- package/dist/esm/init/templates.d.ts +9 -11
- package/dist/esm/init/templates.js +12 -11
- package/dist/esm/init/updateTemplatesCommand.d.ts +13 -0
- package/dist/esm/init/updateTemplatesCommand.js +64 -0
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +12 -17
- package/dist/esm/injectSection.d.ts +3 -3
- package/dist/esm/loadConfig.d.ts +16 -14
- package/dist/esm/loadConfig.js +2 -5
- package/dist/esm/loadValidatedConfig.d.ts +16 -19
- package/dist/esm/loadValidatedConfig.js +6 -1
- package/dist/esm/packsPath.d.ts +9 -9
- package/dist/esm/parseChangeRecordBlock.d.ts +37 -0
- package/dist/esm/parseChangeRecordBlock.js +134 -0
- package/dist/esm/parseCommitMessage.d.ts +42 -32
- package/dist/esm/parseCommitMessage.js +33 -41
- package/dist/esm/parseRequestedTags.d.ts +3 -3
- package/dist/esm/planReleaseNotesPreviews.d.ts +6 -6
- package/dist/esm/planVersionBump.d.ts +5 -3
- package/dist/esm/planVersionBump.js +3 -0
- package/dist/esm/prepareCommand.d.ts +4 -7
- package/dist/esm/prepareCommand.js +17 -18
- package/dist/esm/previewTagPrefixes.d.ts +8 -10
- package/dist/esm/propagateBumps.d.ts +6 -7
- package/dist/esm/publish.d.ts +1 -1
- package/dist/esm/publishCommand.d.ts +4 -3
- package/dist/esm/publishCommand.js +8 -8
- package/dist/esm/pushCommand.d.ts +4 -3
- package/dist/esm/pushCommand.js +8 -2
- package/dist/esm/pushRelease.d.ts +2 -2
- package/dist/esm/readChangelogSections.d.ts +33 -0
- package/dist/esm/readChangelogSections.js +80 -0
- package/dist/esm/releaseFiles.d.ts +5 -7
- package/dist/esm/releasePlan.d.ts +11 -13
- package/dist/esm/releasePrepare.d.ts +25 -25
- package/dist/esm/releasePrepare.js +80 -80
- package/dist/esm/releasePrepareMono.d.ts +9 -7
- package/dist/esm/releasePrepareMono.js +114 -99
- package/dist/esm/releasePrepareProject.d.ts +19 -29
- package/dist/esm/releasePrepareProject.js +46 -45
- package/dist/esm/renderChangelogMarkdown.d.ts +10 -4
- package/dist/esm/renderChangelogMarkdown.js +27 -2
- package/dist/esm/renderReleaseNotes.d.ts +3 -11
- package/dist/esm/renderReleaseNotes.js +2 -6
- package/dist/esm/reportPrepare.d.ts +4 -5
- package/dist/esm/reportPrepare.js +67 -42
- package/dist/esm/resolveCommandTags.d.ts +15 -11
- package/dist/esm/resolveCommandTags.js +35 -5
- package/dist/esm/resolveConfigFlag.d.ts +7 -0
- package/dist/esm/resolveConfigFlag.js +4 -0
- package/dist/esm/resolveReleaseTags.d.ts +6 -8
- package/dist/esm/resolveReleaseTags.js +1 -1
- package/dist/esm/resolveStylesOrExit.d.ts +2 -2
- package/dist/esm/showTagPrefixesCommand.d.ts +7 -10
- package/dist/esm/showTagPrefixesCommand.js +4 -4
- package/dist/esm/stripEmojiPrefix.d.ts +1 -0
- package/dist/esm/stripScope.d.ts +2 -2
- package/dist/esm/stripScope.js +3 -3
- package/dist/esm/sync-labels/generateCommand.d.ts +11 -10
- package/dist/esm/sync-labels/generateCommand.js +11 -8
- package/dist/esm/sync-labels/initCommand.d.ts +6 -7
- package/dist/esm/sync-labels/initCommand.js +2 -2
- package/dist/esm/sync-labels/presets.d.ts +5 -4
- package/dist/esm/sync-labels/presets.js +9 -9
- package/dist/esm/sync-labels/resolveLabels.d.ts +7 -7
- package/dist/esm/sync-labels/resolveLabels.js +1 -1
- package/dist/esm/sync-labels/retiredConfig.d.ts +4 -5
- package/dist/esm/sync-labels/syncCommand.d.ts +1 -4
- package/dist/esm/sync-labels/templates.d.ts +3 -3
- package/dist/esm/sync-labels/templates.js +3 -3
- package/dist/esm/sync-labels/typeLabels.d.ts +14 -0
- package/dist/esm/sync-labels/typeLabels.js +38 -0
- package/dist/esm/sync-labels/types.d.ts +1 -1
- package/dist/esm/tagCommand.d.ts +1 -1
- package/dist/esm/typeGuards.d.ts +2 -2
- package/dist/esm/types.d.ts +185 -116
- package/dist/esm/types.js +2 -2
- package/dist/esm/validateConfig.d.ts +2 -11
- package/dist/esm/validateConfig.js +5 -1
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -12
- package/dist/esm/validateOverridesCommand.d.ts +19 -21
- package/dist/esm/validateOverridesCommand.js +16 -23
- package/docs/api.md +6 -6
- package/docs/changelogs.md +90 -28
- package/docs/configuration.md +30 -29
- package/docs/editorial-overrides.md +90 -31
- package/docs/labels.md +21 -19
- package/docs/project-releases.md +14 -14
- package/docs/publishing.md +5 -5
- package/docs/readiness-checks.md +3 -3
- package/docs/releasing.md +54 -24
- package/docs/work-types.md +46 -58
- package/package.json +7 -7
- package/presets/labels/common.yaml +1 -41
- 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/resolveReleaseNotesConfig.d.ts +0 -26
- package/dist/esm/resolveReleaseNotesConfig.js +0 -29
- package/dist/esm/runGitCliff.d.ts +0 -38
- package/dist/esm/runGitCliff.js +0 -25
- package/dist/esm/workTypesData.d.ts +0 -50
- package/dist/esm/workTypesData.js +0 -145
- package/schemas/label-map.json +0 -24
|
@@ -30,8 +30,12 @@ function preprocessDeprecatedKeys(raw) {
|
|
|
30
30
|
return { cleaned: raw, deprecationErrors: [] };
|
|
31
31
|
const errors = [];
|
|
32
32
|
const cleaned = { ...raw };
|
|
33
|
+
if (Object.hasOwn(cleaned, 'cliffConfigPath')) {
|
|
34
|
+
errors.push('cliffConfigPath is no longer supported. release-kit reads changelog history from git directly and does not use any external config file. Remove this field from the config, and delete the file that it names.');
|
|
35
|
+
delete cleaned['cliffConfigPath'];
|
|
36
|
+
}
|
|
33
37
|
if (isRecord(cleaned['releaseNotes']) && Object.hasOwn(cleaned['releaseNotes'], 'shouldCreateGithubRelease')) {
|
|
34
|
-
errors.push('releaseNotes.shouldCreateGithubRelease is no longer supported. Adoption is now signaled by installing the create-github-release workflow, which `release-kit init` scaffolds. Remove this field from
|
|
38
|
+
errors.push('releaseNotes.shouldCreateGithubRelease is no longer supported. Adoption is now signaled by installing the create-github-release workflow, which `release-kit init` scaffolds. Remove this field from the config.');
|
|
35
39
|
const releaseNotesCopy = { ...cleaned['releaseNotes'] };
|
|
36
40
|
delete releaseNotesCopy['shouldCreateGithubRelease'];
|
|
37
41
|
cleaned['releaseNotes'] = releaseNotesCopy;
|
|
@@ -4,32 +4,34 @@ import type { WorkspaceConfig } from './types.js';
|
|
|
4
4
|
export interface CommitsProbeResult {
|
|
5
5
|
/** Whether the workspace has any commits since its last tag (excluding `release:` commits). */
|
|
6
6
|
has: boolean;
|
|
7
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* The baseline tag from which the commits were measured; `undefined` when the workspace does not have a prior tag.
|
|
9
|
+
*/
|
|
8
10
|
tag: string | undefined;
|
|
9
11
|
}
|
|
10
12
|
/** A single excluded workspace whose changes would be stranded by the `--only` invocation. */
|
|
11
13
|
export interface StrandedDependentViolation {
|
|
12
14
|
/** The excluded workspace's `dir`. */
|
|
13
15
|
dir: string;
|
|
14
|
-
/** The
|
|
16
|
+
/** The `dir` of the released (or anticipated-release) dependency whose release republishes this workspace. */
|
|
15
17
|
downstreamOf: string;
|
|
16
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* The baseline tag from which this workspace's commits were counted; `undefined` if the workspace does not have a
|
|
20
|
+
* prior tag.
|
|
21
|
+
*/
|
|
17
22
|
tag: string | undefined;
|
|
18
23
|
}
|
|
19
24
|
/** Probe callback that returns whether a workspace has commits since its last tag. */
|
|
20
25
|
type CommitsProbe = (workspace: WorkspaceConfig) => CommitsProbeResult;
|
|
21
26
|
/**
|
|
22
|
-
*
|
|
27
|
+
* Detects `--only` invocations that would silently strand changes in excluded internal dependents.
|
|
23
28
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* continues *through* such workspaces (anticipating that the user will add them to `--only`,
|
|
29
|
-
* surfacing deeper footguns in one pass), but stops at excluded workspaces with no commits
|
|
30
|
-
* (case-3 barriers — they don't republish, so their downstream dependents aren't affected by R).
|
|
29
|
+
* First computes R, the `--only` workspaces that will release, then walks the reverse-dependency graph breadth-first
|
|
30
|
+
* from R and records each excluded workspace with its own commits as a violation. The walk continues through a
|
|
31
|
+
* violation, as though the user had added it to `--only`, which reports deeper violations in the same pass. It stops
|
|
32
|
+
* at an excluded workspace without commits, which does not republish, so its dependents are unaffected by R.
|
|
31
33
|
*
|
|
32
|
-
* Returns `undefined` when
|
|
34
|
+
* Returns `undefined` when the walk does not find any violation, or the violations sorted by `dir`.
|
|
33
35
|
*/
|
|
34
36
|
export declare function validateOnlyExcludesStrandedDependents(workspaces: readonly WorkspaceConfig[], only: readonly string[], graph: DependencyGraph, hasCommits: CommitsProbe): StrandedDependentViolation[] | undefined;
|
|
35
37
|
export {};
|
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
import { type OutputStyle, type StreamStyles } from '@williamthorsen/nmr-core';
|
|
2
|
+
import { type ReleaseHistoryConfig } from './buildChangelogEntries.js';
|
|
2
3
|
import { type ValidateAllChangelogOverridesInputs, type ValidateAllChangelogOverridesResult } from './changelogOverrides.js';
|
|
3
4
|
import { type WorkspaceDiscovery } from './discoverWorkspaces.js';
|
|
5
|
+
import { type GenerateChangelogOptions } from './generateChangelogs.js';
|
|
4
6
|
import { type LoadValidatedConfigResult } from './loadValidatedConfig.js';
|
|
5
|
-
import type { ChangelogEntry
|
|
7
|
+
import type { ChangelogEntry } from './types.js';
|
|
6
8
|
/**
|
|
7
|
-
* Result of {@link validateOverridesCommand}: tiered exit code paired with a human-readable
|
|
8
|
-
* message, which keeps the CLI dispatch layer thin.
|
|
9
|
+
* Result of {@link validateOverridesCommand}: a tiered exit code paired with a human-readable message.
|
|
9
10
|
*
|
|
10
11
|
* Exit codes:
|
|
11
|
-
* - `0
|
|
12
|
-
* - `1
|
|
13
|
-
* - `2
|
|
12
|
+
* - `0`: clean, with no errors and no warnings.
|
|
13
|
+
* - `1`: only stale-key warnings.
|
|
14
|
+
* - `2`: schema or parse errors, or keys that fail to match (an ambiguous prefix, overlapping keys, or a bare key
|
|
15
|
+
* that sets fields on several items). Errors take precedence when both classes exist.
|
|
14
16
|
*/
|
|
15
17
|
export interface ValidateOverridesCommandResult {
|
|
16
18
|
exitCode: 0 | 1 | 2;
|
|
@@ -21,30 +23,26 @@ export interface ValidateOverridesCommandDependencies {
|
|
|
21
23
|
discoverWorkspaces?: () => WorkspaceDiscovery;
|
|
22
24
|
loadValidatedConfig?: () => Promise<LoadValidatedConfigResult>;
|
|
23
25
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* construction. `tagPattern` and `includePaths` are passed straight through to git-cliff.
|
|
26
|
+
* Builds changelog entries for a scope. Defaults to `buildChangelogEntries`, which `release-kit prepare` also
|
|
27
|
+
* uses, so that `validate` sees the same items as `prepare`.
|
|
27
28
|
*/
|
|
28
|
-
buildEntries?: (config:
|
|
29
|
-
/**
|
|
29
|
+
buildEntries?: (config: ReleaseHistoryConfig, options: GenerateChangelogOptions) => ChangelogEntry[];
|
|
30
|
+
/** Validator; defaults to `validateAllChangelogOverrides`. Tests use it to feed specific results to the formatter. */
|
|
30
31
|
validate?: (inputs: ValidateAllChangelogOverridesInputs) => ValidateAllChangelogOverridesResult;
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
* and per-scope
|
|
34
|
+
* Validates every changelog override file across the project and per-workspace scopes, and
|
|
35
|
+
* returns a tiered exit-code-plus-message result. Performs workspace discovery, config load,
|
|
36
|
+
* and per-scope item collection, then delegates the actual validation to
|
|
36
37
|
* {@link validateAllChangelogOverrides}.
|
|
37
38
|
*
|
|
38
|
-
* Single-package and monorepo modes are handled uniformly:
|
|
39
|
-
* project scope; monorepo expands to a project scope plus one scope per workspace. A workspace
|
|
40
|
-
*
|
|
39
|
+
* Single-package and monorepo modes are handled uniformly: Single-package collapses to one
|
|
40
|
+
* project scope; monorepo expands to a project scope plus one scope per workspace. A workspace that doesn't resolve
|
|
41
|
+
* to any package is neither, and the command returns exit code `2` for it.
|
|
41
42
|
*
|
|
42
43
|
* `configPath` names the config file to read, relative to the working directory; it defaults to
|
|
43
44
|
* `CONFIG_FILE_PATH`.
|
|
44
45
|
*/
|
|
45
46
|
export declare function validateOverridesCommand(styles: StreamStyles, configPath?: string, dependencies?: ValidateOverridesCommandDependencies): Promise<ValidateOverridesCommandResult>;
|
|
46
|
-
/**
|
|
47
|
-
* Pure formatter — take an aggregated validation result, return the tiered exit code and a
|
|
48
|
-
* rendered message. Exported for unit testing without going through the full discovery path.
|
|
49
|
-
*/
|
|
47
|
+
/** Returns the tiered exit code and rendered message for an aggregated validation result. */
|
|
50
48
|
export declare function formatValidateOverridesResult(result: ValidateAllChangelogOverridesResult, style: OutputStyle): ValidateOverridesCommandResult;
|
|
@@ -3,7 +3,7 @@ import { describeError } from '@williamthorsen/toolbelt.errors';
|
|
|
3
3
|
import { buildChangelogEntries } from "./buildChangelogEntries.js";
|
|
4
4
|
import { resolveOverridePath, validateAllChangelogOverrides, } from "./changelogOverrides.js";
|
|
5
5
|
import { describeEmptyWorkspace, discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getAllTagPrefixes } from "./generateChangelogs.js";
|
|
7
7
|
import { mergeMonorepoConfig, mergeSinglePackageConfig, readRootPackageVersion } from "./loadConfig.js";
|
|
8
8
|
import { loadValidatedConfig } from "./loadValidatedConfig.js";
|
|
9
9
|
const SYNTHETIC_VALIDATE_TAG = 'validate-only';
|
|
@@ -68,28 +68,21 @@ function formatSummaryLine(errorCount, warningCount) {
|
|
|
68
68
|
function pluralize(count, noun) {
|
|
69
69
|
return count === 1 ? `${count} ${noun}` : `${count} ${noun}s`;
|
|
70
70
|
}
|
|
71
|
-
function defaultBuildEntries(config,
|
|
72
|
-
|
|
73
|
-
if (tagPattern !== undefined) {
|
|
74
|
-
options.tagPattern = tagPattern;
|
|
75
|
-
}
|
|
76
|
-
if (includePaths !== undefined) {
|
|
77
|
-
options.includePaths = includePaths;
|
|
78
|
-
}
|
|
79
|
-
return buildChangelogEntries(config, SYNTHETIC_VALIDATE_TAG, options);
|
|
71
|
+
function defaultBuildEntries(config, options) {
|
|
72
|
+
return buildChangelogEntries(config, SYNTHETIC_VALIDATE_TAG, options).entries;
|
|
80
73
|
}
|
|
81
|
-
function
|
|
82
|
-
const
|
|
74
|
+
function flattenEntriesToItems(entries) {
|
|
75
|
+
const items = [];
|
|
83
76
|
for (const entry of entries) {
|
|
84
77
|
for (const section of entry.sections) {
|
|
85
|
-
for (const
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
}
|
|
78
|
+
for (const { hash, entry: position } of section.items) {
|
|
79
|
+
if (hash === undefined)
|
|
80
|
+
continue;
|
|
81
|
+
items.push(position === undefined ? { hash } : { hash, entry: position });
|
|
89
82
|
}
|
|
90
83
|
}
|
|
91
84
|
}
|
|
92
|
-
return
|
|
85
|
+
return items;
|
|
93
86
|
}
|
|
94
87
|
function formatConfigProblem(problem) {
|
|
95
88
|
return problem.kind === 'load'
|
|
@@ -98,19 +91,19 @@ function formatConfigProblem(problem) {
|
|
|
98
91
|
}
|
|
99
92
|
function buildSinglePackageInputs(userConfig, buildEntries) {
|
|
100
93
|
const config = mergeSinglePackageConfig(userConfig);
|
|
101
|
-
const
|
|
94
|
+
const items = flattenEntriesToItems(buildEntries(config, { tagPrefixes: [config.tagPrefix] }));
|
|
102
95
|
return {
|
|
103
|
-
project: { filePath: resolveOverridePath('.'),
|
|
96
|
+
project: { filePath: resolveOverridePath('.'), items },
|
|
104
97
|
};
|
|
105
98
|
}
|
|
106
99
|
function buildMonorepoInputs(discoveredPaths, userConfig, buildEntries) {
|
|
107
100
|
const rootPackage = readRootPackageVersion();
|
|
108
101
|
const config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
|
|
109
102
|
const workspaces = config.workspaces.map((workspace) => {
|
|
110
|
-
const
|
|
103
|
+
const options = { tagPrefixes: getAllTagPrefixes(workspace), paths: workspace.paths, workspaceDir: workspace.dir };
|
|
111
104
|
return {
|
|
112
105
|
filePath: resolveOverridePath(workspace.workspacePath),
|
|
113
|
-
|
|
106
|
+
items: flattenEntriesToItems(buildEntries(config, options)),
|
|
114
107
|
};
|
|
115
108
|
});
|
|
116
109
|
const project = config.project;
|
|
@@ -118,8 +111,8 @@ function buildMonorepoInputs(discoveredPaths, userConfig, buildEntries) {
|
|
|
118
111
|
filePath: resolveOverridePath('.'),
|
|
119
112
|
};
|
|
120
113
|
if (project !== undefined) {
|
|
121
|
-
const
|
|
122
|
-
projectScope.
|
|
114
|
+
const options = { tagPrefixes: [project.tagPrefix], paths: project.paths };
|
|
115
|
+
projectScope.items = flattenEntriesToItems(buildEntries(config, options));
|
|
123
116
|
}
|
|
124
117
|
return { project: projectScope, workspaces };
|
|
125
118
|
}
|
package/docs/api.md
CHANGED
|
@@ -4,7 +4,7 @@ How to build a release config by hand with `deriveWorkspaceConfig()`, the script
|
|
|
4
4
|
|
|
5
5
|
## Using `deriveWorkspaceConfig()` for manual configuration
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
For a `MonorepoReleaseConfig` built manually (e.g., for the legacy script-based approach), the exported `deriveWorkspaceConfig()` helper creates a `WorkspaceConfig` from a workspace-relative path. It reads the workspace's `package.json` to derive the tag prefix from the package name:
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
10
|
import { deriveWorkspaceConfig } from '@williamthorsen/release-kit';
|
|
@@ -22,9 +22,9 @@ deriveWorkspaceConfig('packages/arrays');
|
|
|
22
22
|
// }
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
`dir` is the basename of the workspace path and is the stable internal identifier used by `--only`, `WorkspaceOverride.dir`, and the dependency graph. `tagPrefix` is derived from the unscoped `package.json` `name`
|
|
25
|
+
`dir` is the basename of the workspace path and is the stable internal identifier used by `--only`, `WorkspaceOverride.dir`, and the dependency graph. `tagPrefix` is derived from the unscoped `package.json` `name` (with any leading `@scope/` stripped), so tags reflect the package identity rather than the directory layout. For example, a workspace at `packages/core` with `"name": "@williamthorsen/nmr-core"` produces `tagPrefix: 'nmr-core-v'`, yielding tags like `nmr-core-v1.3.0`.
|
|
26
26
|
|
|
27
|
-
The workspace's `package.json` must declare a non-empty `name` field; `deriveWorkspaceConfig()` throws otherwise. If two workspaces produce the same `tagPrefix` (because their unscoped names collide), `mergeMonorepoConfig()` throws and names the colliding workspaces so
|
|
27
|
+
The workspace's `package.json` must declare a non-empty `name` field; `deriveWorkspaceConfig()` throws otherwise. If two workspaces produce the same `tagPrefix` (because their unscoped names collide), `mergeMonorepoConfig()` throws and names the colliding workspaces so that the developer can rename one.
|
|
28
28
|
|
|
29
29
|
## Legacy script-based approach
|
|
30
30
|
|
|
@@ -49,7 +49,7 @@ import { config } from './release.config.ts';
|
|
|
49
49
|
runReleasePrepare(config);
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
The key difference:
|
|
52
|
+
The key difference: The script-based approach requires manually listing every workspace, while the CLI auto-discovers them from `pnpm-workspace.yaml`.
|
|
53
53
|
|
|
54
54
|
## `resolveReleaseTags` takes workspaces; `WorkspaceConfig` requires `workspacePath`
|
|
55
55
|
|
|
@@ -58,7 +58,7 @@ Tag resolution is now driven by workspace records rather than a caller-supplied
|
|
|
58
58
|
- `resolveReleaseTags` signature changed from `(workspaceMap?: Map<string, string>)` to `(workspaces?: readonly WorkspaceConfig[])`.
|
|
59
59
|
- `WorkspaceConfig` gained a required `workspacePath: string` field.
|
|
60
60
|
|
|
61
|
-
Replace direct `Map`-based calls with `deriveWorkspaceConfig()`, which now populates `workspacePath
|
|
61
|
+
Replace direct `Map`-based calls with `deriveWorkspaceConfig()`, which now populates `workspacePath`:
|
|
62
62
|
|
|
63
63
|
```diff
|
|
64
64
|
-import { resolveReleaseTags } from '@williamthorsen/release-kit';
|
|
@@ -70,4 +70,4 @@ Replace direct `Map`-based calls with `deriveWorkspaceConfig()`, which now popul
|
|
|
70
70
|
+resolveReleaseTags([deriveWorkspaceConfig('packages/core')]);
|
|
71
71
|
```
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
When constructing `WorkspaceConfig` objects directly, add `workspacePath` alongside the other required fields.
|
package/docs/changelogs.md
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
# Changelogs
|
|
2
2
|
|
|
3
|
-
What
|
|
3
|
+
What a published tarball includes, what each `.meta/changelog.json` item contains, which commits appear in a changelog, how a merge commit's change-record block becomes items, and how release notes are written into a README.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
npm doesn't include either `CHANGELOG.md` or `.meta/changelog.json` in a published tarball on its own. npm stopped including `CHANGELOG` automatically in npm 7, so a package that declares a `files` field publishes a changelog only when 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`
|
|
9
|
+
Each item under a section in `.meta/changelog.json` has 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`
|
|
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` when the commit subject had 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:`.
|
|
24
|
+
A commit body states a migration step as a paragraph opening with the literal label `Migration:`. When one is present, `migration` contains that paragraph with the label stripped. An item derived from a change-record entry takes `migration` from the entry instead, and doesn't have a `body`.
|
|
25
|
+
|
|
26
|
+
A title-derived item looks like this:
|
|
24
27
|
|
|
25
28
|
```jsonc
|
|
26
29
|
{
|
|
@@ -32,32 +35,91 @@ A commit body states a migration step as a paragraph opening with the literal la
|
|
|
32
35
|
|
|
33
36
|
Three properties are worth knowing:
|
|
34
37
|
|
|
35
|
-
- **It is independent of `breaking`.** A `deprecate` cannot
|
|
38
|
+
- **It is independent of `breaking`.** A `deprecate` cannot have `!` 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 that an override file can set (see [File shape](editorial-overrides.md#file-shape)). On a title-derived item, overriding `body` changes the migration text: release-kit re-derives `migration` from whatever `body` an override installs, and clears it when that body doesn't contain a labeled paragraph. 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
|
+
## Which commits appear in 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 appears in 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 appear in a changelog, whether or not they contain a block. A commit that fails a check doesn't yield an item or raise a bump, so a window without an item doesn't call for a release. A release forced by `--force` or `--set-version` whose window doesn't yield an item records a "Forced version bump." entry under Notes for its version, whether or not the window has commits. Because history does not yield that entry again, `release-kit prepare` keeps it by merging each release's entries with the `changelog.json` on disk. 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
|
+
### Existing `CHANGELOG.md` sections
|
|
57
|
+
|
|
58
|
+
`prepare` also reads the `CHANGELOG.md` that it regenerates, so a version recorded only there, such as history written by hand before release-kit was adopted, survives. The first SemVer token in a `##` heading names the section's version: `## 1.2.0`, `## v1.2.0`, and `## [1.2.0] - 2024-01-01` all name 1.2.0, and so does `## Upgrading from 1.2.0`.
|
|
59
|
+
|
|
60
|
+
- **A version that neither the release windows nor `changelog.json` contain** keeps its section verbatim, placed among the rendered sections in version order.
|
|
61
|
+
- **A version that either contains** is rendered from its entries, and its old text is discarded.
|
|
62
|
+
- **A section whose heading doesn't name a version**, such as `## Unreleased`, is dropped.
|
|
63
|
+
|
|
64
|
+
The header above the first section and the footer comment are regenerated. The prepare report lists the versions kept for each changelog and warns about each section dropped. Kept sections never enter `changelog.json`.
|
|
65
|
+
|
|
66
|
+
### Routing to workspaces
|
|
67
|
+
|
|
68
|
+
A workspace's window contains every commit that touches the workspace's paths. An item derived from a title is routed to every workspace whose window contains its commit. An item derived from a change-record entry is routed to a workspace in that window only when the entry's `scopes` route it there:
|
|
69
|
+
|
|
70
|
+
- **A scope that names the workspace's `dir`** routes the item to that workspace. release-kit resolves each scope through [`scopeAliases`](configuration.md) before it compares the scope with the `dir`.
|
|
71
|
+
- **`*`, or an empty or absent `scopes`,** routes the item to every workspace in the window.
|
|
72
|
+
- **`root`** doesn't name a workspace, so it routes the item nowhere and is not reported. A workspace whose `dir` is `root` is the exception: `root` then names that workspace.
|
|
73
|
+
|
|
74
|
+
A scope narrows the window and never widens it: A scope that names a workspace whose window does not contain the commit doesn't route the item to any workspace, and `prepare` reports it (see [Reported diagnostics](#reported-diagnostics)). The project changelog and a single-package repo take every entry, whatever its scopes.
|
|
75
|
+
|
|
76
|
+
A workspace's bump reads the items routed to it, so a breaking entry scoped to one workspace does not raise a major bump in another that the same commit touched. A workspace whose window has commits but lacks a routed item doesn't call for a release.
|
|
77
|
+
|
|
78
|
+
Each `prepare` rebuilds a workspace's released windows with the same routing. When an earlier release recorded an item under a workspace that the item's scopes do not name, the rebuild removes the item from that workspace's past section. A path window does not follow a renamed directory, and legacy identities do not rename scopes: An entry scoped to a workspace's old directory name is routed to the workspace only through a `scopeAliases` entry from the old name to the new one, and until one exists, `prepare` reports the scope for each such commit in the unreleased window.
|
|
79
|
+
|
|
80
|
+
## Change-record blocks
|
|
81
|
+
|
|
82
|
+
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:
|
|
83
|
+
|
|
84
|
+
| Key | Type | Meaning |
|
|
85
|
+
| --------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
86
|
+
| `entries` | list | The change entries, one per outcome of the change, in the order that they read. |
|
|
87
|
+
| `entries[].type` | `string` | A work type, resolved through the declared types and their aliases. Required. |
|
|
88
|
+
| `entries[].text` | `string` | The sentence that reports the outcome. Required. |
|
|
89
|
+
| `entries[].breaking` | boolean | Whether the outcome breaks consumers. |
|
|
90
|
+
| `entries[].scopes` | list | The scopes that the outcome touched, each a string. They decide which workspaces receive the entry's item; see [Routing to workspaces](#routing-to-workspaces). |
|
|
91
|
+
| `entries[].migration` | `string` | The migration step for a consumer. |
|
|
92
|
+
| `pr_number` | integer | The merged pull request's number, a positive integer. |
|
|
93
|
+
| `ticket_ref` | `string` | The ticket that the change serves. Validated but not yet used. |
|
|
94
|
+
|
|
95
|
+
release-kit reads the **last** `change-record` fence in the message. A key whose value is null reads as absent.
|
|
96
|
+
|
|
97
|
+
### Items from entries
|
|
98
|
+
|
|
99
|
+
Each entry routed to the workspace being read yields one item, whether or not the commit subject has a ticket-ID prefix or a declared type:
|
|
100
|
+
|
|
101
|
+
- **Section**: The header of the entry's `type`, so the section order and the audience follow as for a title.
|
|
102
|
+
- **`description`**: The entry's `text` as written, followed by ` (#N)` when the block records `pr_number`.
|
|
103
|
+
- **`breaking`**: Set when the entry's `breaking` is `true` and the type's breaking policy permits it.
|
|
104
|
+
- **`migration`**: The entry's `migration`, when present. The item doesn't have a `body`.
|
|
105
|
+
- **`hash`** and **`entry`**: The commit's hash, and the entry's 1-based position among all the block's entries, including those that don't yield an item.
|
|
106
|
+
|
|
107
|
+
An entry whose type is excluded from the changelog doesn't yield an 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
108
|
|
|
43
|
-
|
|
109
|
+
### Fallback to the title
|
|
44
110
|
|
|
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 |
|
|
111
|
+
A commit is read from its title, as [Which commits appear in a changelog](#which-commits-appear-in-a-changelog) describes, when it doesn't have a 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
112
|
|
|
52
|
-
|
|
113
|
+
### Reported diagnostics
|
|
53
114
|
|
|
54
|
-
-
|
|
55
|
-
- Handles both `type: description` and `workspace|type: description` commit formats
|
|
56
|
-
- Groups commits by work type via `[git].commit_parsers`
|
|
115
|
+
`release-kit prepare` reports these as warnings for the release being prepared, from the commits that aren't in any earlier release:
|
|
57
116
|
|
|
58
|
-
|
|
117
|
+
- **A malformed block**, with the defect that stopped the read.
|
|
118
|
+
- **An entry whose type is not declared**, with its position. It doesn't yield an item.
|
|
119
|
+
- **An entry that violates its type's breaking policy**, as a policy violation at the entry's position. Its item is not marked breaking.
|
|
120
|
+
- **An entry scope that doesn't name a workspace in its commit's window**, with the commit's hash, the entry's position, and the scope as the entry declares it. release-kit reports it under every workspace whose window contains the commit. A scope that names nothing, or a workspace whose window does not contain the commit, is reported; `*`, `root`, and a scope that names a workspace in the window are not. Under `--only`, a scope that names a configured workspace left out of the run is not reported either. A workspace excluded with `shouldExclude: true` is not a configured workspace, so a scope that names one is reported.
|
|
59
121
|
|
|
60
|
-
|
|
122
|
+
The result that `prepare` returns includes these as `malformedBlocks`, `undeclaredEntryTypes`, `policyViolations` entries whose `surface` is `'entry'`, and `unroutedEntryScopes`, whether the target releases or is skipped. Because a block whose entries are all undeclared or excluded doesn't yield an item, the commit doesn't raise a bump.
|
|
61
123
|
|
|
62
124
|
## Release-notes injection
|
|
63
125
|
|
|
@@ -67,4 +129,4 @@ With `releaseNotes.shouldInjectIntoReadme` set to `true`, `release-kit publish`
|
|
|
67
129
|
<!-- section:release-notes --><!-- /section:release-notes -->
|
|
68
130
|
```
|
|
69
131
|
|
|
70
|
-
A README without the marker pair receives the notes at its top, above its title, and release-kit's readyup kit reports a README that lacks the markers. Injection is skipped with a warning when `changelogJson.enabled` is `false`, when the version
|
|
132
|
+
A README without the marker pair receives the notes at its top, above its title, and release-kit's readyup kit reports a README that lacks the markers. Injection is skipped with a warning when `changelogJson.enabled` is `false`, when the version doesn't have an entry, or when the entry doesn't have any all-audience content. `release-kit prepare --with-release-notes` writes the same injected README as a [preview](releasing.md#previewing-release-notes-with---with-release-notes).
|
package/docs/configuration.md
CHANGED
|
@@ -6,30 +6,29 @@ A minimal config file is in the [README](../README.md#configuration).
|
|
|
6
6
|
|
|
7
7
|
## Config file location
|
|
8
8
|
|
|
9
|
-
release-kit reads `.config/release-kit.config.ts`, resolved against the
|
|
9
|
+
release-kit reads `.config/release-kit.config.ts`, resolved against the repo root, which release-kit locates from the directory from which it was invoked. A run reads a different file when the subcommand is given `--config <path>`, which is resolved against the invocation directory rather than the root, so that a relative path means what it means in the caller's shell.
|
|
10
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`,
|
|
11
|
+
The flag is accepted by every subcommand that reads a config: `prepare`, `push`, `publish`, `create-github-release`, `show-tag-prefixes`, `overrides validate`, `sync-labels init`, and `sync-labels generate`. `commit`, `tag`, and `sync-labels sync` don't read a config, and they reject the flag.
|
|
12
12
|
|
|
13
|
-
The two paths differ in what an absent file means. An absent default path means the repo
|
|
13
|
+
The two paths differ in what an absent file means. An absent default path means that the repo doesn't declare a 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
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
|
|
15
|
+
`--config` names a file to read, never a write target. `release-kit init --with-config` scaffolds to `.config/release-kit.config.ts` and doesn't take `--config`, and `sync-labels init` writes its seeded `repoLabels` block to that same path.
|
|
16
16
|
|
|
17
17
|
## `ReleaseKitConfig` reference
|
|
18
18
|
|
|
19
|
-
| Field | Type
|
|
20
|
-
| ------------------ |
|
|
21
|
-
| `
|
|
22
|
-
| `
|
|
23
|
-
| `
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `
|
|
27
|
-
| `
|
|
28
|
-
| `
|
|
29
|
-
| `
|
|
30
|
-
| `
|
|
31
|
-
| `
|
|
32
|
-
| `releaseNotes` | `ReleaseNotesConfig` | `shouldInjectIntoReadme` (default `false`) injects release notes into each package's README while it publishes; see [Release-notes injection](changelogs.md#release-notes-injection) |
|
|
19
|
+
| Field | Type | Description |
|
|
20
|
+
| ------------------ | ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
21
|
+
| `workspaces` | `WorkspaceOverride[]` | Override or exclude discovered workspaces (matched by `dir`) |
|
|
22
|
+
| `formatCommand` | `string` | Shell command to run after changelog generation; modified file paths are appended as arguments |
|
|
23
|
+
| `versionPatterns` | `VersionPatterns` | Rules for which commit types trigger major/minor bumps |
|
|
24
|
+
| `scopeAliases` | `Record<string, string>` | Maps shorthand scope names to canonical names in commit titles and in change-record entry `scopes`, which [route items to workspaces](changelogs.md#routing-to-workspaces). A renamed workspace directory needs an entry from its old name to its new one |
|
|
25
|
+
| `workTypes` | `Record<string, WorkTypeConfig>` | Work type definitions, merged with defaults by key |
|
|
26
|
+
| `breakingPolicies` | `Record<string, 'forbidden' \| 'optional'>` | Per-type `!`-policy lookup. Defaults to `DEFAULT_BREAKING_POLICIES`. Replaces the default entirely when provided. Set to `{}` to disable enforcement |
|
|
27
|
+
| `retiredPackages` | `RetiredPackage[]` | Packages that once lived in this repo but have been extracted or removed; suppresses undeclared-tag-prefix warnings |
|
|
28
|
+
| `project` | `ProjectConfig` | Opt-in project-level release block. Declaring `project: {}` (even empty) enables a project-release stage in `prepare` |
|
|
29
|
+
| `repoLabels` | `RepoLabelsConfig` | Declares the repository's label registry for `sync-labels`; see [Label configuration](labels.md#label-configuration) |
|
|
30
|
+
| `changelogJson` | `ChangelogJsonConfig` | Controls `.meta/changelog.json`: `enabled` (default `true`), `outputPath` (default `'.meta/changelog.json'`), and `devOnlySections`; see [Custom work types](work-types.md#custom-work-types) |
|
|
31
|
+
| `releaseNotes` | `ReleaseNotesConfig` | `shouldInjectIntoReadme` (default `false`) injects release notes into each package's README while it publishes; see [Release-notes injection](changelogs.md#release-notes-injection) |
|
|
33
32
|
|
|
34
33
|
All fields are optional.
|
|
35
34
|
|
|
@@ -48,7 +47,9 @@ interface LegacyIdentity {
|
|
|
48
47
|
}
|
|
49
48
|
```
|
|
50
49
|
|
|
51
|
-
`
|
|
50
|
+
`prepare` never versions or tags an excluded workspace. A tag of one found on HEAD anyway, made by hand or before the exclusion, is skipped with a warning by `push`, `publish`, and `create-github-release`.
|
|
51
|
+
|
|
52
|
+
`legacyIdentities` captures prior identities of a workspace as complete `(name, tagPrefix)` snapshots. The union of the current `tagPrefix` and each identity's `tagPrefix` is consulted when release-kit searches for the most recent baseline tag and when generating changelogs. Use it when a workspace's historical tags were published under a different npm name, a different tag prefix, or both, typically across a package rename. Both fields are required per identity: Each entry must be a complete historical snapshot that stays valid regardless of subsequent renames. Run `release-kit show-tag-prefixes` to detect undeclared candidates and produce a paste-ready config snippet. Listing the current identity (full `(name, tagPrefix)` match) is rejected as a no-op duplicate; an identity whose `tagPrefix` matches the current but whose `name` differs is valid and documents a prior rename that reused the same tag shape. If the workspace no longer exists in this repo at all (the package was extracted or removed), use [`retiredPackages`](#retiredpackage) instead.
|
|
52
53
|
|
|
53
54
|
## `RetiredPackage`
|
|
54
55
|
|
|
@@ -60,9 +61,9 @@ interface RetiredPackage {
|
|
|
60
61
|
}
|
|
61
62
|
```
|
|
62
63
|
|
|
63
|
-
`retiredPackages` is the repo-level complement to `legacyIdentities`. Use `legacyIdentities` when the workspace still exists in this repo under a new identity; use `retiredPackages` when no workspace for this package
|
|
64
|
+
`retiredPackages` is the repo-level complement to `legacyIdentities`. Use `legacyIdentities` when the workspace still exists in this repo under a new identity; use `retiredPackages` when this repo no longer contains a workspace for this package, because the package was extracted to another repo or removed outright. Retired entries are inert: release-kit never consults them for baseline lookup or changelog attribution. Their declared `tagPrefix` values are recognized as historical, so `show-tag-prefixes` stops flagging them under "Undeclared tag prefixes."
|
|
64
65
|
|
|
65
|
-
Worked example
|
|
66
|
+
Worked example: `preflight` was extracted from this monorepo and continues as the standalone `readyup` project. Its tags stay in this repo as historical anchors:
|
|
66
67
|
|
|
67
68
|
```typescript
|
|
68
69
|
import { defineConfig } from '@williamthorsen/release-kit/config';
|
|
@@ -77,7 +78,7 @@ Validation rules:
|
|
|
77
78
|
- `name` and `tagPrefix` are required per entry and must be non-empty strings.
|
|
78
79
|
- `successor` is optional; if present, it must be a non-empty string.
|
|
79
80
|
- Full-tuple `(name, tagPrefix)` duplicates within `retiredPackages` are rejected.
|
|
80
|
-
- Two entries sharing the same `tagPrefix` but different `name`s are accepted
|
|
81
|
+
- Two entries sharing the same `tagPrefix` but different `name`s are accepted: Such a pair documents a package renamed within the repo before being retired.
|
|
81
82
|
|
|
82
83
|
`show-tag-prefixes` currently does not render a dedicated "Retired packages" section (deferred). Declaring a retired entry is verifiable by confirming that its `tagPrefix` stops appearing under "Undeclared tag prefixes" in the `show-tag-prefixes` output.
|
|
83
84
|
|
|
@@ -89,23 +90,23 @@ Tag prefixes from distinct owners must not be identical or be a strict prefix of
|
|
|
89
90
|
- A `retiredPackages[]` entry (one owner per entry).
|
|
90
91
|
- The `project` block, when configured.
|
|
91
92
|
|
|
92
|
-
release-kit
|
|
93
|
+
Because release-kit matches a tag to a prefix when the tag name starts with the prefix and continues with a digit, 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`, whose shorter prefix can't match any tag of the longer one.
|
|
93
94
|
|
|
94
95
|
The rule is enforced at config load; the resulting error identifies both colliding declarations.
|
|
95
96
|
|
|
96
97
|
## `release-kit show-tag-prefixes`
|
|
97
98
|
|
|
98
|
-
|
|
99
|
+
Prints a per-workspace table of derived tag prefixes, tag counts, and declared legacy prefixes. Also reports any release-shaped tag prefix in the repo that is neither a derived prefix nor declared via `legacyIdentities`, along with a copy-pasteable `workspaces: [...]` config snippet. The snippet uses a `TODO-fill-in-legacy-npm-name` placeholder for each identity's `name`; replace it with the package's prior npm name before pasting.
|
|
99
100
|
|
|
100
|
-
Exits `0` when every workspace derives a prefix and
|
|
101
|
+
Exits `0` when every workspace derives a prefix and the workspaces' prefixes don't collide; exits `1` on any derivation failure or collision. Undeclared candidates do not affect the exit code; the `legacy tag prefixes are declared` readyup check reports them as a warning.
|
|
101
102
|
|
|
102
|
-
|
|
103
|
+
When a config file exists and either fails to load or fails schema validation, the command reports the failure on stderr and exits `1` without printing a preview, because a config that cannot be read leaves the declared-versus-undeclared verdict wrong in the one dimension on which the command exists to report. 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.
|
|
103
104
|
|
|
104
105
|
In single-package mode, prints a single row with `workspacePath = .` and `derivedPrefix = v`; legacy entries and undeclared-candidate scanning are not applicable.
|
|
105
106
|
|
|
106
107
|
## `VersionPatterns`
|
|
107
108
|
|
|
108
|
-
Defines which
|
|
109
|
+
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.
|
|
109
110
|
|
|
110
111
|
```typescript
|
|
111
112
|
interface VersionPatterns {
|
|
@@ -134,6 +135,6 @@ The variable outranks detection. Under `auto`, release-kit defers to detection,
|
|
|
134
135
|
|
|
135
136
|
1. `CI` is set to anything other than an empty string or `false`.
|
|
136
137
|
2. The stream is not a terminal, as when its output is piped or redirected.
|
|
137
|
-
3. `TERM` is `linux`, the Linux virtual console, which
|
|
138
|
+
3. `TERM` is `linux`, the Linux virtual console, which doesn't draw emoji.
|
|
138
139
|
|
|
139
|
-
The style
|
|
140
|
+
The style affects only emoji. Changelog section emoji are file content rather than terminal output, and they are unaffected.
|