@williamthorsen/release-kit 4.5.1 → 4.7.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 +122 -99
- package/LICENSE +4 -4
- package/cliff.toml.template +68 -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 +60 -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 +12 -6
- package/dist/esm/sync-labels/presets.d.ts +1 -0
- package/dist/esm/sync-labels/presets.js +10 -0
- 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
- package/dist/esm/init/prompt.d.ts +0 -4
- package/dist/esm/init/prompt.js +0 -19
- package/dist/esm/runReleasePrepare.d.ts +0 -10
- package/dist/esm/runReleasePrepare.js +0 -125
- package/dist/esm/sync-labels/scaffold.d.ts +0 -6
- package/dist/esm/sync-labels/scaffold.js +0 -25
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
3
3
|
import { commitCommand } from "../commitCommand.js";
|
|
4
|
+
import { githubReleaseCommand } from "../githubReleaseCommand.js";
|
|
4
5
|
import { initCommand } from "../init/initCommand.js";
|
|
5
6
|
import { prepareCommand } from "../prepareCommand.js";
|
|
6
7
|
import { publishCommand } from "../publishCommand.js";
|
|
@@ -14,12 +15,13 @@ function showUsage() {
|
|
|
14
15
|
Usage: release-kit <command> [options]
|
|
15
16
|
|
|
16
17
|
Commands:
|
|
17
|
-
prepare
|
|
18
|
-
commit
|
|
19
|
-
tag
|
|
20
|
-
publish
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
prepare Run release preparation (auto-discovers workspaces)
|
|
19
|
+
commit Stage changes and create the release commit
|
|
20
|
+
tag Create annotated git tags from the tags file
|
|
21
|
+
publish Publish packages with release tags on HEAD
|
|
22
|
+
github-release Create GitHub Releases from changelog.json for tags on HEAD
|
|
23
|
+
init Initialize release-kit in the current repository
|
|
24
|
+
sync-labels Manage GitHub label synchronization
|
|
23
25
|
|
|
24
26
|
Options:
|
|
25
27
|
--dry-run Preview changes without writing files
|
|
@@ -97,6 +99,7 @@ Run release preparation with automatic workspace discovery.
|
|
|
97
99
|
Options:
|
|
98
100
|
--dry-run Run without modifying any files
|
|
99
101
|
--bump=major|minor|patch Override the bump type for all components
|
|
102
|
+
--no-git-checks, -n Skip the clean-working-tree check
|
|
100
103
|
--only=name1,name2 Only process the named components (comma-separated, monorepo only)
|
|
101
104
|
--help, -h Show this help message
|
|
102
105
|
`);
|
|
@@ -125,6 +128,18 @@ Options:
|
|
|
125
128
|
--help, -h Show this help message
|
|
126
129
|
`);
|
|
127
130
|
}
|
|
131
|
+
function showGithubReleaseHelp() {
|
|
132
|
+
console.info(`
|
|
133
|
+
Usage: release-kit github-release [options]
|
|
134
|
+
|
|
135
|
+
Create GitHub Releases from changelog.json for tags on HEAD.
|
|
136
|
+
|
|
137
|
+
Options:
|
|
138
|
+
--dry-run Preview without creating releases
|
|
139
|
+
--only=name1,name2 Only create releases for the named packages (comma-separated, monorepo only)
|
|
140
|
+
--help, -h Show this help message
|
|
141
|
+
`);
|
|
142
|
+
}
|
|
128
143
|
function showPublishHelp() {
|
|
129
144
|
console.info(`
|
|
130
145
|
Usage: release-kit publish [options]
|
|
@@ -179,6 +194,14 @@ if (command === "tag") {
|
|
|
179
194
|
tagCommand(flags);
|
|
180
195
|
process.exit(0);
|
|
181
196
|
}
|
|
197
|
+
if (command === "github-release") {
|
|
198
|
+
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
199
|
+
showGithubReleaseHelp();
|
|
200
|
+
process.exit(0);
|
|
201
|
+
}
|
|
202
|
+
await githubReleaseCommand(flags);
|
|
203
|
+
process.exit(0);
|
|
204
|
+
}
|
|
182
205
|
if (command === "publish") {
|
|
183
206
|
if (flags.some((f) => f === "--help" || f === "-h")) {
|
|
184
207
|
showPublishHelp();
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ChangelogEntry } from './types.ts';
|
|
2
|
+
export declare function isChangelogEntry(value: unknown): value is ChangelogEntry;
|
|
3
|
+
export declare function extractVersion(tag: string): string;
|
|
4
|
+
export declare function readChangelogEntries(filePath: string): ChangelogEntry[] | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { isRecord, isUnknownArray } from "./typeGuards.js";
|
|
3
|
+
function isChangelogEntry(value) {
|
|
4
|
+
return isRecord(value) && typeof value.version === "string" && typeof value.date === "string" && isUnknownArray(value.sections);
|
|
5
|
+
}
|
|
6
|
+
function extractVersion(tag) {
|
|
7
|
+
const match = /(\d+\.\d+\.\d+.*)$/.exec(tag);
|
|
8
|
+
return match?.[1] ?? tag;
|
|
9
|
+
}
|
|
10
|
+
function readChangelogEntries(filePath) {
|
|
11
|
+
if (!existsSync(filePath)) {
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const content = readFileSync(filePath, "utf8");
|
|
16
|
+
const parsed = JSON.parse(content);
|
|
17
|
+
if (!isUnknownArray(parsed)) {
|
|
18
|
+
return void 0;
|
|
19
|
+
}
|
|
20
|
+
return parsed.filter(isChangelogEntry);
|
|
21
|
+
} catch {
|
|
22
|
+
return void 0;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export {
|
|
26
|
+
extractVersion,
|
|
27
|
+
isChangelogEntry,
|
|
28
|
+
readChangelogEntries
|
|
29
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ReleaseNotesConfig } from './types.ts';
|
|
2
|
+
export interface CreateGithubReleaseOptions {
|
|
3
|
+
tag: string;
|
|
4
|
+
changelogJsonPath: string;
|
|
5
|
+
dryRun: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function createGithubRelease(options: CreateGithubReleaseOptions): boolean;
|
|
8
|
+
export declare function createGithubReleases(tags: Array<{
|
|
9
|
+
tag: string;
|
|
10
|
+
workspacePath: string;
|
|
11
|
+
}>, releaseNotes: ReleaseNotesConfig, changelogJsonOutputPath: string, dryRun: boolean): void;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { extractVersion, readChangelogEntries } from "./changelogJsonUtils.js";
|
|
5
|
+
import { matchesAudience, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
6
|
+
function createGithubRelease(options) {
|
|
7
|
+
const { tag, changelogJsonPath, dryRun } = options;
|
|
8
|
+
if (!existsSync(changelogJsonPath)) {
|
|
9
|
+
console.warn(`Warning: ${changelogJsonPath} not found; skipping GitHub Release creation`);
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
const version = extractVersion(tag);
|
|
13
|
+
const entries = readChangelogEntries(changelogJsonPath);
|
|
14
|
+
if (entries === void 0) {
|
|
15
|
+
console.warn(`Warning: could not parse ${changelogJsonPath}; skipping GitHub Release creation`);
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
const entry = entries.find((e) => e.version === version);
|
|
19
|
+
if (entry === void 0) {
|
|
20
|
+
console.warn(`Warning: no changelog entry for version ${version}; skipping GitHub Release creation`);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (!entry.sections.some(matchesAudience("all"))) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
const body = renderReleaseNotesSingle(entry, {
|
|
27
|
+
filter: matchesAudience("all"),
|
|
28
|
+
includeHeading: false
|
|
29
|
+
});
|
|
30
|
+
if (body.trim() === "") {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const args = ["release", "create", tag, "--title", tag, "--notes", body];
|
|
34
|
+
if (dryRun) {
|
|
35
|
+
console.info(`[dry-run] Would run: gh ${args.join(" ")}`);
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
execFileSync("gh", args, { stdio: "inherit" });
|
|
40
|
+
return true;
|
|
41
|
+
} catch (error) {
|
|
42
|
+
console.warn(
|
|
43
|
+
`Warning: failed to create GitHub Release for ${tag}: ${error instanceof Error ? error.message : String(error)}`
|
|
44
|
+
);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function createGithubReleases(tags, releaseNotes, changelogJsonOutputPath, dryRun) {
|
|
49
|
+
if (!releaseNotes.shouldCreateGithubRelease) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
for (const { tag, workspacePath } of tags) {
|
|
53
|
+
const changelogJsonPath = join(workspacePath, changelogJsonOutputPath);
|
|
54
|
+
createGithubRelease({ tag, changelogJsonPath, dryRun });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
export {
|
|
58
|
+
createGithubRelease,
|
|
59
|
+
createGithubReleases
|
|
60
|
+
};
|
package/dist/esm/defaults.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import type { VersionPatterns, WorkTypeConfig } from './types.ts';
|
|
1
|
+
import type { ChangelogJsonConfig, ReleaseNotesConfig, VersionPatterns, WorkTypeConfig } from './types.ts';
|
|
2
2
|
export declare const DEFAULT_WORK_TYPES: Record<string, WorkTypeConfig>;
|
|
3
3
|
export declare const DEFAULT_VERSION_PATTERNS: VersionPatterns;
|
|
4
|
+
export declare const DEFAULT_CHANGELOG_JSON_CONFIG: ChangelogJsonConfig;
|
|
5
|
+
export declare const DEFAULT_RELEASE_NOTES_CONFIG: ReleaseNotesConfig;
|
package/dist/esm/defaults.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
const DEFAULT_WORK_TYPES = {
|
|
2
2
|
fix: { header: "Bug fixes", aliases: ["bugfix"] },
|
|
3
|
+
deprecate: { header: "Deprecated" },
|
|
3
4
|
feat: { header: "Features", aliases: ["feature"] },
|
|
4
5
|
internal: { header: "Internal" },
|
|
6
|
+
perf: { header: "Performance", aliases: ["performance"] },
|
|
5
7
|
refactor: { header: "Refactoring" },
|
|
8
|
+
sec: { header: "Security", aliases: ["security"] },
|
|
6
9
|
tests: { header: "Tests", aliases: ["test"] },
|
|
7
10
|
tooling: { header: "Tooling" },
|
|
8
11
|
ci: { header: "CI" },
|
|
@@ -14,7 +17,18 @@ const DEFAULT_VERSION_PATTERNS = {
|
|
|
14
17
|
major: ["!"],
|
|
15
18
|
minor: ["feat"]
|
|
16
19
|
};
|
|
20
|
+
const DEFAULT_CHANGELOG_JSON_CONFIG = {
|
|
21
|
+
enabled: true,
|
|
22
|
+
outputPath: ".meta/changelog.json",
|
|
23
|
+
devOnlySections: ["CI", "Dependencies", "Formatting", "Internal", "Tests", "Tooling"]
|
|
24
|
+
};
|
|
25
|
+
const DEFAULT_RELEASE_NOTES_CONFIG = {
|
|
26
|
+
shouldInjectIntoReadme: false,
|
|
27
|
+
shouldCreateGithubRelease: false
|
|
28
|
+
};
|
|
17
29
|
export {
|
|
30
|
+
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
31
|
+
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
18
32
|
DEFAULT_VERSION_PATTERNS,
|
|
19
33
|
DEFAULT_WORK_TYPES
|
|
20
34
|
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { GenerateChangelogOptions } from './generateChangelogs.ts';
|
|
2
|
+
import type { ReleaseConfig } from './types.ts';
|
|
3
|
+
export declare function generateChangelogJson(config: Pick<ReleaseConfig, 'cliffConfigPath' | 'changelogJson'>, changelogPath: string, tag: string, dryRun: boolean, options?: GenerateChangelogOptions): string[];
|
|
4
|
+
export declare function generateSyntheticChangelogJson(config: Pick<ReleaseConfig, 'changelogJson'>, changelogPath: string, newVersion: string, date: string, propagatedFrom: Array<{
|
|
5
|
+
packageName: string;
|
|
6
|
+
newVersion: string;
|
|
7
|
+
}>, dryRun: boolean): string[];
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import stringify from "json-stringify-pretty-compact";
|
|
6
|
+
import { extractVersion, isChangelogEntry } from "./changelogJsonUtils.js";
|
|
7
|
+
import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
|
|
8
|
+
import { isRecord, isUnknownArray } from "./typeGuards.js";
|
|
9
|
+
function generateChangelogJson(config, changelogPath, tag, dryRun, options) {
|
|
10
|
+
const outputFile = join(changelogPath, config.changelogJson.outputPath);
|
|
11
|
+
if (dryRun) {
|
|
12
|
+
return [outputFile];
|
|
13
|
+
}
|
|
14
|
+
const resolvedConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
|
|
15
|
+
let cliffConfigPath = resolvedConfigPath;
|
|
16
|
+
let tempDir;
|
|
17
|
+
if (resolvedConfigPath.endsWith(".template")) {
|
|
18
|
+
tempDir = mkdtempSync(join(tmpdir(), "cliff-"));
|
|
19
|
+
cliffConfigPath = join(tempDir, "cliff.toml");
|
|
20
|
+
copyFileSync(resolvedConfigPath, cliffConfigPath);
|
|
21
|
+
}
|
|
22
|
+
const args = ["--config", cliffConfigPath, "--context", "--tag", tag];
|
|
23
|
+
if (options?.tagPattern !== void 0) {
|
|
24
|
+
args.push("--tag-pattern", options.tagPattern);
|
|
25
|
+
}
|
|
26
|
+
for (const includePath of options?.includePaths ?? []) {
|
|
27
|
+
args.push("--include-path", includePath);
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const contextJson = execFileSync("npx", ["--yes", "git-cliff", ...args], {
|
|
31
|
+
encoding: "utf8",
|
|
32
|
+
stdio: ["pipe", "pipe", "inherit"]
|
|
33
|
+
});
|
|
34
|
+
const releases = parseCliffContext(contextJson);
|
|
35
|
+
const devOnlySections = new Set(config.changelogJson.devOnlySections);
|
|
36
|
+
const entries = transformReleases(releases, devOnlySections);
|
|
37
|
+
const existingEntries = readExistingEntries(outputFile);
|
|
38
|
+
const merged = mergeEntries(entries, existingEntries);
|
|
39
|
+
mkdirSync(dirname(outputFile), { recursive: true });
|
|
40
|
+
writeFileSync(outputFile, stringify(merged, { maxLength: 100 }) + "\n", "utf8");
|
|
41
|
+
return [outputFile];
|
|
42
|
+
} catch (error) {
|
|
43
|
+
throw new Error(
|
|
44
|
+
`Failed to generate changelog JSON for ${outputFile}: ${error instanceof Error ? error.message : String(error)}`
|
|
45
|
+
);
|
|
46
|
+
} finally {
|
|
47
|
+
if (tempDir !== void 0) {
|
|
48
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function generateSyntheticChangelogJson(config, changelogPath, newVersion, date, propagatedFrom, dryRun) {
|
|
53
|
+
const outputFile = join(changelogPath, config.changelogJson.outputPath);
|
|
54
|
+
if (dryRun) {
|
|
55
|
+
return [outputFile];
|
|
56
|
+
}
|
|
57
|
+
const items = propagatedFrom.map((dep) => ({
|
|
58
|
+
description: `Bumped \`${dep.packageName}\` to ${dep.newVersion}`
|
|
59
|
+
}));
|
|
60
|
+
const entry = {
|
|
61
|
+
version: newVersion,
|
|
62
|
+
date,
|
|
63
|
+
sections: [{ title: "Dependency updates", audience: "dev", items }]
|
|
64
|
+
};
|
|
65
|
+
const existingEntries = readExistingEntries(outputFile);
|
|
66
|
+
const merged = mergeEntries([entry], existingEntries);
|
|
67
|
+
mkdirSync(dirname(outputFile), { recursive: true });
|
|
68
|
+
writeFileSync(outputFile, stringify(merged, { maxLength: 100 }) + "\n", "utf8");
|
|
69
|
+
return [outputFile];
|
|
70
|
+
}
|
|
71
|
+
function parseCliffContext(json) {
|
|
72
|
+
const parsed = JSON.parse(json);
|
|
73
|
+
if (!isUnknownArray(parsed)) {
|
|
74
|
+
throw new TypeError("Expected git-cliff --context output to be an array");
|
|
75
|
+
}
|
|
76
|
+
return parsed.map(toCliffContextRelease);
|
|
77
|
+
}
|
|
78
|
+
function toCliffContextRelease(value) {
|
|
79
|
+
if (!isRecord(value)) {
|
|
80
|
+
return {};
|
|
81
|
+
}
|
|
82
|
+
const release = {};
|
|
83
|
+
if (typeof value.version === "string") {
|
|
84
|
+
release.version = value.version;
|
|
85
|
+
}
|
|
86
|
+
if (typeof value.timestamp === "number") {
|
|
87
|
+
release.timestamp = value.timestamp;
|
|
88
|
+
}
|
|
89
|
+
if (isUnknownArray(value.commits)) {
|
|
90
|
+
release.commits = value.commits.map(toCliffContextCommit);
|
|
91
|
+
}
|
|
92
|
+
return release;
|
|
93
|
+
}
|
|
94
|
+
function toCliffContextCommit(value) {
|
|
95
|
+
if (!isRecord(value)) {
|
|
96
|
+
return { message: "" };
|
|
97
|
+
}
|
|
98
|
+
const commit = {
|
|
99
|
+
message: typeof value.message === "string" ? value.message : ""
|
|
100
|
+
};
|
|
101
|
+
if (typeof value.group === "string") {
|
|
102
|
+
commit.group = value.group;
|
|
103
|
+
}
|
|
104
|
+
return commit;
|
|
105
|
+
}
|
|
106
|
+
function transformReleases(releases, devOnlySections) {
|
|
107
|
+
const entries = [];
|
|
108
|
+
for (const release of releases) {
|
|
109
|
+
if (release.version === void 0) {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
const version = extractVersion(release.version);
|
|
113
|
+
const date = release.timestamp !== void 0 ? new Date(release.timestamp * 1e3).toISOString().slice(0, 10) : "unreleased";
|
|
114
|
+
const sectionMap = /* @__PURE__ */ new Map();
|
|
115
|
+
for (const commit of release.commits ?? []) {
|
|
116
|
+
const group = commit.group ?? "Other";
|
|
117
|
+
const description = extractDescription(commit.message);
|
|
118
|
+
let items = sectionMap.get(group);
|
|
119
|
+
if (items === void 0) {
|
|
120
|
+
items = [];
|
|
121
|
+
sectionMap.set(group, items);
|
|
122
|
+
}
|
|
123
|
+
items.push({ description });
|
|
124
|
+
}
|
|
125
|
+
const sections = [];
|
|
126
|
+
for (const [title, items] of sectionMap) {
|
|
127
|
+
if (items.length === 0) {
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
sections.push({
|
|
131
|
+
title,
|
|
132
|
+
audience: devOnlySections.has(title) ? "dev" : "all",
|
|
133
|
+
items
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
if (sections.length > 0) {
|
|
137
|
+
entries.push({ version, date, sections });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return entries;
|
|
141
|
+
}
|
|
142
|
+
function extractDescription(message) {
|
|
143
|
+
const firstLine = message.split("\n")[0] ?? message;
|
|
144
|
+
const afterColon = firstLine.split(": ").slice(1).join(": ");
|
|
145
|
+
if (afterColon.length > 0) {
|
|
146
|
+
return afterColon.charAt(0).toUpperCase() + afterColon.slice(1);
|
|
147
|
+
}
|
|
148
|
+
return firstLine;
|
|
149
|
+
}
|
|
150
|
+
function readExistingEntries(filePath) {
|
|
151
|
+
if (!existsSync(filePath)) {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
const content = readFileSync(filePath, "utf8");
|
|
156
|
+
const parsed = JSON.parse(content);
|
|
157
|
+
if (!isUnknownArray(parsed)) {
|
|
158
|
+
return [];
|
|
159
|
+
}
|
|
160
|
+
return parsed.filter(isChangelogEntry);
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.warn(
|
|
163
|
+
`Warning: could not parse existing ${filePath}: ${error instanceof Error ? error.message : String(error)}; treating as empty`
|
|
164
|
+
);
|
|
165
|
+
return [];
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function mergeEntries(newEntries, existingEntries) {
|
|
169
|
+
const versionMap = /* @__PURE__ */ new Map();
|
|
170
|
+
for (const entry of existingEntries) {
|
|
171
|
+
versionMap.set(entry.version, entry);
|
|
172
|
+
}
|
|
173
|
+
for (const entry of newEntries) {
|
|
174
|
+
versionMap.set(entry.version, entry);
|
|
175
|
+
}
|
|
176
|
+
return [...versionMap.values()].sort((a, b) => compareVersionsDescending(a.version, b.version));
|
|
177
|
+
}
|
|
178
|
+
function parseVersionParts(version) {
|
|
179
|
+
return version.split(".").map((s) => {
|
|
180
|
+
const n = Number(s);
|
|
181
|
+
return Number.isNaN(n) ? 0 : n;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
function compareVersionsDescending(a, b) {
|
|
185
|
+
const partsA = parseVersionParts(a);
|
|
186
|
+
const partsB = parseVersionParts(b);
|
|
187
|
+
for (let i = 0; i < 3; i++) {
|
|
188
|
+
const diff = (partsB[i] ?? 0) - (partsA[i] ?? 0);
|
|
189
|
+
if (diff !== 0) return diff;
|
|
190
|
+
}
|
|
191
|
+
return 0;
|
|
192
|
+
}
|
|
193
|
+
export {
|
|
194
|
+
generateChangelogJson,
|
|
195
|
+
generateSyntheticChangelogJson
|
|
196
|
+
};
|
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { copyFileSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
2
5
|
import { resolveCliffConfigPath } from "./resolveCliffConfigPath.js";
|
|
3
6
|
function buildTagPattern(tagPrefix) {
|
|
4
7
|
return `${tagPrefix}[0-9].*`;
|
|
5
8
|
}
|
|
6
9
|
function generateChangelog(config, changelogPath, tag, dryRun, options) {
|
|
7
|
-
const
|
|
10
|
+
const resolvedConfigPath = resolveCliffConfigPath(config.cliffConfigPath, import.meta.url);
|
|
11
|
+
let cliffConfigPath = resolvedConfigPath;
|
|
12
|
+
let tempDir;
|
|
13
|
+
if (resolvedConfigPath.endsWith(".template")) {
|
|
14
|
+
tempDir = mkdtempSync(join(tmpdir(), "cliff-"));
|
|
15
|
+
cliffConfigPath = join(tempDir, "cliff.toml");
|
|
16
|
+
copyFileSync(resolvedConfigPath, cliffConfigPath);
|
|
17
|
+
}
|
|
8
18
|
const outputFile = `${changelogPath}/CHANGELOG.md`;
|
|
9
19
|
const args = ["--config", cliffConfigPath, "--output", outputFile, "--tag", tag];
|
|
10
20
|
if (options?.tagPattern !== void 0) {
|
|
@@ -13,16 +23,22 @@ function generateChangelog(config, changelogPath, tag, dryRun, options) {
|
|
|
13
23
|
for (const includePath of options?.includePaths ?? []) {
|
|
14
24
|
args.push("--include-path", includePath);
|
|
15
25
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
try {
|
|
27
|
+
if (!dryRun) {
|
|
28
|
+
try {
|
|
29
|
+
execFileSync("npx", ["--yes", "git-cliff", ...args], { stdio: "inherit" });
|
|
30
|
+
} catch (error) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Failed to generate changelog for ${outputFile}: ${error instanceof Error ? error.message : String(error)}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return [outputFile];
|
|
37
|
+
} finally {
|
|
38
|
+
if (tempDir !== void 0) {
|
|
39
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
23
40
|
}
|
|
24
41
|
}
|
|
25
|
-
return [outputFile];
|
|
26
42
|
}
|
|
27
43
|
function generateChangelogs(config, tag, dryRun) {
|
|
28
44
|
const tagPattern = buildTagPattern(config.tagPrefix);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function githubReleaseCommand(argv: string[]): Promise<void>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
2
|
+
import { createGithubReleases } from "./createGithubRelease.js";
|
|
3
|
+
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
4
|
+
import { resolveReleaseNotesConfig } from "./resolveReleaseNotesConfig.js";
|
|
5
|
+
const githubReleaseFlagSchema = {
|
|
6
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
7
|
+
only: { long: "--only", type: "string" }
|
|
8
|
+
};
|
|
9
|
+
async function githubReleaseCommand(argv) {
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = parseArgs(argv, githubReleaseFlagSchema);
|
|
13
|
+
} catch (error) {
|
|
14
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
const { dryRun } = parsed.flags;
|
|
18
|
+
const only = parsed.flags.only?.split(",");
|
|
19
|
+
const resolvedTags = await resolveCommandTags(only);
|
|
20
|
+
const { releaseNotes, changelogJsonOutputPath } = await resolveReleaseNotesConfig();
|
|
21
|
+
try {
|
|
22
|
+
createGithubReleases(
|
|
23
|
+
resolvedTags,
|
|
24
|
+
{ ...releaseNotes, shouldCreateGithubRelease: true },
|
|
25
|
+
changelogJsonOutputPath,
|
|
26
|
+
dryRun
|
|
27
|
+
);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.error(`Error creating GitHub Releases: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
githubReleaseCommand
|
|
35
|
+
};
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -5,25 +5,33 @@ export type { PublishOptions } from './publish.ts';
|
|
|
5
5
|
export type { ReleasePrepareOptions } from './releasePrepare.ts';
|
|
6
6
|
export type { ResolvedTag } from './resolveReleaseTags.ts';
|
|
7
7
|
export type { LabelDefinition, SyncLabelsConfig } from './sync-labels/types.ts';
|
|
8
|
-
export type { BumpResult, Commit, ComponentConfig, ComponentOverride, ComponentPrepareResult, MonorepoReleaseConfig, ParsedCommit, PrepareResult, ReleaseConfig, ReleaseKitConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
|
|
9
|
-
export { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from './defaults.ts';
|
|
8
|
+
export type { BumpResult, ChangelogAudience, ChangelogEntry, ChangelogItem, ChangelogJsonConfig, ChangelogSection, Commit, ComponentConfig, ComponentOverride, ComponentPrepareResult, MonorepoReleaseConfig, ParsedCommit, PrepareResult, ReleaseConfig, ReleaseKitConfig, ReleaseNotesConfig, ReleaseType, VersionPatterns, WorkTypeConfig, } from './types.ts';
|
|
9
|
+
export { DEFAULT_CHANGELOG_JSON_CONFIG, DEFAULT_RELEASE_NOTES_CONFIG, DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES, } from './defaults.ts';
|
|
10
10
|
export { buildReleaseSummary } from './buildReleaseSummary.ts';
|
|
11
11
|
export { bumpAllVersions } from './bumpAllVersions.ts';
|
|
12
12
|
export { bumpVersion } from './bumpVersion.ts';
|
|
13
13
|
export { commitCommand } from './commitCommand.ts';
|
|
14
14
|
export { component } from './component.ts';
|
|
15
|
+
export type { CreateGithubReleaseOptions } from './createGithubRelease.ts';
|
|
16
|
+
export { createGithubRelease, createGithubReleases } from './createGithubRelease.ts';
|
|
15
17
|
export { createTags } from './createTags.ts';
|
|
16
18
|
export { deleteFileIfExists } from './deleteFileIfExists.ts';
|
|
17
19
|
export { detectPackageManager } from './detectPackageManager.ts';
|
|
18
20
|
export { determineBumpType } from './determineBumpType.ts';
|
|
19
21
|
export { discoverWorkspaces } from './discoverWorkspaces.ts';
|
|
22
|
+
export { generateChangelogJson, generateSyntheticChangelogJson } from './generateChangelogJson.ts';
|
|
20
23
|
export { generateChangelog, generateChangelogs } from './generateChangelogs.ts';
|
|
21
24
|
export { getCommitsSinceTarget } from './getCommitsSinceTarget.ts';
|
|
25
|
+
export { injectReleaseNotesIntoReadme, resolveReadmePath } from './injectReleaseNotesIntoReadme.ts';
|
|
26
|
+
export { injectSection } from './injectSection.ts';
|
|
22
27
|
export { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from './parseCommitMessage.ts';
|
|
23
28
|
export { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from './prepareCommand.ts';
|
|
24
|
-
export {
|
|
29
|
+
export { publishPackage } from './publish.ts';
|
|
25
30
|
export { releasePrepare } from './releasePrepare.ts';
|
|
26
31
|
export { releasePrepareMono } from './releasePrepareMono.ts';
|
|
32
|
+
export type { RenderOptions } from './renderReleaseNotes.ts';
|
|
33
|
+
export { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from './renderReleaseNotes.ts';
|
|
27
34
|
export { reportPrepare } from './reportPrepare.ts';
|
|
35
|
+
export { resolveCommandTags } from './resolveCommandTags.ts';
|
|
28
36
|
export { resolveReleaseTags } from './resolveReleaseTags.ts';
|
|
29
37
|
export { stripScope } from './stripScope.ts';
|
package/dist/esm/index.js
CHANGED
|
@@ -1,26 +1,39 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
3
|
+
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
4
|
+
DEFAULT_VERSION_PATTERNS,
|
|
5
|
+
DEFAULT_WORK_TYPES
|
|
6
|
+
} from "./defaults.js";
|
|
2
7
|
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
3
8
|
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
4
9
|
import { bumpVersion } from "./bumpVersion.js";
|
|
5
10
|
import { commitCommand } from "./commitCommand.js";
|
|
6
11
|
import { component } from "./component.js";
|
|
12
|
+
import { createGithubRelease, createGithubReleases } from "./createGithubRelease.js";
|
|
7
13
|
import { createTags } from "./createTags.js";
|
|
8
14
|
import { deleteFileIfExists } from "./deleteFileIfExists.js";
|
|
9
15
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
10
16
|
import { determineBumpType } from "./determineBumpType.js";
|
|
11
17
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
18
|
+
import { generateChangelogJson, generateSyntheticChangelogJson } from "./generateChangelogJson.js";
|
|
12
19
|
import { generateChangelog, generateChangelogs } from "./generateChangelogs.js";
|
|
13
20
|
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
21
|
+
import { injectReleaseNotesIntoReadme, resolveReadmePath } from "./injectReleaseNotesIntoReadme.js";
|
|
22
|
+
import { injectSection } from "./injectSection.js";
|
|
14
23
|
import { COMMIT_PREPROCESSOR_PATTERNS, parseCommitMessage } from "./parseCommitMessage.js";
|
|
15
24
|
import { RELEASE_SUMMARY_FILE, RELEASE_TAGS_FILE, writeReleaseTags } from "./prepareCommand.js";
|
|
16
|
-
import {
|
|
25
|
+
import { publishPackage } from "./publish.js";
|
|
17
26
|
import { releasePrepare } from "./releasePrepare.js";
|
|
18
27
|
import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
28
|
+
import { matchesAudience, renderReleaseNotesMulti, renderReleaseNotesSingle } from "./renderReleaseNotes.js";
|
|
19
29
|
import { reportPrepare } from "./reportPrepare.js";
|
|
30
|
+
import { resolveCommandTags } from "./resolveCommandTags.js";
|
|
20
31
|
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
21
32
|
import { stripScope } from "./stripScope.js";
|
|
22
33
|
export {
|
|
23
34
|
COMMIT_PREPROCESSOR_PATTERNS,
|
|
35
|
+
DEFAULT_CHANGELOG_JSON_CONFIG,
|
|
36
|
+
DEFAULT_RELEASE_NOTES_CONFIG,
|
|
24
37
|
DEFAULT_VERSION_PATTERNS,
|
|
25
38
|
DEFAULT_WORK_TYPES,
|
|
26
39
|
RELEASE_SUMMARY_FILE,
|
|
@@ -30,19 +43,30 @@ export {
|
|
|
30
43
|
bumpVersion,
|
|
31
44
|
commitCommand,
|
|
32
45
|
component,
|
|
46
|
+
createGithubRelease,
|
|
47
|
+
createGithubReleases,
|
|
33
48
|
createTags,
|
|
34
49
|
deleteFileIfExists,
|
|
35
50
|
detectPackageManager,
|
|
36
51
|
determineBumpType,
|
|
37
52
|
discoverWorkspaces,
|
|
38
53
|
generateChangelog,
|
|
54
|
+
generateChangelogJson,
|
|
39
55
|
generateChangelogs,
|
|
56
|
+
generateSyntheticChangelogJson,
|
|
40
57
|
getCommitsSinceTarget,
|
|
58
|
+
injectReleaseNotesIntoReadme,
|
|
59
|
+
injectSection,
|
|
60
|
+
matchesAudience,
|
|
41
61
|
parseCommitMessage,
|
|
42
|
-
|
|
62
|
+
publishPackage,
|
|
43
63
|
releasePrepare,
|
|
44
64
|
releasePrepareMono,
|
|
65
|
+
renderReleaseNotesMulti,
|
|
66
|
+
renderReleaseNotesSingle,
|
|
45
67
|
reportPrepare,
|
|
68
|
+
resolveCommandTags,
|
|
69
|
+
resolveReadmePath,
|
|
46
70
|
resolveReleaseTags,
|
|
47
71
|
stripScope,
|
|
48
72
|
writeReleaseTags
|