versionary 1.0.0 → 1.0.1
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/dist/cli/index.js +16 -2
- package/dist/release/changelog.js +9 -6
- package/dist/release/plan.d.ts +22 -0
- package/dist/release/plan.js +32 -0
- package/dist/release/pr.js +8 -2
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -5,6 +5,20 @@ import { createReleasePlan } from "../release/plan.js";
|
|
|
5
5
|
import { closeStaleReviewRequestIfExists, isReleaseCommitMessage, openOrUpdateReviewRequest, prepareReleasePr, pushReleaseBranch, resolveReleaseHighlights, } from "../release/pr.js";
|
|
6
6
|
import { runRelease, runReleaseDetailed } from "../release/release.js";
|
|
7
7
|
import { verifyProject } from "../release/verify-project.js";
|
|
8
|
+
/**
|
|
9
|
+
* What a dry run would actually release. `plan.nextVersion` is the repository
|
|
10
|
+
* aggregate, which in a monorepo names no real release, so prefer the
|
|
11
|
+
* per-package versions whenever the plan has them.
|
|
12
|
+
*/
|
|
13
|
+
function formatDryRunReleaseSubject(plan) {
|
|
14
|
+
const releasing = (plan.packages ?? []).filter((pkg) => pkg.nextVersion);
|
|
15
|
+
if (releasing.length === 0) {
|
|
16
|
+
return plan.nextVersion ?? "";
|
|
17
|
+
}
|
|
18
|
+
return releasing
|
|
19
|
+
.map((pkg) => `${pkg.path === "." ? plan.packageName : pkg.path} ${pkg.nextVersion}`)
|
|
20
|
+
.join(", ");
|
|
21
|
+
}
|
|
8
22
|
function printVerifyResult() {
|
|
9
23
|
const result = verifyProject();
|
|
10
24
|
const categories = [
|
|
@@ -114,7 +128,7 @@ async function main() {
|
|
|
114
128
|
return 0;
|
|
115
129
|
}
|
|
116
130
|
if (flags["dry-run"]) {
|
|
117
|
-
const dryRunMessage = `Dry run: would prepare release PR branch ${plan.releaseBranchPrefix} for ${plan
|
|
131
|
+
const dryRunMessage = `Dry run: would prepare release PR branch ${plan.releaseBranchPrefix} for ${formatDryRunReleaseSubject(plan)}`;
|
|
118
132
|
if (flags.json) {
|
|
119
133
|
emitJson({
|
|
120
134
|
action: "pr-dry-run",
|
|
@@ -218,7 +232,7 @@ async function main() {
|
|
|
218
232
|
return 0;
|
|
219
233
|
}
|
|
220
234
|
if (flags["dry-run"]) {
|
|
221
|
-
console.log(`Dry run: would prepare release PR branch ${plan.releaseBranchPrefix} for ${plan
|
|
235
|
+
console.log(`Dry run: would prepare release PR branch ${plan.releaseBranchPrefix} for ${formatDryRunReleaseSubject(plan)}`);
|
|
222
236
|
return 0;
|
|
223
237
|
}
|
|
224
238
|
const pr = prepareReleasePr(process.cwd(), { logger: console });
|
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { applyRevertSuppression, inferReleaseTypeFromParsedCommit, parseConventionalCommitMessage, } from "../git/commits.js";
|
|
4
4
|
import { resolveRepositoryWebBaseUrl } from "../git/repo-url.js";
|
|
5
|
-
import { resolvePackageDependencies } from "./plan.js";
|
|
5
|
+
import { resolvePackageDependencies, resolveRootReleaseView, } from "./plan.js";
|
|
6
6
|
import { isVersionHeading } from "./semver.js";
|
|
7
7
|
const REVIEW_REQUEST_FOOTER = "---\n\nThis PR was generated by [Versionary](https://github.com/jolars/versionary).";
|
|
8
8
|
function formatDate() {
|
|
@@ -192,24 +192,27 @@ export function renderReviewRequestFooter() {
|
|
|
192
192
|
return REVIEW_REQUEST_FOOTER;
|
|
193
193
|
}
|
|
194
194
|
export function renderReleasePlanChangelog(plan, options = {}) {
|
|
195
|
-
|
|
195
|
+
// Root's changelog describes root's own release, so it must be driven by the
|
|
196
|
+
// root package's version and commit set rather than the plan-level aggregate.
|
|
197
|
+
const root = resolveRootReleaseView(plan);
|
|
198
|
+
if (!root.nextVersion) {
|
|
196
199
|
return "";
|
|
197
200
|
}
|
|
198
201
|
const dedupedCommits = [
|
|
199
|
-
...new Map(
|
|
202
|
+
...new Map(root.commits.map((commit) => [commit.hash, commit])).values(),
|
|
200
203
|
];
|
|
201
204
|
if (plan.changelogFormat === "r-news") {
|
|
202
205
|
return renderRNewsReleaseNotes({
|
|
203
206
|
packageName: plan.packageName,
|
|
204
|
-
nextVersion:
|
|
207
|
+
nextVersion: root.nextVersion,
|
|
205
208
|
commits: dedupedCommits,
|
|
206
209
|
cwd: process.cwd(),
|
|
207
210
|
highlights: options.highlights,
|
|
208
211
|
});
|
|
209
212
|
}
|
|
210
213
|
return renderReleaseNotesSection({
|
|
211
|
-
currentVersion:
|
|
212
|
-
nextVersion:
|
|
214
|
+
currentVersion: root.currentVersion,
|
|
215
|
+
nextVersion: root.nextVersion,
|
|
213
216
|
commits: dedupedCommits,
|
|
214
217
|
cwd: options.cwd ?? process.cwd(),
|
|
215
218
|
dependencies: resolvePackageDependencies(plan, "."),
|
package/dist/release/plan.d.ts
CHANGED
|
@@ -34,6 +34,28 @@ export declare function getChangelogDefaults(config: {
|
|
|
34
34
|
changelogFormat: VersionaryChangelogFormat;
|
|
35
35
|
};
|
|
36
36
|
export declare function createReleasePlan(cwd?: string): ReleasePlan;
|
|
37
|
+
/**
|
|
38
|
+
* The root package's own release, as distinct from the plan-level aggregate.
|
|
39
|
+
*
|
|
40
|
+
* `plan.releaseType`/`plan.nextVersion`/`plan.commits` describe the repository
|
|
41
|
+
* as a whole: the aggregate release type folds in every package's commits and
|
|
42
|
+
* is then applied to root's version number. That answers "is anything
|
|
43
|
+
* releasing, and how large is the biggest change anywhere" — not "what is root
|
|
44
|
+
* releasing". A sibling's `feat` therefore lifts the aggregate to a minor even
|
|
45
|
+
* when root's own `exclude-paths` drop that commit, so anything that names or
|
|
46
|
+
* describes root's release must go through here instead. Using the aggregate
|
|
47
|
+
* would report a version no version file carries and list commits root
|
|
48
|
+
* deliberately excluded.
|
|
49
|
+
*
|
|
50
|
+
* Falls back to the aggregate when the plan has no explicit root package: the
|
|
51
|
+
* top-level changelog is then a repository-wide summary with no package of its
|
|
52
|
+
* own to describe.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveRootReleaseView(plan: ReleasePlan): {
|
|
55
|
+
currentVersion: string;
|
|
56
|
+
nextVersion: string | null;
|
|
57
|
+
commits: ParsedCommit[];
|
|
58
|
+
};
|
|
37
59
|
export declare function resolvePackageDependencies(plan: ReleasePlan, packagePath: string): Array<{
|
|
38
60
|
name: string;
|
|
39
61
|
version: string;
|
package/dist/release/plan.js
CHANGED
|
@@ -480,6 +480,38 @@ export function createReleasePlan(cwd = process.cwd()) {
|
|
|
480
480
|
packages: visiblePackages.map(({ implicitRoot: _implicitRoot, ...pkgPlan }) => pkgPlan),
|
|
481
481
|
};
|
|
482
482
|
}
|
|
483
|
+
/**
|
|
484
|
+
* The root package's own release, as distinct from the plan-level aggregate.
|
|
485
|
+
*
|
|
486
|
+
* `plan.releaseType`/`plan.nextVersion`/`plan.commits` describe the repository
|
|
487
|
+
* as a whole: the aggregate release type folds in every package's commits and
|
|
488
|
+
* is then applied to root's version number. That answers "is anything
|
|
489
|
+
* releasing, and how large is the biggest change anywhere" — not "what is root
|
|
490
|
+
* releasing". A sibling's `feat` therefore lifts the aggregate to a minor even
|
|
491
|
+
* when root's own `exclude-paths` drop that commit, so anything that names or
|
|
492
|
+
* describes root's release must go through here instead. Using the aggregate
|
|
493
|
+
* would report a version no version file carries and list commits root
|
|
494
|
+
* deliberately excluded.
|
|
495
|
+
*
|
|
496
|
+
* Falls back to the aggregate when the plan has no explicit root package: the
|
|
497
|
+
* top-level changelog is then a repository-wide summary with no package of its
|
|
498
|
+
* own to describe.
|
|
499
|
+
*/
|
|
500
|
+
export function resolveRootReleaseView(plan) {
|
|
501
|
+
const rootPackage = plan.packages?.find((pkg) => pkg.path === ".");
|
|
502
|
+
if (!rootPackage) {
|
|
503
|
+
return {
|
|
504
|
+
currentVersion: plan.currentVersion,
|
|
505
|
+
nextVersion: plan.nextVersion,
|
|
506
|
+
commits: plan.commits,
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
return {
|
|
510
|
+
currentVersion: rootPackage.currentVersion,
|
|
511
|
+
nextVersion: rootPackage.nextVersion,
|
|
512
|
+
commits: rootPackage.commits,
|
|
513
|
+
};
|
|
514
|
+
}
|
|
483
515
|
export function resolvePackageDependencies(plan, packagePath) {
|
|
484
516
|
const target = plan.packages?.find((pkg) => pkg.path === packagePath);
|
|
485
517
|
if (!target) {
|
package/dist/release/pr.js
CHANGED
|
@@ -258,8 +258,14 @@ export function prepareReleasePr(cwd = process.cwd(), options = {}) {
|
|
|
258
258
|
const highlightsResult = resolveReleaseHighlights(cwd, plan.changelogFile, plan.changelogFormat);
|
|
259
259
|
const highlights = highlightsResult.highlights;
|
|
260
260
|
const section = renderReleasePlanChangelog(plan, { highlights, cwd });
|
|
261
|
-
|
|
262
|
-
|
|
261
|
+
const updatedChangelogFiles = [];
|
|
262
|
+
// An empty section means root itself is not releasing, only siblings are.
|
|
263
|
+
// Writing then would leave a bare heading behind and, worse, strip the
|
|
264
|
+
// manual-notes block that nothing has folded in yet.
|
|
265
|
+
if (section.length > 0) {
|
|
266
|
+
prependChangelog(cwd, plan.changelogFile, section, plan.changelogFormat);
|
|
267
|
+
updatedChangelogFiles.push(plan.changelogFile);
|
|
268
|
+
}
|
|
263
269
|
for (const packagePlan of plan.packages ?? []) {
|
|
264
270
|
if (!packagePlan.nextVersion || packagePlan.path === ".") {
|
|
265
271
|
continue;
|