@williamthorsen/release-kit 5.0.0 → 5.1.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/CHANGELOG.md +60 -0
- package/README.md +137 -43
- package/dist/esm/.cache +1 -1
- package/dist/esm/assertCleanWorkingTree.js +1 -1
- package/dist/esm/bin/release-kit.js +1 -1
- package/dist/esm/buildChangelogEntries.d.ts +3 -0
- package/dist/esm/{generateChangelogJson.js → buildChangelogEntries.js} +7 -80
- package/dist/esm/buildDependencyGraph.d.ts +1 -0
- package/dist/esm/buildDependencyGraph.js +8 -1
- package/dist/esm/buildReleaseSummary.js +9 -1
- package/dist/esm/buildSyntheticChangelogEntry.d.ts +5 -0
- package/dist/esm/buildSyntheticChangelogEntry.js +13 -0
- package/dist/esm/changelogJsonFile.d.ts +4 -0
- package/dist/esm/changelogJsonFile.js +68 -0
- package/dist/esm/decideRelease.d.ts +25 -0
- package/dist/esm/decideRelease.js +28 -0
- package/dist/esm/defaults.d.ts +1 -0
- package/dist/esm/defaults.js +2 -0
- package/dist/esm/index.d.ts +2 -43
- package/dist/esm/index.js +0 -82
- package/dist/esm/init/templates.js +2 -2
- package/dist/esm/loadConfig.d.ts +10 -1
- package/dist/esm/loadConfig.js +96 -2
- package/dist/esm/prepareCommand.js +51 -9
- package/dist/esm/publish.d.ts +0 -1
- package/dist/esm/publish.js +3 -3
- package/dist/esm/publishCommand.js +10 -1
- package/dist/esm/releasePrepare.js +83 -39
- package/dist/esm/releasePrepareMono.js +133 -87
- package/dist/esm/releasePrepareProject.d.ts +9 -0
- package/dist/esm/releasePrepareProject.js +109 -0
- package/dist/esm/reportPrepare.js +70 -24
- package/dist/esm/types.d.ts +57 -14
- package/dist/esm/validateConfig.js +26 -0
- package/dist/esm/validateOnlyExcludesStrandedDependents.d.ts +14 -0
- package/dist/esm/validateOnlyExcludesStrandedDependents.js +109 -0
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +4 -1
- package/presets/labels/common.yaml +9 -6
- package/schemas/label-map.json +24 -0
- package/dist/esm/generateChangelogJson.d.ts +0 -7
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Commit, ReleaseType, VersionPatterns, WorkTypeConfig } from './types.ts';
|
|
2
|
+
export interface DecideReleaseArgs {
|
|
3
|
+
commits: readonly Commit[];
|
|
4
|
+
force?: boolean | undefined;
|
|
5
|
+
bumpOverride: ReleaseType | undefined;
|
|
6
|
+
workTypes: Record<string, WorkTypeConfig>;
|
|
7
|
+
versionPatterns: VersionPatterns;
|
|
8
|
+
scopeAliases: Record<string, string> | undefined;
|
|
9
|
+
skipReasons: {
|
|
10
|
+
noCommits: string;
|
|
11
|
+
noBumpWorthy: string;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
export type DecideReleaseResult = {
|
|
15
|
+
outcome: 'release';
|
|
16
|
+
releaseType: ReleaseType;
|
|
17
|
+
parsedCommitCount: number;
|
|
18
|
+
unparseableCommits: Commit[] | undefined;
|
|
19
|
+
} | {
|
|
20
|
+
outcome: 'skip';
|
|
21
|
+
skipReason: string;
|
|
22
|
+
parsedCommitCount: number;
|
|
23
|
+
unparseableCommits: Commit[] | undefined;
|
|
24
|
+
};
|
|
25
|
+
export declare function decideRelease(args: DecideReleaseArgs): DecideReleaseResult;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { determineBumpType } from "./determineBumpType.js";
|
|
2
|
+
import { parseCommitMessage } from "./parseCommitMessage.js";
|
|
3
|
+
function decideRelease(args) {
|
|
4
|
+
const { commits, force = false, bumpOverride, workTypes, versionPatterns, scopeAliases, skipReasons } = args;
|
|
5
|
+
const parsedCommits = [];
|
|
6
|
+
const unparseable = [];
|
|
7
|
+
for (const commit of commits) {
|
|
8
|
+
const parsed = parseCommitMessage(commit.message, commit.hash, workTypes, scopeAliases);
|
|
9
|
+
if (parsed === void 0) {
|
|
10
|
+
unparseable.push(commit);
|
|
11
|
+
} else {
|
|
12
|
+
parsedCommits.push(parsed);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const parsedCommitCount = parsedCommits.length;
|
|
16
|
+
const unparseableCommits = unparseable.length > 0 ? unparseable : void 0;
|
|
17
|
+
const naturalBump = determineBumpType(parsedCommits, workTypes, versionPatterns);
|
|
18
|
+
const shouldRelease = naturalBump !== void 0 || force;
|
|
19
|
+
if (!shouldRelease) {
|
|
20
|
+
const skipReason = commits.length === 0 ? skipReasons.noCommits : skipReasons.noBumpWorthy;
|
|
21
|
+
return { outcome: "skip", skipReason, parsedCommitCount, unparseableCommits };
|
|
22
|
+
}
|
|
23
|
+
const releaseType = bumpOverride ?? naturalBump ?? "patch";
|
|
24
|
+
return { outcome: "release", releaseType, parsedCommitCount, unparseableCommits };
|
|
25
|
+
}
|
|
26
|
+
export {
|
|
27
|
+
decideRelease
|
|
28
|
+
};
|
package/dist/esm/defaults.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export declare const DEFAULT_WORK_TYPES: Record<string, WorkTypeConfig>;
|
|
|
3
3
|
export declare const DEFAULT_VERSION_PATTERNS: VersionPatterns;
|
|
4
4
|
export declare const DEFAULT_CHANGELOG_JSON_CONFIG: ChangelogJsonConfig;
|
|
5
5
|
export declare const DEFAULT_RELEASE_NOTES_CONFIG: ReleaseNotesConfig;
|
|
6
|
+
export declare const DEFAULT_PROJECT_TAG_PREFIX = "v";
|
package/dist/esm/defaults.js
CHANGED
|
@@ -28,8 +28,10 @@ const DEFAULT_CHANGELOG_JSON_CONFIG = {
|
|
|
28
28
|
const DEFAULT_RELEASE_NOTES_CONFIG = {
|
|
29
29
|
shouldInjectIntoReadme: false
|
|
30
30
|
};
|
|
31
|
+
const DEFAULT_PROJECT_TAG_PREFIX = "v";
|
|
31
32
|
export {
|
|
32
33
|
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
34
|
+
DEFAULT_PROJECT_TAG_PREFIX,
|
|
33
35
|
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
34
36
|
DEFAULT_VERSION_PATTERNS,
|
|
35
37
|
DEFAULT_WORK_TYPES
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -1,43 +1,2 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
export type {
|
|
3
|
-
export type { GenerateChangelogOptions } from './generateChangelogs.ts';
|
|
4
|
-
export type { RetiredPackagePreviewEntry } from './previewTagPrefixes.ts';
|
|
5
|
-
export type { PublishOptions } from './publish.ts';
|
|
6
|
-
export type { ReleasePrepareOptions } from './releasePrepare.ts';
|
|
7
|
-
export type { ResolvedTag } from './resolveReleaseTags.ts';
|
|
8
|
-
export type { LabelDefinition, SyncLabelsConfig } from './sync-labels/types.ts';
|
|
9
|
-
export type { BumpResult, ChangelogAudience, ChangelogEntry, ChangelogItem, ChangelogJsonConfig, ChangelogSection, Commit, LegacyIdentity, MonorepoReleaseConfig, ParsedCommit, PrepareResult, ReleaseConfig, ReleaseKitConfig, ReleaseNotesConfig, ReleaseType, RetiredPackage, VersionPatterns, WorkspaceConfig, WorkspaceOverride, WorkspacePrepareResult, WorkTypeConfig, } from './types.ts';
|
|
10
|
-
export { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES, } from './defaults.ts';
|
|
11
|
-
export { buildReleaseSummary } from './buildReleaseSummary.ts';
|
|
12
|
-
export { bumpAllVersions } from './bumpAllVersions.ts';
|
|
13
|
-
export { bumpVersion } from './bumpVersion.ts';
|
|
14
|
-
export { commitCommand } from './commitCommand.ts';
|
|
15
|
-
export type { CreateGithubReleaseOptions } from './createGithubRelease.ts';
|
|
16
|
-
export { createGithubRelease, createGithubReleases } from './createGithubRelease.ts';
|
|
17
|
-
export { createTags } from './createTags.ts';
|
|
18
|
-
export { deleteFileIfExists } from './deleteFileIfExists.ts';
|
|
19
|
-
export { deriveWorkspaceConfig } from './deriveWorkspaceConfig.ts';
|
|
20
|
-
export { detectPackageManager } from './detectPackageManager.ts';
|
|
21
|
-
export { determineBumpType } from './determineBumpType.ts';
|
|
22
|
-
export { discoverWorkspaces } from './discoverWorkspaces.ts';
|
|
23
|
-
export { generateChangelogJson, generateSyntheticChangelogJson } from './generateChangelogJson.ts';
|
|
24
|
-
export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
|
|
25
|
-
export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
|
|
26
|
-
export type { RenderedInjectedReadme } from './injectReleaseNotesIntoReadme.ts';
|
|
27
|
-
export { injectReleaseNotesIntoReadme, renderInjectedReadme, resolveReadmePath, } from './injectReleaseNotesIntoReadme.ts';
|
|
28
|
-
export { injectSection } from './injectSection.ts';
|
|
29
|
-
export { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from './parseCommitMessage.ts';
|
|
30
|
-
export { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
|
|
31
|
-
export { publishPackage } from './publish.ts';
|
|
32
|
-
export type { PushReleaseOptions } from './pushRelease.ts';
|
|
33
|
-
export { pushRelease } from './pushRelease.ts';
|
|
34
|
-
export { releasePrepare } from './releasePrepare.ts';
|
|
35
|
-
export { releasePrepareMono } from './releasePrepareMono.ts';
|
|
36
|
-
export type { RenderOptions } from './renderReleaseNotes.ts';
|
|
37
|
-
export { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from './renderReleaseNotes.ts';
|
|
38
|
-
export { reportPrepare } from './reportPrepare.ts';
|
|
39
|
-
export { resolveCommandTags } from './resolveCommandTags.ts';
|
|
40
|
-
export { resolveReleaseTags } from './resolveReleaseTags.ts';
|
|
41
|
-
export { stripScope } from './stripScope.ts';
|
|
42
|
-
export type { PreviewFileResult, WriteReleaseNotesPreviewsOptions, WriteReleaseNotesPreviewsResult, } from './writeReleaseNotesPreviews.ts';
|
|
43
|
-
export { writeReleaseNotesPreviews } from './writeReleaseNotesPreviews.ts';
|
|
1
|
+
export type { SyncLabelsConfig } from './sync-labels/types.ts';
|
|
2
|
+
export type { ReleaseKitConfig } from './types.ts';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
3
|
-
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
4
|
-
DEFAULT_VERSION_PATTERNS,
|
|
5
|
-
DEFAULT_WORK_TYPES
|
|
6
|
-
} from "./defaults.js";
|
|
7
|
-
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
8
|
-
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
9
|
-
import { bumpVersion } from "./bumpVersion.js";
|
|
10
|
-
import { commitCommand } from "./commitCommand.js";
|
|
11
|
-
import { createGithubRelease, createGithubReleases } from "./createGithubRelease.js";
|
|
12
|
-
import { createTags } from "./createTags.js";
|
|
13
|
-
import { deleteFileIfExists } from "./deleteFileIfExists.js";
|
|
14
|
-
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
15
|
-
import { detectPackageManager } from "./detectPackageManager.js";
|
|
16
|
-
import { determineBumpType } from "./determineBumpType.js";
|
|
17
|
-
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
18
|
-
import { generateChangelogJson, generateSyntheticChangelogJson } from "./generateChangelogJson.js";
|
|
19
|
-
import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
|
|
20
|
-
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
21
|
-
import {
|
|
22
|
-
injectReleaseNotesIntoReadme,
|
|
23
|
-
renderInjectedReadme,
|
|
24
|
-
resolveReadmePath
|
|
25
|
-
} from "./injectReleaseNotesIntoReadme.js";
|
|
26
|
-
import { injectSection } from "./injectSection.js";
|
|
27
|
-
import { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from "./parseCommitMessage.js";
|
|
28
|
-
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
|
|
29
|
-
import { publishPackage } from "./publish.js";
|
|
30
|
-
import { pushRelease } from "./pushRelease.js";
|
|
31
|
-
import { releasePrepare } from "./releasePrepare.js";
|
|
32
|
-
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
33
|
-
import { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
34
|
-
import { reportPrepare } from "./reportPrepare.js";
|
|
35
|
-
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
36
|
-
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
37
|
-
import { stripScope } from "./stripScope.js";
|
|
38
|
-
import { writeReleaseNotesPreviews } from "./writeReleaseNotesPreviews.js";
|
|
39
|
-
export {
|
|
40
|
-
COMMIT_PREPROCESSOR_PATTERNS,
|
|
41
|
-
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
42
|
-
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
43
|
-
DEFAULT_VERSION_PATTERNS,
|
|
44
|
-
DEFAULT_WORK_TYPES,
|
|
45
|
-
RELEASE_SUMMARY_FILE,
|
|
46
|
-
RELEASE_TAGS_FILE,
|
|
47
|
-
buildReleaseSummary,
|
|
48
|
-
bumpAllVersions,
|
|
49
|
-
bumpVersion,
|
|
50
|
-
commitCommand,
|
|
51
|
-
createGithubRelease,
|
|
52
|
-
createGithubReleases,
|
|
53
|
-
createTags,
|
|
54
|
-
deleteFileIfExists,
|
|
55
|
-
deriveWorkspaceConfig,
|
|
56
|
-
detectPackageManager,
|
|
57
|
-
determineBumpType,
|
|
58
|
-
discoverWorkspaces,
|
|
59
|
-
generateChangelog,
|
|
60
|
-
generateChangelogJson,
|
|
61
|
-
generateChangelogs,
|
|
62
|
-
generateSyntheticChangelogJson,
|
|
63
|
-
getCommitsSinceTarget,
|
|
64
|
-
injectReleaseNotesIntoReadme,
|
|
65
|
-
injectSection,
|
|
66
|
-
matchesAudience,
|
|
67
|
-
parseCommitMessage,
|
|
68
|
-
publishPackage,
|
|
69
|
-
pushRelease,
|
|
70
|
-
releasePrepare,
|
|
71
|
-
releasePrepareMono,
|
|
72
|
-
renderInjectedReadme,
|
|
73
|
-
renderReleaseNotesMulti,
|
|
74
|
-
renderReleaseNotesSingle,
|
|
75
|
-
reportPrepare,
|
|
76
|
-
resolveCommandTags,
|
|
77
|
-
resolveReadmePath,
|
|
78
|
-
resolveReleaseTags,
|
|
79
|
-
stripScope,
|
|
80
|
-
writeReleaseNotesPreviews,
|
|
81
|
-
writeReleaseTags
|
|
82
|
-
};
|
|
@@ -107,7 +107,7 @@ on:
|
|
|
107
107
|
- minor
|
|
108
108
|
- major
|
|
109
109
|
force:
|
|
110
|
-
description: 'Force a release even when
|
|
110
|
+
description: 'Force a release even when no commits or no bump-worthy commits exist (defaults to patch; combine with --bump for a different level)'
|
|
111
111
|
required: false
|
|
112
112
|
type: boolean
|
|
113
113
|
default: false
|
|
@@ -141,7 +141,7 @@ on:
|
|
|
141
141
|
- minor
|
|
142
142
|
- major
|
|
143
143
|
force:
|
|
144
|
-
description: 'Force a release even when
|
|
144
|
+
description: 'Force a release even when no commits or no bump-worthy commits exist (defaults to patch; combine with --bump for a different level)'
|
|
145
145
|
required: false
|
|
146
146
|
type: boolean
|
|
147
147
|
default: false
|
package/dist/esm/loadConfig.d.ts
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import type { MonorepoReleaseConfig, ReleaseConfig, ReleaseKitConfig, WorkTypeConfig } from './types.ts';
|
|
2
|
+
export declare const ROOT_PACKAGE_JSON_PATH = "package.json";
|
|
3
|
+
export declare function readRootPackageVersion(): {
|
|
4
|
+
exists: boolean;
|
|
5
|
+
version: string | undefined;
|
|
6
|
+
};
|
|
2
7
|
export declare const CONFIG_FILE_PATH = ".config/release-kit.config.ts";
|
|
3
8
|
export declare function loadConfig(): Promise<unknown>;
|
|
4
|
-
export
|
|
9
|
+
export interface RootPackageInfo {
|
|
10
|
+
exists: boolean;
|
|
11
|
+
version: string | undefined;
|
|
12
|
+
}
|
|
13
|
+
export declare function mergeMonorepoConfig(discoveredPaths: string[], userConfig: ReleaseKitConfig | undefined, rootPackage?: RootPackageInfo): MonorepoReleaseConfig;
|
|
5
14
|
export declare function mergeSinglePackageConfig(userConfig: ReleaseKitConfig | undefined): ReleaseConfig;
|
|
6
15
|
export declare function resolveWorkTypes(userWorkTypes?: Record<string, WorkTypeConfig>): Record<string, WorkTypeConfig>;
|
package/dist/esm/loadConfig.js
CHANGED
|
@@ -1,13 +1,41 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import {
|
|
4
4
|
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
5
|
+
DEFAULT_PROJECT_TAG_PREFIX,
|
|
5
6
|
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
6
7
|
DEFAULT_VERSION_PATTERNS,
|
|
7
8
|
DEFAULT_WORK_TYPES
|
|
8
9
|
} from "./defaults.js";
|
|
9
10
|
import { deriveWorkspaceConfig } from "./deriveWorkspaceConfig.js";
|
|
10
11
|
import { isRecord } from "./typeGuards.js";
|
|
12
|
+
const ROOT_PACKAGE_JSON_PATH = "package.json";
|
|
13
|
+
function readRootPackageVersion() {
|
|
14
|
+
const absolutePath = path.resolve(process.cwd(), ROOT_PACKAGE_JSON_PATH);
|
|
15
|
+
if (!existsSync(absolutePath)) {
|
|
16
|
+
return { exists: false, version: void 0 };
|
|
17
|
+
}
|
|
18
|
+
let contents;
|
|
19
|
+
try {
|
|
20
|
+
contents = readFileSync(absolutePath, "utf8");
|
|
21
|
+
} catch (error) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`Failed to read root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
let parsed;
|
|
27
|
+
try {
|
|
28
|
+
parsed = JSON.parse(contents);
|
|
29
|
+
} catch (error) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Failed to parse root ${ROOT_PACKAGE_JSON_PATH}: ${error instanceof Error ? error.message : String(error)}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
if (!isRecord(parsed)) {
|
|
35
|
+
return { exists: true, version: void 0 };
|
|
36
|
+
}
|
|
37
|
+
return { exists: true, version: typeof parsed.version === "string" ? parsed.version : void 0 };
|
|
38
|
+
}
|
|
11
39
|
const CONFIG_FILE_PATH = ".config/release-kit.config.ts";
|
|
12
40
|
async function loadConfig() {
|
|
13
41
|
const absoluteConfigPath = path.resolve(process.cwd(), CONFIG_FILE_PATH);
|
|
@@ -28,7 +56,7 @@ async function loadConfig() {
|
|
|
28
56
|
}
|
|
29
57
|
return resolved;
|
|
30
58
|
}
|
|
31
|
-
function mergeMonorepoConfig(discoveredPaths, userConfig) {
|
|
59
|
+
function mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage) {
|
|
32
60
|
let workspaces = discoveredPaths.map((workspacePath) => deriveWorkspaceConfig(workspacePath));
|
|
33
61
|
assertUniqueTagPrefixes(workspaces);
|
|
34
62
|
if (userConfig?.workspaces !== void 0) {
|
|
@@ -48,10 +76,12 @@ function mergeMonorepoConfig(discoveredPaths, userConfig) {
|
|
|
48
76
|
if (userConfig?.retiredPackages !== void 0) {
|
|
49
77
|
assertRetiredPackagesDoNotCollideWithActive(workspaces, userConfig.retiredPackages);
|
|
50
78
|
}
|
|
79
|
+
const project = resolveProjectConfig(userConfig?.project, rootPackage);
|
|
51
80
|
const workTypes = resolveWorkTypes(userConfig?.workTypes);
|
|
52
81
|
const versionPatterns = userConfig?.versionPatterns === void 0 ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
|
|
53
82
|
const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
|
|
54
83
|
const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
|
|
84
|
+
assertNoTagPrefixCollisions(workspaces, userConfig?.retiredPackages, project);
|
|
55
85
|
const result = {
|
|
56
86
|
workspaces,
|
|
57
87
|
workTypes,
|
|
@@ -59,6 +89,9 @@ function mergeMonorepoConfig(discoveredPaths, userConfig) {
|
|
|
59
89
|
changelogJson,
|
|
60
90
|
releaseNotes
|
|
61
91
|
};
|
|
92
|
+
if (project !== void 0) {
|
|
93
|
+
result.project = project;
|
|
94
|
+
}
|
|
62
95
|
const formatCommand = userConfig?.formatCommand;
|
|
63
96
|
if (formatCommand !== void 0) {
|
|
64
97
|
result.formatCommand = formatCommand;
|
|
@@ -74,6 +107,9 @@ function mergeMonorepoConfig(discoveredPaths, userConfig) {
|
|
|
74
107
|
return result;
|
|
75
108
|
}
|
|
76
109
|
function mergeSinglePackageConfig(userConfig) {
|
|
110
|
+
if (userConfig?.project !== void 0) {
|
|
111
|
+
throw new Error("project block is not supported in single-package mode");
|
|
112
|
+
}
|
|
77
113
|
const workTypes = resolveWorkTypes(userConfig?.workTypes);
|
|
78
114
|
const versionPatterns = userConfig?.versionPatterns === void 0 ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
|
|
79
115
|
const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
|
|
@@ -137,6 +173,62 @@ function assertRetiredPackagesDoNotCollideWithActive(workspaces, retiredPackages
|
|
|
137
173
|
}
|
|
138
174
|
}
|
|
139
175
|
}
|
|
176
|
+
function resolveProjectConfig(userProject, rootPackage) {
|
|
177
|
+
if (userProject === void 0) {
|
|
178
|
+
return void 0;
|
|
179
|
+
}
|
|
180
|
+
if (rootPackage === void 0 || !rootPackage.exists) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`project block requires a root ${ROOT_PACKAGE_JSON_PATH}; create one with a 'version' field at the repo root`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
if (rootPackage.version === void 0) {
|
|
186
|
+
throw new Error(
|
|
187
|
+
`project block requires root ${ROOT_PACKAGE_JSON_PATH} to have a 'version' field; add a 'version' field to your root package.json`
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
return { tagPrefix: userProject.tagPrefix ?? DEFAULT_PROJECT_TAG_PREFIX };
|
|
191
|
+
}
|
|
192
|
+
function assertNoTagPrefixCollisions(workspaces, retiredPackages, project) {
|
|
193
|
+
const sources = [];
|
|
194
|
+
for (const workspace of workspaces) {
|
|
195
|
+
const owner = `ws:${workspace.dir}`;
|
|
196
|
+
sources.push({ prefix: workspace.tagPrefix, label: `workspace '${workspace.dir}'`, owner });
|
|
197
|
+
for (const identity of workspace.legacyIdentities ?? []) {
|
|
198
|
+
sources.push({
|
|
199
|
+
prefix: identity.tagPrefix,
|
|
200
|
+
label: `workspace '${workspace.dir}' legacyIdentities entry (name='${identity.name}')`,
|
|
201
|
+
owner
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for (const [index, retired] of (retiredPackages ?? []).entries()) {
|
|
206
|
+
sources.push({
|
|
207
|
+
prefix: retired.tagPrefix,
|
|
208
|
+
label: `retiredPackages entry (name='${retired.name}')`,
|
|
209
|
+
owner: `retired:${index}`
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (project !== void 0) {
|
|
213
|
+
sources.push({ prefix: project.tagPrefix, label: "project", owner: "project" });
|
|
214
|
+
}
|
|
215
|
+
for (let i = 0; i < sources.length; i++) {
|
|
216
|
+
for (let j = i + 1; j < sources.length; j++) {
|
|
217
|
+
const a = sources[i];
|
|
218
|
+
const b = sources[j];
|
|
219
|
+
if (a === void 0 || b === void 0) continue;
|
|
220
|
+
if (a.owner === b.owner) continue;
|
|
221
|
+
if (isPrefixCollision(a.prefix, b.prefix)) {
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Tag prefix collision: '${a.prefix}' (${a.label}) and '${b.prefix}' (${b.label}). One prefix is identical to or a strict prefix of the other; this would cause \`git describe --match=<prefix>*\` to return cross-matches.`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function isPrefixCollision(a, b) {
|
|
230
|
+
return a === b || a.startsWith(b) || b.startsWith(a);
|
|
231
|
+
}
|
|
140
232
|
function assertUniqueTagPrefixes(workspaces) {
|
|
141
233
|
const pathsByPrefix = /* @__PURE__ */ new Map();
|
|
142
234
|
for (const workspace of workspaces) {
|
|
@@ -163,8 +255,10 @@ function mergeReleaseNotesConfig(partial) {
|
|
|
163
255
|
}
|
|
164
256
|
export {
|
|
165
257
|
CONFIG_FILE_PATH,
|
|
258
|
+
ROOT_PACKAGE_JSON_PATH,
|
|
166
259
|
loadConfig,
|
|
167
260
|
mergeMonorepoConfig,
|
|
168
261
|
mergeSinglePackageConfig,
|
|
262
|
+
readRootPackageVersion,
|
|
169
263
|
resolveWorkTypes
|
|
170
264
|
};
|
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { parseArgs as coreParseArgs, translateParseError, writeFileWithCheck } from "@williamthorsen/nmr-core";
|
|
2
2
|
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
3
|
+
import { buildDependencyGraph } from "./buildDependencyGraph.js";
|
|
3
4
|
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
4
5
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
5
6
|
import { dim } from "./format.js";
|
|
6
|
-
import {
|
|
7
|
+
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
8
|
+
import { loadConfig, mergeMonorepoConfig, mergeSinglePackageConfig, readRootPackageVersion } from "./loadConfig.js";
|
|
7
9
|
import { releasePrepare } from "./releasePrepare.js";
|
|
8
10
|
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
9
11
|
import { reportPrepare } from "./reportPrepare.js";
|
|
10
12
|
import { validateConfig } from "./validateConfig.js";
|
|
13
|
+
import { validateOnlyExcludesStrandedDependents } from "./validateOnlyExcludesStrandedDependents.js";
|
|
11
14
|
const RELEASE_TAGS_FILE = "tmp/.release-tags";
|
|
12
15
|
const RELEASE_SUMMARY_FILE = "tmp/.release-summary";
|
|
13
16
|
const VALID_BUMP_TYPES = ["major", "minor", "patch"];
|
|
@@ -22,10 +25,14 @@ Usage: npx @williamthorsen/release-kit prepare [options]
|
|
|
22
25
|
Options:
|
|
23
26
|
--dry-run Run without modifying any files
|
|
24
27
|
--bump=major|minor|patch Override the bump type for all workspaces
|
|
25
|
-
--set-version=X.Y.Z Set an explicit version; bypasses commit-derived bumps.
|
|
26
|
-
|
|
28
|
+
--set-version=X.Y.Z Set an explicit version; bypasses commit-derived bumps.
|
|
29
|
+
Requires --only in monorepo mode (rejected when a 'project' block is configured).
|
|
30
|
+
--force Release even when no commits or no bump-worthy commits exist
|
|
31
|
+
since the last tag. Defaults to patch when --bump is not given;
|
|
32
|
+
use --bump=X to release at a different level.
|
|
27
33
|
--no-git-checks, -n Skip the clean-working-tree check
|
|
28
|
-
--only=name1,name2 Only process the named workspaces (comma-separated, monorepo only
|
|
34
|
+
--only=name1,name2 Only process the named workspaces (comma-separated, monorepo only;
|
|
35
|
+
rejected when a 'project' block is configured)
|
|
29
36
|
--with-release-notes Also write per-workspace release-notes previews under {workspacePath}/docs/
|
|
30
37
|
(docs/README.v{version}.md and docs/RELEASE_NOTES.v{version}.md).
|
|
31
38
|
Recommended .gitignore entry: packages/*/docs/*.v*.md (or docs/*.v*.md).
|
|
@@ -84,9 +91,6 @@ function parseArgs(argv) {
|
|
|
84
91
|
if (setVersion !== void 0 && flags.force) {
|
|
85
92
|
throw new Error("--set-version cannot be combined with --force");
|
|
86
93
|
}
|
|
87
|
-
if (flags.force && bumpOverride === void 0) {
|
|
88
|
-
throw new Error("--force requires --bump to specify the version bump type");
|
|
89
|
-
}
|
|
90
94
|
return {
|
|
91
95
|
dryRun: flags.dryRun,
|
|
92
96
|
force: flags.force,
|
|
@@ -157,17 +161,36 @@ function runSinglePackageMode(userConfig, options, only, dryRun) {
|
|
|
157
161
|
console.error("Error: --only is only supported for monorepo configurations");
|
|
158
162
|
process.exit(1);
|
|
159
163
|
}
|
|
164
|
+
if (options.force && options.bumpOverride === void 0) {
|
|
165
|
+
console.error(
|
|
166
|
+
"Error: --force without --bump is only supported for monorepo configurations. Use --bump=major|minor|patch to set the level for a single-package release."
|
|
167
|
+
);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
160
170
|
const config = mergeSinglePackageConfig(userConfig);
|
|
161
171
|
runAndReport(() => releasePrepare(config, options), dryRun);
|
|
162
172
|
}
|
|
163
173
|
function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion, dryRun) {
|
|
164
174
|
let config;
|
|
165
175
|
try {
|
|
166
|
-
|
|
176
|
+
const rootPackage = readRootPackageVersion();
|
|
177
|
+
config = mergeMonorepoConfig(discoveredPaths, userConfig, rootPackage);
|
|
167
178
|
} catch (error) {
|
|
168
179
|
console.error(`Error resolving workspaces: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
180
|
process.exit(1);
|
|
170
181
|
}
|
|
182
|
+
if (setVersion !== void 0 && config.project !== void 0) {
|
|
183
|
+
console.error(
|
|
184
|
+
"Error: --set-version cannot be combined with a project release. --set-version operates on a single workspace; a project release rolls up every contributing workspace. To use --set-version, run on a config without a `project` block."
|
|
185
|
+
);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
}
|
|
188
|
+
if (only !== void 0 && config.project !== void 0) {
|
|
189
|
+
console.error(
|
|
190
|
+
"Error: --only cannot be combined with a project release. To release a single workspace, use a config without a `project` block, or run a full `prepare` (no --only) to include the project release."
|
|
191
|
+
);
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
171
194
|
if (only !== void 0) {
|
|
172
195
|
const knownNames = config.workspaces.map((w) => w.dir);
|
|
173
196
|
for (const name of only) {
|
|
@@ -176,6 +199,25 @@ function runMonorepoMode(discoveredPaths, userConfig, options, only, setVersion,
|
|
|
176
199
|
process.exit(1);
|
|
177
200
|
}
|
|
178
201
|
}
|
|
202
|
+
const graph = buildDependencyGraph(config.workspaces);
|
|
203
|
+
const violations = validateOnlyExcludesStrandedDependents(config.workspaces, only, graph, (workspace) => {
|
|
204
|
+
const tagPrefixes = [
|
|
205
|
+
workspace.tagPrefix,
|
|
206
|
+
...workspace.legacyIdentities?.map((identity) => identity.tagPrefix) ?? []
|
|
207
|
+
];
|
|
208
|
+
const result = getCommitsSinceTarget(tagPrefixes, workspace.paths);
|
|
209
|
+
return { has: result.commits.length > 0, tag: result.tag };
|
|
210
|
+
});
|
|
211
|
+
if (violations !== void 0) {
|
|
212
|
+
console.error("Error: --only excludes packages with changes that would be stranded by the release.");
|
|
213
|
+
console.error("The following packages must be added to --only or have their dependencies removed:");
|
|
214
|
+
for (const violation of violations) {
|
|
215
|
+
const since = violation.tag ?? "the beginning";
|
|
216
|
+
console.error(` - ${violation.dir} (downstream of ${violation.downstreamOf}; has commits since ${since})`);
|
|
217
|
+
}
|
|
218
|
+
console.error("Alternatively, run `release-kit prepare` without --only to release everything.");
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
179
221
|
config.workspaces = config.workspaces.filter((w) => only.includes(w.dir));
|
|
180
222
|
}
|
|
181
223
|
if (setVersion !== void 0) {
|
|
@@ -221,7 +263,7 @@ function runAndReport(execute, dryRun) {
|
|
|
221
263
|
try {
|
|
222
264
|
result = execute();
|
|
223
265
|
} catch (error) {
|
|
224
|
-
console.error(
|
|
266
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
225
267
|
process.exit(1);
|
|
226
268
|
}
|
|
227
269
|
process.stdout.write(reportPrepare(result) + "\n");
|
package/dist/esm/publish.d.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { PackageManager } from './detectPackageManager.ts';
|
|
|
2
2
|
import type { ResolvedTag } from './resolveReleaseTags.ts';
|
|
3
3
|
export interface PublishOptions {
|
|
4
4
|
dryRun: boolean;
|
|
5
|
-
noGitChecks: boolean;
|
|
6
5
|
provenance: boolean;
|
|
7
6
|
}
|
|
8
7
|
export declare function publishPackage(resolvedTag: ResolvedTag, packageManager: PackageManager, options: PublishOptions): void;
|
package/dist/esm/publish.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
function publishPackage(resolvedTag, packageManager, options) {
|
|
3
|
-
const { dryRun,
|
|
3
|
+
const { dryRun, provenance } = options;
|
|
4
4
|
const executable = resolveExecutable(packageManager);
|
|
5
|
-
const args = buildPublishArgs(packageManager, { dryRun,
|
|
5
|
+
const args = buildPublishArgs(packageManager, { dryRun, provenance });
|
|
6
6
|
console.info(
|
|
7
7
|
`
|
|
8
8
|
${dryRun ? "[dry-run] " : ""}Running: ${executable} ${args.join(" ")} (cwd: ${resolvedTag.workspacePath})`
|
|
@@ -20,7 +20,7 @@ function buildPublishArgs(packageManager, options) {
|
|
|
20
20
|
if (options.dryRun) {
|
|
21
21
|
args.push("--dry-run");
|
|
22
22
|
}
|
|
23
|
-
if (
|
|
23
|
+
if (packageManager === "pnpm") {
|
|
24
24
|
args.push("--no-git-checks");
|
|
25
25
|
}
|
|
26
26
|
if (options.provenance && packageManager !== "yarn") {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { writeFileSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { parseArgs, translateParseError } from "@williamthorsen/nmr-core";
|
|
4
|
+
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
4
5
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
5
6
|
import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
|
|
6
7
|
import { parseRequestedTags } from "./parseRequestedTags.js";
|
|
@@ -22,6 +23,14 @@ async function publishCommand(argv) {
|
|
|
22
23
|
process.exit(1);
|
|
23
24
|
}
|
|
24
25
|
const { dryRun, noGitChecks, provenance } = parsed.flags;
|
|
26
|
+
if (!dryRun && !noGitChecks) {
|
|
27
|
+
try {
|
|
28
|
+
assertCleanWorkingTree();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
25
34
|
const requestedTags = parseRequestedTags(parsed.flags.tags);
|
|
26
35
|
const resolvedTags = await resolveCommandTags(requestedTags);
|
|
27
36
|
if (resolvedTags.length === 0) {
|
|
@@ -51,7 +60,7 @@ async function publishCommand(argv) {
|
|
|
51
60
|
}
|
|
52
61
|
}
|
|
53
62
|
try {
|
|
54
|
-
publishPackage(resolvedTag, packageManager, { dryRun,
|
|
63
|
+
publishPackage(resolvedTag, packageManager, { dryRun, provenance });
|
|
55
64
|
published.push(resolvedTag.tag);
|
|
56
65
|
} finally {
|
|
57
66
|
if (readmePath !== void 0 && originalReadme !== void 0) {
|