@williamthorsen/release-kit 2.3.2 → 4.4.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 +90 -24
- package/README.md +123 -173
- package/bin/release-kit.js +10 -1
- package/cliff.toml.template +13 -12
- package/dist/esm/.cache +1 -1
- package/dist/esm/bin/release-kit.js +55 -13
- package/dist/esm/buildDependencyGraph.d.ts +7 -0
- package/dist/esm/buildDependencyGraph.js +59 -0
- package/dist/esm/buildReleaseSummary.d.ts +2 -0
- package/dist/esm/buildReleaseSummary.js +22 -0
- package/dist/esm/commitCommand.d.ts +1 -0
- package/dist/esm/commitCommand.js +60 -0
- package/dist/esm/createTags.js +5 -13
- package/dist/esm/deleteFileIfExists.d.ts +1 -0
- package/dist/esm/deleteFileIfExists.js +14 -0
- package/dist/esm/determineBumpFromCommits.d.ts +1 -1
- package/dist/esm/determineBumpFromCommits.js +2 -2
- package/dist/esm/generateChangelogs.d.ts +2 -0
- package/dist/esm/generateChangelogs.js +9 -1
- package/dist/esm/index.d.ts +5 -1
- package/dist/esm/index.js +10 -1
- package/dist/esm/init/initCommand.js +4 -2
- package/dist/esm/init/scaffold.js +3 -2
- package/dist/esm/init/templates.d.ts +1 -0
- package/dist/esm/init/templates.js +24 -2
- package/dist/esm/loadConfig.js +6 -6
- package/dist/esm/parseCommitMessage.d.ts +1 -1
- package/dist/esm/parseCommitMessage.js +9 -7
- package/dist/esm/prepareCommand.d.ts +1 -0
- package/dist/esm/prepareCommand.js +64 -34
- package/dist/esm/propagateBumps.d.ts +8 -0
- package/dist/esm/propagateBumps.js +54 -0
- package/dist/esm/publish.d.ts +1 -0
- package/dist/esm/publish.js +6 -3
- package/dist/esm/publishCommand.js +15 -9
- package/dist/esm/releasePrepare.js +2 -1
- package/dist/esm/releasePrepareMono.js +237 -48
- package/dist/esm/reportPrepare.js +29 -3
- package/dist/esm/stripScope.d.ts +1 -0
- package/dist/esm/stripScope.js +24 -0
- package/dist/esm/sync-labels/templates.js +1 -1
- package/dist/esm/tagCommand.js +11 -6
- package/dist/esm/types.d.ts +11 -4
- package/dist/esm/validateConfig.js +6 -6
- package/dist/esm/version.d.ts +1 -0
- package/dist/esm/version.js +4 -0
- package/dist/esm/writeSyntheticChangelog.d.ts +9 -0
- package/dist/esm/writeSyntheticChangelog.js +27 -0
- package/package.json +2 -2
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
parseArgs as coreParseArgs,
|
|
3
|
+
translateParseError,
|
|
4
|
+
writeFileWithCheck
|
|
5
|
+
} from "@williamthorsen/node-monorepo-core";
|
|
6
|
+
import { buildReleaseSummary } from "./buildReleaseSummary.js";
|
|
2
7
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
3
8
|
import { dim } from "./format.js";
|
|
4
9
|
import { loadConfig, mergeMonorepoConfig, mergeSinglePackageConfig } from "./loadConfig.js";
|
|
@@ -7,6 +12,7 @@ import { releasePrepareMono } from "./releasePrepareMono.js";
|
|
|
7
12
|
import { reportPrepare } from "./reportPrepare.js";
|
|
8
13
|
import { validateConfig } from "./validateConfig.js";
|
|
9
14
|
const RELEASE_TAGS_FILE = "tmp/.release-tags";
|
|
15
|
+
const RELEASE_SUMMARY_FILE = "tmp/.release-summary";
|
|
10
16
|
const VALID_BUMP_TYPES = ["major", "minor", "patch"];
|
|
11
17
|
function isReleaseType(value) {
|
|
12
18
|
return VALID_BUMP_TYPES.includes(value);
|
|
@@ -23,43 +29,40 @@ Options:
|
|
|
23
29
|
--help Show this help message
|
|
24
30
|
`);
|
|
25
31
|
}
|
|
32
|
+
const prepareFlagSchema = {
|
|
33
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
34
|
+
force: { long: "--force", type: "boolean" },
|
|
35
|
+
bump: { long: "--bump", type: "string" },
|
|
36
|
+
only: { long: "--only", type: "string" },
|
|
37
|
+
help: { long: "--help", type: "boolean", short: "-h" }
|
|
38
|
+
};
|
|
26
39
|
function parseArgs(argv) {
|
|
27
|
-
let
|
|
28
|
-
|
|
40
|
+
let parsed;
|
|
41
|
+
try {
|
|
42
|
+
parsed = coreParseArgs(argv, prepareFlagSchema);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new Error(translateParseError(error));
|
|
45
|
+
}
|
|
46
|
+
const { flags } = parsed;
|
|
47
|
+
if (flags.help) {
|
|
48
|
+
showHelp();
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
29
51
|
let bumpOverride;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
dryRun = true;
|
|
34
|
-
} else if (arg === "--force") {
|
|
35
|
-
force = true;
|
|
36
|
-
} else if (arg.startsWith("--bump=")) {
|
|
37
|
-
const value = arg.slice("--bump=".length);
|
|
38
|
-
if (!isReleaseType(value)) {
|
|
39
|
-
console.error(`Error: Invalid bump type "${value}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
|
|
40
|
-
process.exit(1);
|
|
41
|
-
}
|
|
42
|
-
bumpOverride = value;
|
|
43
|
-
} else if (arg.startsWith("--only=")) {
|
|
44
|
-
const value = arg.slice("--only=".length);
|
|
45
|
-
if (!value) {
|
|
46
|
-
console.error("Error: --only requires a comma-separated list of component names");
|
|
47
|
-
process.exit(1);
|
|
48
|
-
}
|
|
49
|
-
only = value.split(",");
|
|
50
|
-
} else if (arg === "--help" || arg === "-h") {
|
|
51
|
-
showHelp();
|
|
52
|
-
process.exit(0);
|
|
53
|
-
} else {
|
|
54
|
-
console.error(`Error: Unknown argument: ${arg}`);
|
|
55
|
-
process.exit(1);
|
|
52
|
+
if (flags.bump !== void 0) {
|
|
53
|
+
if (!isReleaseType(flags.bump)) {
|
|
54
|
+
throw new Error(`Invalid bump type "${flags.bump}". Must be one of: ${VALID_BUMP_TYPES.join(", ")}`);
|
|
56
55
|
}
|
|
56
|
+
bumpOverride = flags.bump;
|
|
57
57
|
}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
let only;
|
|
59
|
+
if (flags.only !== void 0) {
|
|
60
|
+
only = flags.only.split(",");
|
|
61
|
+
}
|
|
62
|
+
if (flags.force && bumpOverride === void 0) {
|
|
63
|
+
throw new Error("--force requires --bump to specify the version bump type");
|
|
61
64
|
}
|
|
62
|
-
return { dryRun, force, bumpOverride, only };
|
|
65
|
+
return { dryRun: flags.dryRun, force: flags.force, bumpOverride, only };
|
|
63
66
|
}
|
|
64
67
|
function writeReleaseTags(tags, dryRun) {
|
|
65
68
|
if (tags.length === 0) {
|
|
@@ -68,7 +71,16 @@ function writeReleaseTags(tags, dryRun) {
|
|
|
68
71
|
return writeFileWithCheck(RELEASE_TAGS_FILE, tags.join("\n"), { dryRun, overwrite: true });
|
|
69
72
|
}
|
|
70
73
|
async function prepareCommand(argv) {
|
|
71
|
-
|
|
74
|
+
let dryRun;
|
|
75
|
+
let force;
|
|
76
|
+
let bumpOverride;
|
|
77
|
+
let only;
|
|
78
|
+
try {
|
|
79
|
+
({ dryRun, force, bumpOverride, only } = parseArgs(argv));
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
72
84
|
const options = {
|
|
73
85
|
dryRun,
|
|
74
86
|
force,
|
|
@@ -148,8 +160,26 @@ function runAndReport(execute, dryRun) {
|
|
|
148
160
|
Release tags file: ${RELEASE_TAGS_FILE}`));
|
|
149
161
|
}
|
|
150
162
|
}
|
|
163
|
+
const summary = buildReleaseSummary(result);
|
|
164
|
+
if (summary.length > 0) {
|
|
165
|
+
const summaryResult = writeFileWithCheck(RELEASE_SUMMARY_FILE, summary, { dryRun, overwrite: true });
|
|
166
|
+
if (summaryResult.outcome === "failed") {
|
|
167
|
+
console.error(`Error writing release summary: ${summaryResult.error ?? "unknown error"}`);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
if (dryRun) {
|
|
171
|
+
console.info(dim(` [dry-run] Would write ${RELEASE_SUMMARY_FILE}`));
|
|
172
|
+
} else {
|
|
173
|
+
console.info(dim(` Wrote ${RELEASE_SUMMARY_FILE}`));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (writeResult && !dryRun) {
|
|
177
|
+
console.error(`
|
|
178
|
+
Run 'release-kit commit' to create the release commit.`);
|
|
179
|
+
}
|
|
151
180
|
}
|
|
152
181
|
export {
|
|
182
|
+
RELEASE_SUMMARY_FILE,
|
|
153
183
|
RELEASE_TAGS_FILE,
|
|
154
184
|
parseArgs,
|
|
155
185
|
prepareCommand,
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DependencyGraph } from './buildDependencyGraph.ts';
|
|
2
|
+
import type { PropagationSource, ReleaseType } from './types.ts';
|
|
3
|
+
export interface ReleaseEntry {
|
|
4
|
+
releaseType: ReleaseType;
|
|
5
|
+
propagatedFrom?: PropagationSource[];
|
|
6
|
+
}
|
|
7
|
+
export type CurrentVersions = Map<string, string>;
|
|
8
|
+
export declare function propagateBumps(directBumps: Map<string, ReleaseEntry>, graph: DependencyGraph, currentVersions: CurrentVersions): Map<string, ReleaseEntry>;
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { bumpVersion } from "./bumpVersion.js";
|
|
2
|
+
function propagateBumps(directBumps, graph, currentVersions) {
|
|
3
|
+
const result = /* @__PURE__ */ new Map();
|
|
4
|
+
for (const [dir, entry] of directBumps) {
|
|
5
|
+
result.set(dir, { ...entry });
|
|
6
|
+
}
|
|
7
|
+
const queue = [...directBumps.keys()];
|
|
8
|
+
const visited = /* @__PURE__ */ new Set();
|
|
9
|
+
while (queue.length > 0) {
|
|
10
|
+
const dir = queue.shift();
|
|
11
|
+
if (dir === void 0) {
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
if (visited.has(dir)) {
|
|
15
|
+
continue;
|
|
16
|
+
}
|
|
17
|
+
visited.add(dir);
|
|
18
|
+
const packageName = graph.dirToPackageName.get(dir);
|
|
19
|
+
if (packageName === void 0) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
const currentVersion = currentVersions.get(dir);
|
|
23
|
+
const entry = result.get(dir);
|
|
24
|
+
if (currentVersion === void 0 || entry === void 0) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
const newVersion = bumpVersion(currentVersion, entry.releaseType);
|
|
28
|
+
const dependents = graph.dependentsOf.get(packageName);
|
|
29
|
+
if (dependents === void 0) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
for (const dependent of dependents) {
|
|
33
|
+
const dependentDir = dependent.dir;
|
|
34
|
+
const existing = result.get(dependentDir);
|
|
35
|
+
const propagationInfo = { packageName, newVersion };
|
|
36
|
+
if (existing === void 0) {
|
|
37
|
+
result.set(dependentDir, {
|
|
38
|
+
releaseType: "patch",
|
|
39
|
+
propagatedFrom: [propagationInfo]
|
|
40
|
+
});
|
|
41
|
+
} else {
|
|
42
|
+
const existingPropagated = existing.propagatedFrom ?? [];
|
|
43
|
+
existing.propagatedFrom = [...existingPropagated, propagationInfo];
|
|
44
|
+
}
|
|
45
|
+
if (!visited.has(dependentDir)) {
|
|
46
|
+
queue.push(dependentDir);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
propagateBumps
|
|
54
|
+
};
|
package/dist/esm/publish.d.ts
CHANGED
|
@@ -3,5 +3,6 @@ import type { ResolvedTag } from './resolveReleaseTags.ts';
|
|
|
3
3
|
export interface PublishOptions {
|
|
4
4
|
dryRun: boolean;
|
|
5
5
|
noGitChecks: boolean;
|
|
6
|
+
provenance: boolean;
|
|
6
7
|
}
|
|
7
8
|
export declare function publish(resolvedTags: ResolvedTag[], packageManager: PackageManager, options: PublishOptions): void;
|
package/dist/esm/publish.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
function publish(resolvedTags, packageManager, options) {
|
|
3
|
-
const { dryRun, noGitChecks } = options;
|
|
3
|
+
const { dryRun, noGitChecks, provenance } = options;
|
|
4
4
|
if (resolvedTags.length === 0) {
|
|
5
5
|
return;
|
|
6
6
|
}
|
|
@@ -9,9 +9,9 @@ function publish(resolvedTags, packageManager, options) {
|
|
|
9
9
|
console.info(` ${tag} (${workspacePath})`);
|
|
10
10
|
}
|
|
11
11
|
const published = [];
|
|
12
|
+
const executable = resolveExecutable(packageManager);
|
|
13
|
+
const args = buildPublishArgs(packageManager, { dryRun, noGitChecks, provenance });
|
|
12
14
|
for (const { tag, workspacePath } of resolvedTags) {
|
|
13
|
-
const executable = resolveExecutable(packageManager);
|
|
14
|
-
const args = buildPublishArgs(packageManager, { dryRun, noGitChecks });
|
|
15
15
|
try {
|
|
16
16
|
console.info(`
|
|
17
17
|
${dryRun ? "[dry-run] " : ""}Running: ${executable} ${args.join(" ")} (cwd: ${workspacePath})`);
|
|
@@ -42,6 +42,9 @@ function buildPublishArgs(packageManager, options) {
|
|
|
42
42
|
if (options.noGitChecks && packageManager === "pnpm") {
|
|
43
43
|
args.push("--no-git-checks");
|
|
44
44
|
}
|
|
45
|
+
if (options.provenance && packageManager !== "yarn") {
|
|
46
|
+
args.push("--provenance");
|
|
47
|
+
}
|
|
45
48
|
return args;
|
|
46
49
|
}
|
|
47
50
|
export {
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { basename } from "node:path";
|
|
2
|
+
import { parseArgs, translateParseError } from "@williamthorsen/node-monorepo-core";
|
|
2
3
|
import { detectPackageManager } from "./detectPackageManager.js";
|
|
3
4
|
import { discoverWorkspaces } from "./discoverWorkspaces.js";
|
|
4
5
|
import { publish } from "./publish.js";
|
|
5
6
|
import { resolveReleaseTags } from "./resolveReleaseTags.js";
|
|
7
|
+
const publishFlagSchema = {
|
|
8
|
+
dryRun: { long: "--dry-run", type: "boolean" },
|
|
9
|
+
noGitChecks: { long: "--no-git-checks", type: "boolean" },
|
|
10
|
+
provenance: { long: "--provenance", type: "boolean" },
|
|
11
|
+
only: { long: "--only", type: "string" }
|
|
12
|
+
};
|
|
6
13
|
async function publishCommand(argv) {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
14
|
+
let parsed;
|
|
15
|
+
try {
|
|
16
|
+
parsed = parseArgs(argv, publishFlagSchema);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
console.error(`Error: ${translateParseError(error)}`);
|
|
11
19
|
process.exit(1);
|
|
12
20
|
}
|
|
13
|
-
const dryRun =
|
|
14
|
-
const
|
|
15
|
-
const onlyArg = argv.find((f) => f.startsWith("--only="));
|
|
16
|
-
const only = onlyArg?.slice("--only=".length).split(",");
|
|
21
|
+
const { dryRun, noGitChecks, provenance } = parsed.flags;
|
|
22
|
+
const only = parsed.flags.only?.split(",");
|
|
17
23
|
let discoveredPaths;
|
|
18
24
|
try {
|
|
19
25
|
discoveredPaths = await discoverWorkspaces();
|
|
@@ -43,7 +49,7 @@ async function publishCommand(argv) {
|
|
|
43
49
|
}
|
|
44
50
|
const packageManager = detectPackageManager();
|
|
45
51
|
try {
|
|
46
|
-
publish(resolvedTags, packageManager, { dryRun, noGitChecks });
|
|
52
|
+
publish(resolvedTags, packageManager, { dryRun, noGitChecks, provenance });
|
|
47
53
|
} catch (error) {
|
|
48
54
|
console.error(error instanceof Error ? error.message : String(error));
|
|
49
55
|
process.exit(1);
|
|
@@ -14,7 +14,7 @@ function releasePrepare(config, options) {
|
|
|
14
14
|
let parsedCommitCount;
|
|
15
15
|
let unparseableCommits;
|
|
16
16
|
if (bumpOverride === void 0) {
|
|
17
|
-
const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.
|
|
17
|
+
const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.scopeAliases);
|
|
18
18
|
parsedCommitCount = determination.parsedCommitCount;
|
|
19
19
|
unparseableCommits = determination.unparseableCommits;
|
|
20
20
|
releaseType = determination.releaseType;
|
|
@@ -73,6 +73,7 @@ function releasePrepare(config, options) {
|
|
|
73
73
|
tag: newTag,
|
|
74
74
|
bumpedFiles: bump.files,
|
|
75
75
|
changelogFiles,
|
|
76
|
+
commits,
|
|
76
77
|
unparseableCommits
|
|
77
78
|
}
|
|
78
79
|
],
|
|
@@ -1,29 +1,84 @@
|
|
|
1
1
|
import { execSync } from "node:child_process";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { buildDependencyGraph } from "./buildDependencyGraph.js";
|
|
2
4
|
import { bumpAllVersions } from "./bumpAllVersions.js";
|
|
3
5
|
import { DEFAULT_VERSION_PATTERNS, DEFAULT_WORK_TYPES } from "./defaults.js";
|
|
4
6
|
import { determineBumpFromCommits } from "./determineBumpFromCommits.js";
|
|
5
|
-
import { generateChangelog } from "./generateChangelogs.js";
|
|
7
|
+
import { buildTagPattern, generateChangelog } from "./generateChangelogs.js";
|
|
6
8
|
import { getCommitsSinceTarget } from "./getCommitsSinceTarget.js";
|
|
7
9
|
import { hasPrettierConfig } from "./hasPrettierConfig.js";
|
|
10
|
+
import { propagateBumps } from "./propagateBumps.js";
|
|
11
|
+
import { writeSyntheticChangelog } from "./writeSyntheticChangelog.js";
|
|
8
12
|
function releasePrepareMono(config, options) {
|
|
9
|
-
const { dryRun
|
|
13
|
+
const { dryRun } = options;
|
|
14
|
+
const { directBumps, directResults, skippedResults, currentVersions } = determineDirectBumps(config, options);
|
|
15
|
+
const previousTags = /* @__PURE__ */ new Map();
|
|
16
|
+
for (const result of directResults.values()) {
|
|
17
|
+
previousTags.set(result.component.dir, result.tag);
|
|
18
|
+
}
|
|
19
|
+
for (const skipped of skippedResults) {
|
|
20
|
+
previousTags.set(skipped.component.dir, skipped.tag);
|
|
21
|
+
}
|
|
22
|
+
const graph = buildDependencyGraph(config.components);
|
|
23
|
+
const fullReleaseSet = propagateBumps(directBumps, graph, currentVersions);
|
|
24
|
+
const { sorted: sortedDirs, cyclicDirs } = topologicalSort(fullReleaseSet, graph);
|
|
25
|
+
const warnings = [];
|
|
26
|
+
if (cyclicDirs.length > 0) {
|
|
27
|
+
warnings.push(
|
|
28
|
+
`Circular workspace dependencies detected among: ${cyclicDirs.join(", ")}. Propagation metadata may be incomplete for these components.`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
const components = collectSkippedComponents(skippedResults, fullReleaseSet);
|
|
32
|
+
const { tags, modifiedFiles } = executeReleaseSet(
|
|
33
|
+
sortedDirs,
|
|
34
|
+
fullReleaseSet,
|
|
35
|
+
config,
|
|
36
|
+
directResults,
|
|
37
|
+
previousTags,
|
|
38
|
+
dryRun,
|
|
39
|
+
components
|
|
40
|
+
);
|
|
41
|
+
const configOrder = new Map(config.components.map((c, i) => [c.dir, i]));
|
|
42
|
+
components.sort((a, b) => {
|
|
43
|
+
const orderA = configOrder.get(a.name ?? "") ?? 0;
|
|
44
|
+
const orderB = configOrder.get(b.name ?? "") ?? 0;
|
|
45
|
+
return orderA - orderB;
|
|
46
|
+
});
|
|
47
|
+
const formatCommand = runFormatCommand(config, tags, modifiedFiles, dryRun);
|
|
48
|
+
return {
|
|
49
|
+
components,
|
|
50
|
+
tags,
|
|
51
|
+
formatCommand,
|
|
52
|
+
dryRun,
|
|
53
|
+
...warnings.length > 0 ? { warnings } : {}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function determineDirectBumps(config, options) {
|
|
57
|
+
const { force, bumpOverride } = options;
|
|
10
58
|
const workTypes = config.workTypes ?? { ...DEFAULT_WORK_TYPES };
|
|
11
59
|
const versionPatterns = config.versionPatterns ?? { ...DEFAULT_VERSION_PATTERNS };
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
60
|
+
const directBumps = /* @__PURE__ */ new Map();
|
|
61
|
+
const directResults = /* @__PURE__ */ new Map();
|
|
62
|
+
const skippedResults = [];
|
|
63
|
+
const currentVersions = /* @__PURE__ */ new Map();
|
|
15
64
|
for (const component of config.components) {
|
|
16
65
|
const name = component.dir;
|
|
17
66
|
const { tag, commits } = getCommitsSinceTarget(component.tagPrefix, component.paths);
|
|
18
67
|
const since = tag === void 0 ? "(no previous release found)" : `since ${tag}`;
|
|
68
|
+
const primaryPackageFile = component.packageFiles[0];
|
|
69
|
+
if (primaryPackageFile !== void 0) {
|
|
70
|
+
const currentVersion = readCurrentVersion(primaryPackageFile);
|
|
71
|
+
if (currentVersion !== void 0) {
|
|
72
|
+
currentVersions.set(component.dir, currentVersion);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
19
75
|
if (commits.length === 0 && !force) {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
previousTag: tag,
|
|
76
|
+
skippedResults.push({
|
|
77
|
+
component,
|
|
78
|
+
tag,
|
|
24
79
|
commitCount: 0,
|
|
25
|
-
|
|
26
|
-
|
|
80
|
+
parsedCommitCount: void 0,
|
|
81
|
+
unparseableCommits: void 0,
|
|
27
82
|
skipReason: `No changes for ${name} ${since}. Skipping.`
|
|
28
83
|
});
|
|
29
84
|
continue;
|
|
@@ -32,7 +87,7 @@ function releasePrepareMono(config, options) {
|
|
|
32
87
|
let parsedCommitCount;
|
|
33
88
|
let unparseableCommits;
|
|
34
89
|
if (bumpOverride === void 0) {
|
|
35
|
-
const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.
|
|
90
|
+
const determination = determineBumpFromCommits(commits, workTypes, versionPatterns, config.scopeAliases);
|
|
36
91
|
parsedCommitCount = determination.parsedCommitCount;
|
|
37
92
|
unparseableCommits = determination.unparseableCommits;
|
|
38
93
|
releaseType = determination.releaseType;
|
|
@@ -40,67 +95,201 @@ function releasePrepareMono(config, options) {
|
|
|
40
95
|
releaseType = bumpOverride;
|
|
41
96
|
}
|
|
42
97
|
if (releaseType === void 0) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
previousTag: tag,
|
|
98
|
+
skippedResults.push({
|
|
99
|
+
component,
|
|
100
|
+
tag,
|
|
47
101
|
commitCount: commits.length,
|
|
48
102
|
parsedCommitCount,
|
|
49
103
|
unparseableCommits,
|
|
50
|
-
bumpedFiles: [],
|
|
51
|
-
changelogFiles: [],
|
|
52
104
|
skipReason: `No release-worthy changes for ${name} ${since}. Skipping.`
|
|
53
105
|
});
|
|
54
106
|
continue;
|
|
55
107
|
}
|
|
56
|
-
|
|
108
|
+
directBumps.set(component.dir, { releaseType });
|
|
109
|
+
directResults.set(component.dir, {
|
|
110
|
+
component,
|
|
111
|
+
tag,
|
|
112
|
+
commits,
|
|
113
|
+
releaseType,
|
|
114
|
+
parsedCommitCount,
|
|
115
|
+
unparseableCommits
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return { directBumps, directResults, skippedResults, currentVersions };
|
|
119
|
+
}
|
|
120
|
+
function collectSkippedComponents(skippedResults, fullReleaseSet) {
|
|
121
|
+
const components = [];
|
|
122
|
+
for (const skipped of skippedResults) {
|
|
123
|
+
if (fullReleaseSet.has(skipped.component.dir)) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
components.push({
|
|
127
|
+
name: skipped.component.dir,
|
|
128
|
+
status: "skipped",
|
|
129
|
+
previousTag: skipped.tag,
|
|
130
|
+
commitCount: skipped.commitCount,
|
|
131
|
+
parsedCommitCount: skipped.parsedCommitCount,
|
|
132
|
+
unparseableCommits: skipped.unparseableCommits,
|
|
133
|
+
bumpedFiles: [],
|
|
134
|
+
changelogFiles: [],
|
|
135
|
+
skipReason: skipped.skipReason
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
return components;
|
|
139
|
+
}
|
|
140
|
+
function executeReleaseSet(sortedDirs, fullReleaseSet, config, directResults, previousTags, dryRun, components) {
|
|
141
|
+
const tags = [];
|
|
142
|
+
const modifiedFiles = [];
|
|
143
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
144
|
+
for (const dir of sortedDirs) {
|
|
145
|
+
const releaseEntry = fullReleaseSet.get(dir);
|
|
146
|
+
if (releaseEntry === void 0) {
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const component = findComponent(config.components, dir);
|
|
150
|
+
if (component === void 0) {
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
const bump = bumpAllVersions(component.packageFiles, releaseEntry.releaseType, dryRun);
|
|
57
154
|
const newTag = `${component.tagPrefix}${bump.newVersion}`;
|
|
58
155
|
tags.push(newTag);
|
|
59
156
|
modifiedFiles.push(...component.packageFiles, ...component.changelogPaths.map((p) => `${p}/CHANGELOG.md`));
|
|
60
157
|
const changelogFiles = [];
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
158
|
+
const directResult = directResults.get(dir);
|
|
159
|
+
const isPropagationOnly = directResult === void 0;
|
|
160
|
+
if (isPropagationOnly && releaseEntry.propagatedFrom !== void 0) {
|
|
161
|
+
for (const changelogPath of component.changelogPaths) {
|
|
162
|
+
changelogFiles.push(
|
|
163
|
+
writeSyntheticChangelog({
|
|
164
|
+
changelogPath,
|
|
165
|
+
newVersion: bump.newVersion,
|
|
166
|
+
date: today,
|
|
167
|
+
propagatedFrom: releaseEntry.propagatedFrom,
|
|
168
|
+
dryRun
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
} else {
|
|
173
|
+
for (const changelogPath of component.changelogPaths) {
|
|
174
|
+
changelogFiles.push(
|
|
175
|
+
...generateChangelog(config, changelogPath, newTag, dryRun, {
|
|
176
|
+
tagPattern: buildTagPattern(component.tagPrefix),
|
|
177
|
+
includePaths: component.paths
|
|
178
|
+
})
|
|
179
|
+
);
|
|
180
|
+
}
|
|
65
181
|
}
|
|
66
182
|
components.push({
|
|
67
|
-
name,
|
|
183
|
+
name: dir,
|
|
68
184
|
status: "released",
|
|
69
|
-
previousTag: tag,
|
|
70
|
-
commitCount: commits.length,
|
|
71
|
-
parsedCommitCount,
|
|
72
|
-
releaseType,
|
|
185
|
+
previousTag: directResult?.tag ?? previousTags.get(dir),
|
|
186
|
+
commitCount: directResult?.commits.length ?? 0,
|
|
187
|
+
parsedCommitCount: directResult?.parsedCommitCount,
|
|
188
|
+
releaseType: releaseEntry.releaseType,
|
|
73
189
|
currentVersion: bump.currentVersion,
|
|
74
190
|
newVersion: bump.newVersion,
|
|
75
191
|
tag: newTag,
|
|
76
192
|
bumpedFiles: bump.files,
|
|
77
193
|
changelogFiles,
|
|
78
|
-
|
|
194
|
+
commits: directResult?.commits,
|
|
195
|
+
unparseableCommits: directResult?.unparseableCommits,
|
|
196
|
+
propagatedFrom: releaseEntry.propagatedFrom
|
|
79
197
|
});
|
|
80
198
|
}
|
|
199
|
+
return { tags, modifiedFiles };
|
|
200
|
+
}
|
|
201
|
+
function runFormatCommand(config, tags, modifiedFiles, dryRun) {
|
|
81
202
|
const formatCommandStr = config.formatCommand ?? (hasPrettierConfig() ? "npx prettier --write" : void 0);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
203
|
+
if (tags.length === 0 || formatCommandStr === void 0) {
|
|
204
|
+
return void 0;
|
|
205
|
+
}
|
|
206
|
+
const fullCommand = `${formatCommandStr} ${modifiedFiles.join(" ")}`;
|
|
207
|
+
if (dryRun) {
|
|
208
|
+
return { command: fullCommand, executed: false, files: modifiedFiles };
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
execSync(fullCommand, { stdio: "inherit" });
|
|
212
|
+
} catch (error) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`Format command failed ('${fullCommand}'): ${error instanceof Error ? error.message : String(error)}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
return { command: fullCommand, executed: true, files: modifiedFiles };
|
|
218
|
+
}
|
|
219
|
+
function findComponent(components, dir) {
|
|
220
|
+
return components.find((c) => c.dir === dir);
|
|
221
|
+
}
|
|
222
|
+
function hasVersionField(value) {
|
|
223
|
+
return typeof value === "object" && value !== null && "version" in value && typeof value.version === "string";
|
|
224
|
+
}
|
|
225
|
+
function readCurrentVersion(filePath) {
|
|
226
|
+
try {
|
|
227
|
+
const content = readFileSync(filePath, "utf8");
|
|
228
|
+
const parsed = JSON.parse(content);
|
|
229
|
+
if (hasVersionField(parsed)) {
|
|
230
|
+
return parsed.version;
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
return void 0;
|
|
235
|
+
}
|
|
236
|
+
function topologicalSort(releaseSet, graph) {
|
|
237
|
+
const releaseDirs = new Set(releaseSet.keys());
|
|
238
|
+
if (releaseDirs.size === 0) {
|
|
239
|
+
return { sorted: [], cyclicDirs: [] };
|
|
240
|
+
}
|
|
241
|
+
const inDegree = /* @__PURE__ */ new Map();
|
|
242
|
+
const forwardEdges = /* @__PURE__ */ new Map();
|
|
243
|
+
for (const dir of releaseDirs) {
|
|
244
|
+
inDegree.set(dir, 0);
|
|
245
|
+
forwardEdges.set(dir, []);
|
|
246
|
+
}
|
|
247
|
+
for (const [packageName, dependents] of graph.dependentsOf) {
|
|
248
|
+
const depDir = graph.packageNameToDir.get(packageName);
|
|
249
|
+
if (depDir === void 0 || !releaseDirs.has(depDir)) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
for (const dependent of dependents) {
|
|
253
|
+
if (!releaseDirs.has(dependent.dir)) {
|
|
254
|
+
continue;
|
|
94
255
|
}
|
|
95
|
-
|
|
256
|
+
const edges = forwardEdges.get(depDir);
|
|
257
|
+
if (edges !== void 0) {
|
|
258
|
+
edges.push(dependent.dir);
|
|
259
|
+
}
|
|
260
|
+
inDegree.set(dependent.dir, (inDegree.get(dependent.dir) ?? 0) + 1);
|
|
96
261
|
}
|
|
97
262
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}
|
|
263
|
+
const queue = [];
|
|
264
|
+
for (const [dir, degree] of inDegree) {
|
|
265
|
+
if (degree === 0) {
|
|
266
|
+
queue.push(dir);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
const sorted = [];
|
|
270
|
+
while (queue.length > 0) {
|
|
271
|
+
const dir = queue.shift();
|
|
272
|
+
if (dir === void 0) {
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
sorted.push(dir);
|
|
276
|
+
for (const dependent of forwardEdges.get(dir) ?? []) {
|
|
277
|
+
const newDegree = (inDegree.get(dependent) ?? 1) - 1;
|
|
278
|
+
inDegree.set(dependent, newDegree);
|
|
279
|
+
if (newDegree === 0) {
|
|
280
|
+
queue.push(dependent);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const sortedSet = new Set(sorted);
|
|
285
|
+
const cyclicDirs = [];
|
|
286
|
+
for (const dir of releaseDirs) {
|
|
287
|
+
if (!sortedSet.has(dir)) {
|
|
288
|
+
sorted.push(dir);
|
|
289
|
+
cyclicDirs.push(dir);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return { sorted, cyclicDirs };
|
|
104
293
|
}
|
|
105
294
|
export {
|
|
106
295
|
releasePrepareMono
|