@williamthorsen/release-kit 4.5.0 → 4.6.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 +118 -95
- package/LICENSE +4 -4
- package/cliff.toml.template +66 -54
- package/dist/esm/.cache +1 -1
- package/dist/esm/assertCleanWorkingTree.d.ts +1 -0
- package/dist/esm/assertCleanWorkingTree.js +15 -0
- package/dist/esm/bin/release-kit.js +29 -6
- package/dist/esm/changelogJsonUtils.d.ts +4 -0
- package/dist/esm/changelogJsonUtils.js +29 -0
- package/dist/esm/createGithubRelease.d.ts +11 -0
- package/dist/esm/createGithubRelease.js +54 -0
- package/dist/esm/defaults.d.ts +3 -1
- package/dist/esm/defaults.js +14 -0
- package/dist/esm/generateChangelogJson.d.ts +7 -0
- package/dist/esm/generateChangelogJson.js +196 -0
- package/dist/esm/generateChangelogs.js +25 -9
- package/dist/esm/githubReleaseCommand.d.ts +1 -0
- package/dist/esm/githubReleaseCommand.js +35 -0
- package/dist/esm/index.d.ts +11 -3
- package/dist/esm/index.js +27 -3
- package/dist/esm/injectReleaseNotesIntoReadme.d.ts +2 -0
- package/dist/esm/injectReleaseNotesIntoReadme.js +42 -0
- package/dist/esm/injectSection.d.ts +1 -0
- package/dist/esm/injectSection.js +32 -0
- package/dist/esm/loadConfig.js +35 -3
- package/dist/esm/parseCommitMessage.js +1 -1
- package/dist/esm/prepareCommand.d.ts +1 -0
- package/dist/esm/prepareCommand.js +54 -20
- package/dist/esm/publish.d.ts +1 -1
- package/dist/esm/publish.js +7 -26
- package/dist/esm/publishCommand.js +49 -29
- package/dist/esm/releasePrepare.js +12 -1
- package/dist/esm/releasePrepareMono.js +23 -0
- package/dist/esm/renderReleaseNotes.d.ts +8 -0
- package/dist/esm/renderReleaseNotes.js +40 -0
- package/dist/esm/resolveCommandTags.d.ts +2 -0
- package/dist/esm/resolveCommandTags.js +36 -0
- package/dist/esm/resolveReleaseNotesConfig.d.ts +6 -0
- package/dist/esm/resolveReleaseNotesConfig.js +37 -0
- package/dist/esm/sync-labels/generateCommand.d.ts +1 -1
- package/dist/esm/sync-labels/generateCommand.js +13 -7
- package/dist/esm/sync-labels/presets.d.ts +1 -0
- package/dist/esm/sync-labels/presets.js +10 -0
- package/dist/esm/sync-labels/templates.js +5 -1
- package/dist/esm/typeGuards.d.ts +1 -0
- package/dist/esm/typeGuards.js +5 -1
- package/dist/esm/types.d.ts +29 -0
- package/dist/esm/validateConfig.d.ts +1 -0
- package/dist/esm/validateConfig.js +89 -6
- package/dist/esm/version.d.ts +1 -1
- package/dist/esm/version.js +1 -1
- package/package.json +4 -3
- package/presets/labels/common.yaml +43 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
function openMarker(key) {
|
|
2
|
+
return `<!-- section:${key} -->`;
|
|
3
|
+
}
|
|
4
|
+
function closeMarker(key) {
|
|
5
|
+
return `<!-- /section:${key} -->`;
|
|
6
|
+
}
|
|
7
|
+
function injectSection(content, key, injection) {
|
|
8
|
+
const open = openMarker(key);
|
|
9
|
+
const close = closeMarker(key);
|
|
10
|
+
const openIndex = content.indexOf(open);
|
|
11
|
+
const closeIndex = content.indexOf(close);
|
|
12
|
+
if (openIndex !== -1 && closeIndex !== -1 && closeIndex > openIndex) {
|
|
13
|
+
const before = content.slice(0, openIndex + open.length);
|
|
14
|
+
const after = content.slice(closeIndex);
|
|
15
|
+
return `${before}
|
|
16
|
+
${injection}
|
|
17
|
+
${after}`;
|
|
18
|
+
}
|
|
19
|
+
const section = `${open}
|
|
20
|
+
${injection}
|
|
21
|
+
${close}`;
|
|
22
|
+
if (content.length === 0) {
|
|
23
|
+
return `${section}
|
|
24
|
+
`;
|
|
25
|
+
}
|
|
26
|
+
return `${section}
|
|
27
|
+
|
|
28
|
+
${content}`;
|
|
29
|
+
}
|
|
30
|
+
export {
|
|
31
|
+
injectSection
|
|
32
|
+
};
|
package/dist/esm/loadConfig.js
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { component } from "./component.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
6
|
+
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
7
|
+
DEFAULT_VERSION_PATTERNS,
|
|
8
|
+
DEFAULT_WORK_TYPES
|
|
9
|
+
} from "./defaults.js";
|
|
5
10
|
import { isRecord } from "./typeGuards.js";
|
|
6
11
|
const CONFIG_FILE_PATH = ".config/release-kit.config.ts";
|
|
7
12
|
async function loadConfig() {
|
|
@@ -34,10 +39,14 @@ function mergeMonorepoConfig(discoveredPaths, userConfig) {
|
|
|
34
39
|
}
|
|
35
40
|
const workTypes = userConfig?.workTypes === void 0 ? { ...DEFAULT_WORK_TYPES } : { ...DEFAULT_WORK_TYPES, ...userConfig.workTypes };
|
|
36
41
|
const versionPatterns = userConfig?.versionPatterns === void 0 ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
|
|
42
|
+
const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
|
|
43
|
+
const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
|
|
37
44
|
const result = {
|
|
38
45
|
components,
|
|
39
46
|
workTypes,
|
|
40
|
-
versionPatterns
|
|
47
|
+
versionPatterns,
|
|
48
|
+
changelogJson,
|
|
49
|
+
releaseNotes
|
|
41
50
|
};
|
|
42
51
|
const formatCommand = userConfig?.formatCommand;
|
|
43
52
|
if (formatCommand !== void 0) {
|
|
@@ -56,12 +65,16 @@ function mergeMonorepoConfig(discoveredPaths, userConfig) {
|
|
|
56
65
|
function mergeSinglePackageConfig(userConfig) {
|
|
57
66
|
const workTypes = userConfig?.workTypes === void 0 ? { ...DEFAULT_WORK_TYPES } : { ...DEFAULT_WORK_TYPES, ...userConfig.workTypes };
|
|
58
67
|
const versionPatterns = userConfig?.versionPatterns === void 0 ? { ...DEFAULT_VERSION_PATTERNS } : { ...userConfig.versionPatterns };
|
|
68
|
+
const changelogJson = mergeChangelogJsonConfig(userConfig?.changelogJson);
|
|
69
|
+
const releaseNotes = mergeReleaseNotesConfig(userConfig?.releaseNotes);
|
|
59
70
|
const result = {
|
|
60
71
|
tagPrefix: "v",
|
|
61
72
|
packageFiles: ["package.json"],
|
|
62
73
|
changelogPaths: ["."],
|
|
63
74
|
workTypes,
|
|
64
|
-
versionPatterns
|
|
75
|
+
versionPatterns,
|
|
76
|
+
changelogJson,
|
|
77
|
+
releaseNotes
|
|
65
78
|
};
|
|
66
79
|
const formatCommand = userConfig?.formatCommand;
|
|
67
80
|
if (formatCommand !== void 0) {
|
|
@@ -77,6 +90,25 @@ function mergeSinglePackageConfig(userConfig) {
|
|
|
77
90
|
}
|
|
78
91
|
return result;
|
|
79
92
|
}
|
|
93
|
+
function mergeChangelogJsonConfig(partial) {
|
|
94
|
+
if (partial === void 0) {
|
|
95
|
+
return { ...DEFAULT_CHANGELOG_JSON_CONFIG };
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
enabled: partial.enabled ?? DEFAULT_CHANGELOG_JSON_CONFIG.enabled,
|
|
99
|
+
outputPath: partial.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath,
|
|
100
|
+
devOnlySections: partial.devOnlySections ?? [...DEFAULT_CHANGELOG_JSON_CONFIG.devOnlySections]
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function mergeReleaseNotesConfig(partial) {
|
|
104
|
+
if (partial === void 0) {
|
|
105
|
+
return { ...DEFAULT_RELEASE_NOTES_CONFIG };
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
shouldInjectIntoReadme: partial.shouldInjectIntoReadme ?? DEFAULT_RELEASE_NOTES_CONFIG.shouldInjectIntoReadme,
|
|
109
|
+
shouldCreateGithubRelease: partial.shouldCreateGithubRelease ?? DEFAULT_RELEASE_NOTES_CONFIG.shouldCreateGithubRelease
|
|
110
|
+
};
|
|
111
|
+
}
|
|
80
112
|
export {
|
|
81
113
|
CONFIG_FILE_PATH,
|
|
82
114
|
loadConfig,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const COMMIT_PREPROCESSOR_PATTERNS = [/^#\d
|
|
1
|
+
const COMMIT_PREPROCESSOR_PATTERNS = [/^#\d+([.-]\d+)?\s+/, /^[A-Z]+-\d+\s+/];
|
|
2
2
|
function parseCommitMessage(message, hash, workTypes, scopeAliases) {
|
|
3
3
|
const stripped = stripTicketPrefix(message);
|
|
4
4
|
const match = stripped.match(/^(?:([^|]+)\|)?(\w+)(?:\(([^)]+)\))?(!)?:\s*(.*)$/);
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
translateParseError,
|
|
4
4
|
writeFileWithCheck
|
|
5
5
|
} from "@williamthorsen/node-monorepo-core";
|
|
6
|
+
import { assertCleanWorkingTree } from "./assertCleanWorkingTree.js";
|
|
6
7
|
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
7
8
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
8
9
|
import { dim } from "./format.js";
|
|
@@ -25,6 +26,7 @@ Options:
|
|
|
25
26
|
--dry-run Run without modifying any files
|
|
26
27
|
--bump=major|minor|patch Override the bump type for all components
|
|
27
28
|
--force Bypass the "no commits since last tag" check (monorepo only, requires --bump)
|
|
29
|
+
--no-git-checks, -n Skip the clean-working-tree check
|
|
28
30
|
--only=name1,name2 Only process the named components (comma-separated, monorepo only)
|
|
29
31
|
--help Show this help message
|
|
30
32
|
`);
|
|
@@ -32,6 +34,11 @@ Options:
|
|
|
32
34
|
const prepareFlagSchema = {
|
|
33
35
|
dryRun: { long: "--dry-run", type: "boolean" },
|
|
34
36
|
force: { long: "--force", type: "boolean" },
|
|
37
|
+
noGitChecks: {
|
|
38
|
+
long: "--no-git-checks",
|
|
39
|
+
type: "boolean",
|
|
40
|
+
short: "-n"
|
|
41
|
+
},
|
|
35
42
|
bump: { long: "--bump", type: "string" },
|
|
36
43
|
only: { long: "--only", type: "string" },
|
|
37
44
|
help: { long: "--help", type: "boolean", short: "-h" }
|
|
@@ -62,21 +69,31 @@ function parseArgs(argv) {
|
|
|
62
69
|
if (flags.force && bumpOverride === void 0) {
|
|
63
70
|
throw new Error("--force requires --bump to specify the version bump type");
|
|
64
71
|
}
|
|
65
|
-
return {
|
|
72
|
+
return {
|
|
73
|
+
dryRun: flags.dryRun,
|
|
74
|
+
force: flags.force,
|
|
75
|
+
noGitChecks: flags.noGitChecks,
|
|
76
|
+
bumpOverride,
|
|
77
|
+
only
|
|
78
|
+
};
|
|
66
79
|
}
|
|
67
80
|
function writeReleaseTags(tags, dryRun) {
|
|
68
81
|
if (tags.length === 0) {
|
|
69
82
|
return void 0;
|
|
70
83
|
}
|
|
71
|
-
return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join("\n"), {
|
|
84
|
+
return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join("\n"), {
|
|
85
|
+
dryRun,
|
|
86
|
+
overwrite: true
|
|
87
|
+
});
|
|
72
88
|
}
|
|
73
89
|
async function prepareCommand(argv) {
|
|
74
90
|
let dryRun;
|
|
75
91
|
let force;
|
|
92
|
+
let noGitChecks;
|
|
76
93
|
let bumpOverride;
|
|
77
94
|
let only;
|
|
78
95
|
try {
|
|
79
|
-
({ dryRun, force, bumpOverride, only } = parseArgs(argv));
|
|
96
|
+
({ dryRun, force, noGitChecks, bumpOverride, only } = parseArgs(argv));
|
|
80
97
|
} catch (error) {
|
|
81
98
|
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
99
|
process.exit(1);
|
|
@@ -89,25 +106,15 @@ async function prepareCommand(argv) {
|
|
|
89
106
|
if (dryRun) {
|
|
90
107
|
console.info("\n\u{1F50D} DRY RUN \u2014 no files will be modified\n");
|
|
91
108
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
let userConfig;
|
|
100
|
-
if (rawConfig !== void 0) {
|
|
101
|
-
const { config, errors } = validateConfig(rawConfig);
|
|
102
|
-
if (errors.length > 0) {
|
|
103
|
-
console.error("Invalid config:");
|
|
104
|
-
for (const err of errors) {
|
|
105
|
-
console.error(` \u274C ${err}`);
|
|
106
|
-
}
|
|
109
|
+
if (!dryRun && !noGitChecks) {
|
|
110
|
+
try {
|
|
111
|
+
assertCleanWorkingTree();
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
107
114
|
process.exit(1);
|
|
108
115
|
}
|
|
109
|
-
userConfig = config;
|
|
110
116
|
}
|
|
117
|
+
const userConfig = await loadAndValidateConfig();
|
|
111
118
|
let discoveredPaths;
|
|
112
119
|
try {
|
|
113
120
|
discoveredPaths = await discoverWorkspaces();
|
|
@@ -137,6 +144,30 @@ async function prepareCommand(argv) {
|
|
|
137
144
|
runAndReport(() => releasePrepareMono(config, options), dryRun);
|
|
138
145
|
}
|
|
139
146
|
}
|
|
147
|
+
async function loadAndValidateConfig() {
|
|
148
|
+
let rawConfig;
|
|
149
|
+
try {
|
|
150
|
+
rawConfig = await loadConfig();
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.error(`Error loading config: ${error instanceof Error ? error.message : String(error)}`);
|
|
153
|
+
process.exit(1);
|
|
154
|
+
}
|
|
155
|
+
if (rawConfig === void 0) {
|
|
156
|
+
return void 0;
|
|
157
|
+
}
|
|
158
|
+
const { config, errors, warnings } = validateConfig(rawConfig);
|
|
159
|
+
if (errors.length > 0) {
|
|
160
|
+
console.error("Invalid config:");
|
|
161
|
+
for (const err of errors) {
|
|
162
|
+
console.error(` \u274C ${err}`);
|
|
163
|
+
}
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
for (const warning of warnings) {
|
|
167
|
+
console.warn(` \u26A0\uFE0F ${warning}`);
|
|
168
|
+
}
|
|
169
|
+
return config;
|
|
170
|
+
}
|
|
140
171
|
function runAndReport(execute, dryRun) {
|
|
141
172
|
let result;
|
|
142
173
|
try {
|
|
@@ -162,7 +193,10 @@ function runAndReport(execute, dryRun) {
|
|
|
162
193
|
}
|
|
163
194
|
const summary = buildReleaseSummary(result);
|
|
164
195
|
if (summary.length > 0) {
|
|
165
|
-
const summaryResult = writeFileWithCheck(RELEASE_SUMMARY_FILE, summary, {
|
|
196
|
+
const summaryResult = writeFileWithCheck(RELEASE_SUMMARY_FILE, summary, {
|
|
197
|
+
dryRun,
|
|
198
|
+
overwrite: true
|
|
199
|
+
});
|
|
166
200
|
if (summaryResult.outcome === "failed") {
|
|
167
201
|
console.error(`Error writing release summary: ${summaryResult.error ?? "unknown error"}`);
|
|
168
202
|
process.exit(1);
|
package/dist/esm/publish.d.ts
CHANGED
|
@@ -5,4 +5,4 @@ export interface PublishOptions {
|
|
|
5
5
|
noGitChecks: boolean;
|
|
6
6
|
provenance: boolean;
|
|
7
7
|
}
|
|
8
|
-
export declare function
|
|
8
|
+
export declare function publishPackage(resolvedTag: ResolvedTag, packageManager: PackageManager, options: PublishOptions): void;
|
package/dist/esm/publish.js
CHANGED
|
@@ -1,32 +1,13 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
function
|
|
2
|
+
function publishPackage(resolvedTag, packageManager, options) {
|
|
3
3
|
const { dryRun, noGitChecks, provenance } = options;
|
|
4
|
-
if (resolvedTags.length === 0) {
|
|
5
|
-
return;
|
|
6
|
-
}
|
|
7
|
-
console.info(dryRun ? "[dry-run] Would publish:" : "Publishing:");
|
|
8
|
-
for (const { tag, workspacePath } of resolvedTags) {
|
|
9
|
-
console.info(` ${tag} (${workspacePath})`);
|
|
10
|
-
}
|
|
11
|
-
const published = [];
|
|
12
4
|
const executable = resolveExecutable(packageManager);
|
|
13
5
|
const args = buildPublishArgs(packageManager, { dryRun, noGitChecks, provenance });
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
published.push(tag);
|
|
20
|
-
} catch (error) {
|
|
21
|
-
if (published.length > 0) {
|
|
22
|
-
console.warn("Packages published before failure:");
|
|
23
|
-
for (const t of published) {
|
|
24
|
-
console.warn(` ${t}`);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
throw error;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
6
|
+
console.info(
|
|
7
|
+
`
|
|
8
|
+
${dryRun ? "[dry-run] " : ""}Running: ${executable} ${args.join(" ")} (cwd: ${resolvedTag.workspacePath})`
|
|
9
|
+
);
|
|
10
|
+
execFileSync(executable, args, { cwd: resolvedTag.workspacePath, stdio: "inherit" });
|
|
30
11
|
}
|
|
31
12
|
function resolveExecutable(packageManager) {
|
|
32
13
|
if (packageManager === "yarn-berry") {
|
|
@@ -48,5 +29,5 @@ function buildPublishArgs(packageManager, options) {
|
|
|
48
29
|
return args;
|
|
49
30
|
}
|
|
50
31
|
export {
|
|
51
|
-
|
|
32
|
+
publishPackage
|
|
52
33
|
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
2
3
|
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
4
|
+
import { createGithubReleases } from "./createGithubRelease.js";
|
|
3
5
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
6
|
+
import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
|
|
7
|
+
import { publishPackage } from "./publish.js";
|
|
8
|
+
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
9
|
+
import { resolveReleaseNotesConfig } from "./resolveReleaseNotesConfig.js";
|
|
7
10
|
const publishFlagSchema = {
|
|
8
11
|
dryRun: { long: "--dry-run", type: "boolean" },
|
|
9
12
|
noGitChecks: { long: "--no-git-checks", type: "boolean" },
|
|
@@ -20,38 +23,55 @@ async function publishCommand(argv) {
|
|
|
20
23
|
}
|
|
21
24
|
const { dryRun, noGitChecks, provenance } = parsed.flags;
|
|
22
25
|
const only = parsed.flags.only?.split(",");
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
discoveredPaths = await discoverWorkspaces();
|
|
26
|
-
} catch (error) {
|
|
27
|
-
console.error(`Error discovering workspaces: ${error instanceof Error ? error.message : String(error)}`);
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
if (only !== void 0 && discoveredPaths === void 0) {
|
|
31
|
-
console.error("Error: --only is only supported for monorepo configurations");
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
const workspaceMap = discoveredPaths === void 0 ? void 0 : new Map(discoveredPaths.map((p) => [basename(p), p]));
|
|
35
|
-
let resolvedTags = resolveReleaseTags(workspaceMap);
|
|
26
|
+
const resolvedTags = await resolveCommandTags(only);
|
|
36
27
|
if (resolvedTags.length === 0) {
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const packageManager = detectPackageManager();
|
|
31
|
+
const { releaseNotes, changelogJsonOutputPath } = await resolveReleaseNotesConfig();
|
|
32
|
+
const shouldInject = releaseNotes.shouldInjectIntoReadme;
|
|
33
|
+
console.info(dryRun ? "[dry-run] Would publish:" : "Publishing:");
|
|
34
|
+
for (const { tag, workspacePath } of resolvedTags) {
|
|
35
|
+
console.info(` ${tag} (${workspacePath})`);
|
|
39
36
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
for (const
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
const published = [];
|
|
38
|
+
try {
|
|
39
|
+
for (const resolvedTag of resolvedTags) {
|
|
40
|
+
let readmePath;
|
|
41
|
+
let originalReadme;
|
|
42
|
+
if (shouldInject) {
|
|
43
|
+
readmePath = resolveReadmePath(resolvedTag.workspacePath);
|
|
44
|
+
if (readmePath !== void 0) {
|
|
45
|
+
originalReadme = injectReleaseNotesIntoReadme(
|
|
46
|
+
readmePath,
|
|
47
|
+
join(resolvedTag.workspacePath, changelogJsonOutputPath),
|
|
48
|
+
resolvedTag.tag
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
publishPackage(resolvedTag, packageManager, { dryRun, noGitChecks, provenance });
|
|
54
|
+
published.push(resolvedTag.tag);
|
|
55
|
+
} finally {
|
|
56
|
+
if (readmePath !== void 0 && originalReadme !== void 0) {
|
|
57
|
+
writeFileSync(readmePath, originalReadme, "utf8");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (published.length > 0) {
|
|
63
|
+
console.warn("Packages published before failure:");
|
|
64
|
+
for (const t of published) {
|
|
65
|
+
console.warn(` ${t}`);
|
|
46
66
|
}
|
|
47
67
|
}
|
|
48
|
-
|
|
68
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
69
|
+
process.exit(1);
|
|
49
70
|
}
|
|
50
|
-
const packageManager = detectPackageManager();
|
|
51
71
|
try {
|
|
52
|
-
|
|
72
|
+
createGithubReleases(resolvedTags, releaseNotes, changelogJsonOutputPath, dryRun);
|
|
53
73
|
} catch (error) {
|
|
54
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
74
|
+
console.error(`Error creating GitHub Releases: ${error instanceof Error ? error.message : String(error)}`);
|
|
55
75
|
process.exit(1);
|
|
56
76
|
}
|
|
57
77
|
}
|
|
@@ -2,6 +2,7 @@ import { execSync } from "node:child_process";
|
|
|
2
2
|
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
3
3
|
import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
4
4
|
import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
|
|
5
|
+
import { generateChangelogJson } from "./generateChangelogJson.js";
|
|
5
6
|
import { generateChangelogs } from "./generateChangelogs.js";
|
|
6
7
|
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
7
8
|
import { hasPrettierConfig } from "./hasPrettierConfig.js";
|
|
@@ -42,10 +43,20 @@ function releasePrepare(config, options) {
|
|
|
42
43
|
const bump = bumpAllVersions(config.packageFiles, releaseType, dryRun);
|
|
43
44
|
const newTag = `${config.tagPrefix}${bump.newVersion}`;
|
|
44
45
|
const changelogFiles = generateChangelogs(config, newTag, dryRun);
|
|
46
|
+
const changelogJsonFiles = [];
|
|
47
|
+
if (config.changelogJson.enabled) {
|
|
48
|
+
for (const changelogPath of config.changelogPaths) {
|
|
49
|
+
changelogJsonFiles.push(...generateChangelogJson(config, changelogPath, newTag, dryRun));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
45
52
|
const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? "npx prettier --write" : void 0);
|
|
46
53
|
let formatCommand;
|
|
47
54
|
if (formatCommandStr !== void 0) {
|
|
48
|
-
const modifiedFiles = [
|
|
55
|
+
const modifiedFiles = [
|
|
56
|
+
...config.packageFiles,
|
|
57
|
+
...config.changelogPaths.map((p) => `${p}/CHANGELOG.md`),
|
|
58
|
+
...changelogJsonFiles
|
|
59
|
+
];
|
|
49
60
|
const fullCommand = `${formatCommandStr} ${modifiedFiles.join(" ")}`;
|
|
50
61
|
if (dryRun) {
|
|
51
62
|
formatCommand = { command: fullCommand, executed: false, files: modifiedFiles };
|
|
@@ -4,6 +4,7 @@ import { buildDependencyGraph } from "./buildDependencyGraph.js";
|
|
|
4
4
|
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
5
5
|
import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
6
6
|
import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
|
|
7
|
+
import { generateChangelogJson, generateSyntheticChangelogJson } from "./generateChangelogJson.js";
|
|
7
8
|
import { buildTagPattern, generateChangelog } from "./generateChangelogs.js";
|
|
8
9
|
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
9
10
|
import { hasPrettierConfig } from "./hasPrettierConfig.js";
|
|
@@ -169,6 +170,19 @@ function executeReleaseSet(sortedDirs, fullReleaseSet, config, directResults, pr
|
|
|
169
170
|
})
|
|
170
171
|
);
|
|
171
172
|
}
|
|
173
|
+
if (config.changelogJson.enabled) {
|
|
174
|
+
for (const changelogPath of component.changelogPaths) {
|
|
175
|
+
const jsonFiles = generateSyntheticChangelogJson(
|
|
176
|
+
config,
|
|
177
|
+
changelogPath,
|
|
178
|
+
bump.newVersion,
|
|
179
|
+
today,
|
|
180
|
+
releaseEntry.propagatedFrom,
|
|
181
|
+
dryRun
|
|
182
|
+
);
|
|
183
|
+
modifiedFiles.push(...jsonFiles);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
172
186
|
} else {
|
|
173
187
|
for (const changelogPath of component.changelogPaths) {
|
|
174
188
|
changelogFiles.push(
|
|
@@ -178,6 +192,15 @@ function executeReleaseSet(sortedDirs, fullReleaseSet, config, directResults, pr
|
|
|
178
192
|
})
|
|
179
193
|
);
|
|
180
194
|
}
|
|
195
|
+
if (config.changelogJson.enabled) {
|
|
196
|
+
for (const changelogPath of component.changelogPaths) {
|
|
197
|
+
const jsonFiles = generateChangelogJson(config, changelogPath, newTag, dryRun, {
|
|
198
|
+
tagPattern: buildTagPattern(component.tagPrefix),
|
|
199
|
+
includePaths: component.paths
|
|
200
|
+
});
|
|
201
|
+
modifiedFiles.push(...jsonFiles);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
181
204
|
}
|
|
182
205
|
components.push({
|
|
183
206
|
name: dir,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChangelogAudience, ChangelogEntry, ChangelogSection } from './types.ts';
|
|
2
|
+
export interface RenderOptions {
|
|
3
|
+
filter?: (section: ChangelogSection) => boolean;
|
|
4
|
+
includeHeading?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function matchesAudience(audience: ChangelogAudience): (section: ChangelogSection) => boolean;
|
|
7
|
+
export declare function renderReleaseNotesSingle(entry: ChangelogEntry, options?: RenderOptions): string;
|
|
8
|
+
export declare function renderReleaseNotesMulti(entries: ChangelogEntry[], options?: RenderOptions): string;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
function allSections() {
|
|
2
|
+
return true;
|
|
3
|
+
}
|
|
4
|
+
function publicSections(section) {
|
|
5
|
+
return section.audience === "all";
|
|
6
|
+
}
|
|
7
|
+
function matchesAudience(audience) {
|
|
8
|
+
return audience === "dev" ? allSections : publicSections;
|
|
9
|
+
}
|
|
10
|
+
function renderReleaseNotesSingle(entry, options) {
|
|
11
|
+
const filter = options?.filter;
|
|
12
|
+
const includeHeading = options?.includeHeading ?? true;
|
|
13
|
+
const sections = filter !== void 0 ? entry.sections.filter(filter) : entry.sections;
|
|
14
|
+
if (sections.length === 0) {
|
|
15
|
+
return "";
|
|
16
|
+
}
|
|
17
|
+
const lines = [];
|
|
18
|
+
if (includeHeading) {
|
|
19
|
+
lines.push(`## ${entry.version} \u2014 ${entry.date}`);
|
|
20
|
+
}
|
|
21
|
+
for (const section of sections) {
|
|
22
|
+
if (lines.length > 0) {
|
|
23
|
+
lines.push("");
|
|
24
|
+
}
|
|
25
|
+
lines.push(`### ${section.title}`, "");
|
|
26
|
+
for (const item of section.items) {
|
|
27
|
+
lines.push(`- ${item.description}`);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return lines.join("\n") + "\n";
|
|
31
|
+
}
|
|
32
|
+
function renderReleaseNotesMulti(entries, options) {
|
|
33
|
+
const parts = entries.map((entry) => renderReleaseNotesSingle(entry, options)).filter((part) => part.length > 0);
|
|
34
|
+
return parts.join("\n");
|
|
35
|
+
}
|
|
36
|
+
export {
|
|
37
|
+
matchesAudience,
|
|
38
|
+
renderReleaseNotesMulti,
|
|
39
|
+
renderReleaseNotesSingle
|
|
40
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { basename } from "node:path";
|
|
2
|
+
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
3
|
+
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
4
|
+
async function resolveCommandTags(only) {
|
|
5
|
+
let discoveredPaths;
|
|
6
|
+
try {
|
|
7
|
+
discoveredPaths = await discoverWorkspaces();
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.error(`Error discovering workspaces: ${error instanceof Error ? error.message : String(error)}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
if (only !== void 0 && discoveredPaths === void 0) {
|
|
13
|
+
console.error("Error: --only is only supported for monorepo configurations");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
const workspaceMap = discoveredPaths === void 0 ? void 0 : new Map(discoveredPaths.map((p) => [basename(p), p]));
|
|
17
|
+
let resolvedTags = resolveReleaseTags(workspaceMap);
|
|
18
|
+
if (resolvedTags.length === 0) {
|
|
19
|
+
console.error("Error: No release tags found on HEAD. Create tags with `release-kit tag` first.");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
if (only !== void 0) {
|
|
23
|
+
const availableNames = resolvedTags.map((t) => t.dir);
|
|
24
|
+
for (const name of only) {
|
|
25
|
+
if (!availableNames.includes(name)) {
|
|
26
|
+
console.error(`Error: Unknown package "${name}" in --only. Available: ${availableNames.join(", ")}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
resolvedTags = resolvedTags.filter((t) => only.includes(t.dir));
|
|
31
|
+
}
|
|
32
|
+
return resolvedTags;
|
|
33
|
+
}
|
|
34
|
+
export {
|
|
35
|
+
resolveCommandTags
|
|
36
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG } from "./defaults.js";
|
|
2
|
+
import { loadConfig } from "./loadConfig.js";
|
|
3
|
+
import { validateConfig } from "./validateConfig.js";
|
|
4
|
+
async function resolveReleaseNotesConfig() {
|
|
5
|
+
let rawConfig;
|
|
6
|
+
try {
|
|
7
|
+
rawConfig = await loadConfig();
|
|
8
|
+
} catch (error) {
|
|
9
|
+
console.warn(
|
|
10
|
+
`Warning: failed to load config; using defaults: ${error instanceof Error ? error.message : String(error)}`
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
if (rawConfig === void 0) {
|
|
14
|
+
return {
|
|
15
|
+
releaseNotes: { ...DEFAULT_RELEASE_NOTES_CONFIG },
|
|
16
|
+
changelogJsonOutputPath: DEFAULT_CHANGELOG_JSON_CONFIG.outputPath
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const { config, errors, warnings } = validateConfig(rawConfig);
|
|
20
|
+
if (errors.length > 0) {
|
|
21
|
+
console.error("Invalid config:");
|
|
22
|
+
for (const err of errors) {
|
|
23
|
+
console.error(` \u274C ${err}`);
|
|
24
|
+
}
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
27
|
+
for (const warning of warnings) {
|
|
28
|
+
console.warn(` \u26A0\uFE0F ${warning}`);
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
releaseNotes: { ...DEFAULT_RELEASE_NOTES_CONFIG, ...config.releaseNotes },
|
|
32
|
+
changelogJsonOutputPath: config.changelogJson?.outputPath ?? DEFAULT_CHANGELOG_JSON_CONFIG.outputPath
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export {
|
|
36
|
+
resolveReleaseNotesConfig
|
|
37
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { LabelDefinition } from './types.ts';
|
|
2
2
|
export declare const LABELS_OUTPUT_PATH = ".github/labels.yaml";
|
|
3
|
-
export declare function formatLabelsYaml(labels: LabelDefinition[]): string;
|
|
3
|
+
export declare function formatLabelsYaml(labels: LabelDefinition[], presetHashes: Map<string, string>): string;
|
|
4
4
|
export declare function generateCommand(): Promise<number>;
|
|
@@ -2,14 +2,16 @@ import { mkdirSync, writeFileSync } from "node:fs";
|
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { dump } from "js-yaml";
|
|
4
4
|
import { loadSyncLabelsConfig, SYNC_LABELS_CONFIG_PATH } from "./loadSyncLabelsConfig.js";
|
|
5
|
+
import { hashPresetFile } from "./presets.js";
|
|
5
6
|
import { resolveLabels } from "./resolveLabels.js";
|
|
6
7
|
const LABELS_OUTPUT_PATH = ".github/labels.yaml";
|
|
7
|
-
|
|
8
|
-
# Source: ${SYNC_LABELS_CONFIG_PATH}
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
function formatLabelsYaml(labels, presetHashes) {
|
|
9
|
+
const headerLines = ["# Generated by release-kit sync-labels \u2014 do not edit.", `# Source: ${SYNC_LABELS_CONFIG_PATH}`];
|
|
10
|
+
for (const [name, hash] of [...presetHashes.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
11
|
+
headerLines.push(`# ${name} preset hash: ${hash}`);
|
|
12
|
+
}
|
|
13
|
+
const yamlBody = dump(labels, { quotingType: "'", forceQuotes: false, lineWidth: -1 });
|
|
14
|
+
return headerLines.join("\n") + "\n" + yamlBody;
|
|
13
15
|
}
|
|
14
16
|
async function generateCommand() {
|
|
15
17
|
let config;
|
|
@@ -32,7 +34,11 @@ async function generateCommand() {
|
|
|
32
34
|
console.error(`Error resolving labels: ${message}`);
|
|
33
35
|
return 1;
|
|
34
36
|
}
|
|
35
|
-
const
|
|
37
|
+
const presetHashes = /* @__PURE__ */ new Map();
|
|
38
|
+
for (const presetName of config.presets ?? []) {
|
|
39
|
+
presetHashes.set(presetName, hashPresetFile(presetName));
|
|
40
|
+
}
|
|
41
|
+
const content = formatLabelsYaml(labels, presetHashes);
|
|
36
42
|
try {
|
|
37
43
|
mkdirSync(dirname(LABELS_OUTPUT_PATH), { recursive: true });
|
|
38
44
|
writeFileSync(LABELS_OUTPUT_PATH, content, "utf8");
|