versionary 0.21.0 → 0.23.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/README.md CHANGED
@@ -157,6 +157,32 @@ For a quick trial, use:
157
157
  - per-package `package-name` can override release identity (labels + tag base)
158
158
  - per-package `changelog-file` writes package release notes to
159
159
  `<package-path>/<changelog-file>`
160
+ - per-package `follows` declares an asymmetric version link to one or more
161
+ source packages: when any source bumps, the follower releases too, with
162
+ bump = `max(own bump, max(source bumps))`. The follower's changelog gets a
163
+ `### Dependencies` section listing the followed sources. Use it when one
164
+ package bundles another's artifact (e.g. an editor extension that ships
165
+ the CLI binary). Cycles, self-references, unknown source paths, and
166
+ combining `follows` with `monorepo-mode: "fixed"` are config errors.
167
+ `follows` is non-transitive: A follows B does not imply A follows what B
168
+ follows.
169
+
170
+ ```jsonc
171
+ // Editor extension that bundles the root CLI artifact
172
+ {
173
+ "version": 1,
174
+ "release-type": "rust",
175
+ "monorepo-mode": "independent",
176
+ "packages": {
177
+ ".": { "exclude-paths": ["editors"] },
178
+ "editors/code": {
179
+ "release-type": "node",
180
+ "package-name": "panache-code",
181
+ "follows": ["."]
182
+ }
183
+ }
184
+ }
185
+ ```
160
186
 
161
187
  Rust strategy examples:
162
188
 
package/dist/cli/index.js CHANGED
@@ -2,6 +2,7 @@
2
2
  "use strict";
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const node_child_process_1 = require("node:child_process");
5
+ const load_config_js_1 = require("../config/load-config.js");
5
6
  const changelog_js_1 = require("../release/changelog.js");
6
7
  const plan_js_1 = require("../release/plan.js");
7
8
  const pr_js_1 = require("../release/pr.js");
@@ -162,7 +163,7 @@ async function main() {
162
163
  return 0;
163
164
  }
164
165
  (0, pr_js_1.pushReleaseBranch)(process.cwd(), pr.branch);
165
- const reviewResult = await (0, pr_js_1.openOrUpdateReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan, { logger });
166
+ const reviewResult = await (0, pr_js_1.openOrUpdateReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan, { logger, highlights: pr.highlights });
166
167
  const message = `Prepared release PR branch ${pr.branch}`;
167
168
  if (flags.json) {
168
169
  emitJson({
@@ -196,12 +197,18 @@ async function main() {
196
197
  console.log("No releasable commits found.");
197
198
  return 0;
198
199
  }
199
- const section = (0, changelog_js_1.renderReleasePlanChangelog)(plan);
200
+ const loaded = (0, load_config_js_1.loadConfig)();
201
+ const highlightsRead = (0, pr_js_1.readNextReleaseHighlights)(process.cwd(), loaded.config);
202
+ const highlights = highlightsRead?.content ?? "";
203
+ const section = (0, changelog_js_1.renderReleasePlanChangelog)(plan, { highlights });
200
204
  if (!write) {
201
205
  console.log(section);
202
206
  return 0;
203
207
  }
204
208
  (0, changelog_js_1.prependChangelog)(process.cwd(), plan.changelogFile, section, plan.changelogFormat);
209
+ if (highlightsRead) {
210
+ (0, pr_js_1.consumeNextReleaseFile)(process.cwd(), highlightsRead.filePath);
211
+ }
205
212
  console.log(`Updated ${plan.changelogFile}`);
206
213
  return 0;
207
214
  }
@@ -227,7 +234,7 @@ async function main() {
227
234
  return 0;
228
235
  }
229
236
  (0, pr_js_1.pushReleaseBranch)(process.cwd(), pr.branch);
230
- const reviewResult = await (0, pr_js_1.openOrUpdateReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan);
237
+ const reviewResult = await (0, pr_js_1.openOrUpdateReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan, { highlights: pr.highlights });
231
238
  console.log(`Prepared release PR branch ${pr.branch}`);
232
239
  console.log(`Title: ${pr.title}`);
233
240
  console.log(reviewResult);
@@ -21,6 +21,7 @@ export declare const configSchema: z.ZodObject<{
21
21
  }>>;
22
22
  "release-branch": z.ZodOptional<z.ZodString>;
23
23
  "baseline-file": z.ZodOptional<z.ZodString>;
24
+ "next-release-file": z.ZodOptional<z.ZodString>;
24
25
  "bootstrap-sha": z.ZodOptional<z.ZodString>;
25
26
  "monorepo-mode": z.ZodOptional<z.ZodEnum<{
26
27
  independent: "independent";
@@ -52,6 +53,7 @@ export declare const configSchema: z.ZodObject<{
52
53
  jsonpath: z.ZodOptional<z.ZodString>;
53
54
  pattern: z.ZodOptional<z.ZodString>;
54
55
  }, z.core.$strip>>>;
56
+ follows: z.ZodOptional<z.ZodArray<z.ZodString>>;
55
57
  }, z.core.$strict>>>;
56
58
  }, z.core.$strict>;
57
59
  export type ConfigSchema = z.infer<typeof configSchema>;
@@ -60,6 +60,7 @@ const packageSchema = zod_1.z
60
60
  "changelog-format": zod_1.z.enum(["markdown-changelog", "r-news"]).optional(),
61
61
  "exclude-paths": zod_1.z.array(zod_1.z.string()).optional(),
62
62
  "extra-files": zod_1.z.array(artifactRuleSchema).optional(),
63
+ follows: zod_1.z.array(zod_1.z.string().min(1)).optional(),
63
64
  })
64
65
  .strict();
65
66
  exports.configSchema = zod_1.z
@@ -76,6 +77,7 @@ exports.configSchema = zod_1.z
76
77
  .optional(),
77
78
  "release-branch": zod_1.z.string().optional(),
78
79
  "baseline-file": zod_1.z.string().optional(),
80
+ "next-release-file": zod_1.z.string().optional(),
79
81
  "bootstrap-sha": zod_1.z.string().optional(),
80
82
  "monorepo-mode": zod_1.z.enum(["independent", "fixed"]).optional(),
81
83
  "bump-minor-pre-major": zod_1.z.boolean().optional(),
@@ -84,4 +86,88 @@ exports.configSchema = zod_1.z
84
86
  "release-type": zod_1.z.string().optional(),
85
87
  packages: zod_1.z.record(zod_1.z.string().min(1), packageSchema).optional(),
86
88
  })
87
- .strict();
89
+ .strict()
90
+ .superRefine((value, ctx) => {
91
+ const packages = value.packages;
92
+ if (!packages) {
93
+ return;
94
+ }
95
+ const knownPaths = new Set(Object.keys(packages));
96
+ const followsByPath = new Map();
97
+ for (const [packagePath, packageConfig] of Object.entries(packages)) {
98
+ const follows = packageConfig.follows;
99
+ if (!follows || follows.length === 0) {
100
+ continue;
101
+ }
102
+ followsByPath.set(packagePath, follows);
103
+ for (const sourcePath of follows) {
104
+ if (sourcePath === packagePath) {
105
+ ctx.addIssue({
106
+ code: zod_1.z.ZodIssueCode.custom,
107
+ message: `Package "${packagePath}" cannot follow itself.`,
108
+ path: ["packages", packagePath, "follows"],
109
+ });
110
+ continue;
111
+ }
112
+ if (!knownPaths.has(sourcePath)) {
113
+ ctx.addIssue({
114
+ code: zod_1.z.ZodIssueCode.custom,
115
+ message: `Package "${packagePath}" follows unknown package "${sourcePath}".`,
116
+ path: ["packages", packagePath, "follows"],
117
+ });
118
+ }
119
+ }
120
+ }
121
+ if (followsByPath.size > 0 && value["monorepo-mode"] === "fixed") {
122
+ for (const followerPath of followsByPath.keys()) {
123
+ ctx.addIssue({
124
+ code: zod_1.z.ZodIssueCode.custom,
125
+ message: 'Package "follows" cannot be combined with monorepo-mode "fixed" (fixed mode already pins all package versions together).',
126
+ path: ["packages", followerPath, "follows"],
127
+ });
128
+ }
129
+ }
130
+ const reportedCycles = new Set();
131
+ const findCycleFrom = (start) => {
132
+ const trail = [];
133
+ const visit = (node) => {
134
+ const trailIndex = trail.indexOf(node);
135
+ if (trailIndex !== -1) {
136
+ return [...trail.slice(trailIndex), node];
137
+ }
138
+ const sources = followsByPath.get(node);
139
+ if (!sources || sources.length === 0) {
140
+ return null;
141
+ }
142
+ trail.push(node);
143
+ for (const source of sources) {
144
+ if (!knownPaths.has(source)) {
145
+ continue;
146
+ }
147
+ const cycle = visit(source);
148
+ if (cycle) {
149
+ return cycle;
150
+ }
151
+ }
152
+ trail.pop();
153
+ return null;
154
+ };
155
+ return visit(start);
156
+ };
157
+ for (const followerPath of followsByPath.keys()) {
158
+ const cycle = findCycleFrom(followerPath);
159
+ if (!cycle) {
160
+ continue;
161
+ }
162
+ const key = [...cycle].sort().join("->");
163
+ if (reportedCycles.has(key)) {
164
+ continue;
165
+ }
166
+ reportedCycles.add(key);
167
+ ctx.addIssue({
168
+ code: zod_1.z.ZodIssueCode.custom,
169
+ message: `Package "follows" cycle detected: ${cycle.join(" -> ")}.`,
170
+ path: ["packages", cycle[0] ?? followerPath, "follows"],
171
+ });
172
+ }
173
+ });
@@ -26,7 +26,7 @@ function parseFieldPath(fieldPath) {
26
26
  index += 2;
27
27
  continue;
28
28
  }
29
- const keyMatch = fieldPath.slice(index + 1).match(/^[A-Za-z0-9_-]+/u);
29
+ const keyMatch = fieldPath.slice(index + 1).match(/^[A-Za-z0-9_@/-]+/u);
30
30
  if (!keyMatch) {
31
31
  throw new Error(`Invalid field-path "${fieldPath}" near index ${index}.`);
32
32
  }
@@ -10,6 +10,7 @@ export declare function renderSimpleReleaseNotes(input: {
10
10
  name: string;
11
11
  version: string;
12
12
  }>;
13
+ highlights?: string;
13
14
  }, options?: {
14
15
  includeFooter?: boolean;
15
16
  headerLabel?: string;
@@ -19,6 +20,7 @@ export declare function renderReleasePlanChangelog(plan: ReleasePlan, options?:
19
20
  headerLabel?: string;
20
21
  includeFooter?: boolean;
21
22
  cwd?: string;
23
+ highlights?: string;
22
24
  }): string;
23
25
  /** @deprecated Use renderReleasePlanChangelog. */
24
26
  export declare function renderSimpleChangelog(plan: SimplePlan): string;
@@ -35,4 +37,5 @@ export declare function renderRNewsReleaseNotes(input: {
35
37
  nextVersion: string;
36
38
  commits: ParsedCommit[];
37
39
  cwd?: string;
40
+ highlights?: string;
38
41
  }): string;
@@ -148,6 +148,10 @@ function renderSimpleReleaseNotes(input, options = {}) {
148
148
  : `## ${headerLabel} (${formatDate()})`;
149
149
  const grouped = groupCommitLines(input.commits, repoUrl);
150
150
  const sections = [];
151
+ const trimmedHighlights = input.highlights?.trim() ?? "";
152
+ if (trimmedHighlights.length > 0) {
153
+ sections.push(trimmedHighlights, "");
154
+ }
151
155
  if (grouped.breaking.length > 0) {
152
156
  sections.push("### Breaking changes", ...grouped.breaking, "");
153
157
  }
@@ -212,6 +216,7 @@ function renderReleasePlanChangelog(plan, options = {}) {
212
216
  nextVersion: plan.nextVersion,
213
217
  commits: dedupedCommits,
214
218
  cwd: process.cwd(),
219
+ highlights: options.highlights,
215
220
  });
216
221
  }
217
222
  return renderSimpleReleaseNotes({
@@ -220,6 +225,7 @@ function renderReleasePlanChangelog(plan, options = {}) {
220
225
  commits: dedupedCommits,
221
226
  cwd: options.cwd ?? process.cwd(),
222
227
  dependencies,
228
+ highlights: options.highlights,
223
229
  }, {
224
230
  includeFooter: options.includeFooter,
225
231
  headerLabel: options.headerLabel,
@@ -276,6 +282,10 @@ function renderRNewsReleaseNotes(input) {
276
282
  const grouped = groupCommitLines(input.commits, repoUrl);
277
283
  const normalizedVersion = input.nextVersion.replace(/\.\d+$/u, "");
278
284
  const sections = [];
285
+ const trimmedHighlights = input.highlights?.trim() ?? "";
286
+ if (trimmedHighlights.length > 0) {
287
+ sections.push(trimmedHighlights, "");
288
+ }
279
289
  if (grouped.breaking.length > 0) {
280
290
  sections.push("## Breaking changes", "", ...grouped.breaking, "");
281
291
  }
@@ -18,7 +18,7 @@ export interface ReleasePlan {
18
18
  releaseType: ReleaseType;
19
19
  currentVersion: string;
20
20
  nextVersion: string | null;
21
- bumpReason?: "direct" | "dependency-propagation";
21
+ bumpReason?: "direct" | "dependency-propagation" | "follows";
22
22
  dependencySourcePaths?: string[];
23
23
  commits: ParsedCommit[];
24
24
  }>;
@@ -98,6 +98,8 @@ function createReleasePlan(cwd = process.cwd()) {
98
98
  releaseType: null,
99
99
  currentVersion: explicitPackagePlans[0]?.currentVersion ?? "0.0.0",
100
100
  nextVersion: null,
101
+ bumpReason: undefined,
102
+ dependencySourcePaths: undefined,
101
103
  commits: [],
102
104
  parsedCommits: [],
103
105
  resolvedVersionFile: versionFile,
@@ -171,7 +173,7 @@ function createReleasePlan(cwd = process.cwd()) {
171
173
  }
172
174
  }
173
175
  }
174
- const adjustedPackages = packagePlans.map((pkgPlan) => {
176
+ const propagatedPackages = packagePlans.map((pkgPlan) => {
175
177
  const dependencySourcePaths = [
176
178
  ...(dependencySourcePathsByPackage.get(pkgPlan.path) ??
177
179
  new Set()),
@@ -196,6 +198,47 @@ function createReleasePlan(cwd = process.cwd()) {
196
198
  dependencySourcePaths,
197
199
  };
198
200
  });
201
+ const followsByPath = new Map();
202
+ for (const [packagePath, packageConfig] of Object.entries(loaded.config.packages ?? {})) {
203
+ const follows = packageConfig.follows ?? [];
204
+ if (follows.length > 0) {
205
+ followsByPath.set(packagePath, follows);
206
+ }
207
+ }
208
+ const adjustedPackages = propagatedPackages.map((pkgPlan) => {
209
+ const followsSources = followsByPath.get(pkgPlan.path) ?? [];
210
+ const bumpingSources = followsSources
211
+ .map((sourcePath) => propagatedPackages.find((pkg) => pkg.path === sourcePath))
212
+ .filter((sourcePlan) => Boolean(sourcePlan?.nextVersion));
213
+ if (bumpingSources.length === 0) {
214
+ return pkgPlan;
215
+ }
216
+ const ownReleaseType = pkgPlan.releaseType;
217
+ const combinedReleaseType = (0, semver_js_1.maxReleaseType)([
218
+ ownReleaseType,
219
+ ...bumpingSources.map((sourcePlan) => sourcePlan.releaseType),
220
+ ]);
221
+ const mergedDependencySourcePaths = [
222
+ ...new Set([
223
+ ...(pkgPlan.dependencySourcePaths ?? []),
224
+ ...bumpingSources.map((sourcePlan) => sourcePlan.path),
225
+ ]),
226
+ ].sort((a, b) => a.localeCompare(b));
227
+ const sourceDrove = combinedReleaseType !== ownReleaseType ||
228
+ pkgPlan.bumpReason === "dependency-propagation" ||
229
+ pkgPlan.bumpReason === undefined;
230
+ const baseVersion = packageCurrentVersionByPath[pkgPlan.path] ?? pkgPlan.currentVersion;
231
+ const nextVersion = combinedReleaseType
232
+ ? (0, semver_js_1.bumpVersion)(baseVersion, combinedReleaseType, { allowStableMajor })
233
+ : null;
234
+ return {
235
+ ...pkgPlan,
236
+ releaseType: combinedReleaseType,
237
+ nextVersion,
238
+ bumpReason: sourceDrove ? "follows" : pkgPlan.bumpReason,
239
+ dependencySourcePaths: mergedDependencySourcePaths,
240
+ };
241
+ });
199
242
  const visiblePackages = adjustedPackages.filter((pkgPlan) => !pkgPlan.implicitRoot || hasExplicitRootPackage);
200
243
  const rootPackagePlan = adjustedPackages.find((pkgPlan) => pkgPlan.path === ".");
201
244
  if (!rootPackagePlan) {
@@ -1,6 +1,15 @@
1
1
  import type { ParsedCommit } from "../git/commits.js";
2
+ import type { VersionaryConfig } from "../types/config.js";
2
3
  import type { VersionaryPluginContext } from "../types/plugins.js";
3
4
  import { type ReleasePlan, type SimplePlan } from "./plan.js";
5
+ export declare function getNextReleaseFile(config: VersionaryConfig): string;
6
+ export declare function readNextReleaseHighlights(cwd: string, config: VersionaryConfig): {
7
+ content: string;
8
+ filePath: string;
9
+ } | null;
10
+ export declare function consumeNextReleaseFile(cwd: string, filePath: string): {
11
+ tracked: boolean;
12
+ };
4
13
  export declare function splitSafeDirtyFiles(files: string[]): {
5
14
  ignored: string[];
6
15
  blocking: string[];
@@ -15,10 +24,12 @@ export declare function prepareReleasePr(cwd?: string, options?: {
15
24
  commits: ParsedCommit[];
16
25
  plan: ReleasePlan;
17
26
  updated: boolean;
27
+ highlights: string;
18
28
  };
19
- export declare function renderSimpleReviewRequestBody(version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, cwd?: string): string;
29
+ export declare function renderSimpleReviewRequestBody(version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, cwd?: string, highlights?: string): string;
20
30
  export declare function openOrUpdateReviewRequest(cwd: string, branch: string, title: string, version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, options?: {
21
31
  logger?: VersionaryPluginContext["logger"];
32
+ highlights?: string;
22
33
  }): Promise<string>;
23
34
  export declare function closeStaleReviewRequestIfExists(cwd?: string, options?: {
24
35
  logger?: VersionaryPluginContext["logger"];
@@ -34,6 +45,7 @@ export declare function prepareSimpleReleasePr(cwd?: string, options?: {
34
45
  /** @deprecated Use openOrUpdateReviewRequest. */
35
46
  export declare function openOrUpdateSimpleReviewRequest(cwd: string, branch: string, title: string, version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, options?: {
36
47
  logger?: VersionaryPluginContext["logger"];
48
+ highlights?: string;
37
49
  }): Promise<string>;
38
50
  export declare function pushReleaseBranch(cwd: string, branch: string): void;
39
51
  export declare function isReleaseCommitMessage(commitMessage: string): boolean;
@@ -3,6 +3,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getNextReleaseFile = getNextReleaseFile;
7
+ exports.readNextReleaseHighlights = readNextReleaseHighlights;
8
+ exports.consumeNextReleaseFile = consumeNextReleaseFile;
6
9
  exports.splitSafeDirtyFiles = splitSafeDirtyFiles;
7
10
  exports.prepareReleasePr = prepareReleasePr;
8
11
  exports.renderSimpleReviewRequestBody = renderSimpleReviewRequestBody;
@@ -13,6 +16,7 @@ exports.openOrUpdateSimpleReviewRequest = openOrUpdateSimpleReviewRequest;
13
16
  exports.pushReleaseBranch = pushReleaseBranch;
14
17
  exports.isReleaseCommitMessage = isReleaseCommitMessage;
15
18
  const node_child_process_1 = require("node:child_process");
19
+ const node_fs_1 = __importDefault(require("node:fs"));
16
20
  const node_path_1 = __importDefault(require("node:path"));
17
21
  const load_config_js_1 = require("../config/load-config.js");
18
22
  const client_js_1 = require("../scm/client.js");
@@ -29,6 +33,38 @@ const SAFE_DIRTY_FILES = new Set([
29
33
  "npm-shrinkwrap.json",
30
34
  ]);
31
35
  const VERSIONARY_RELEASE_TRAILER = "Versionary-Release: true";
36
+ function getNextReleaseFile(config) {
37
+ return config["next-release-file"] ?? "NEXT_RELEASE.md";
38
+ }
39
+ function readNextReleaseHighlights(cwd, config) {
40
+ const filePath = getNextReleaseFile(config);
41
+ const fullPath = node_path_1.default.join(cwd, filePath);
42
+ if (!node_fs_1.default.existsSync(fullPath)) {
43
+ return null;
44
+ }
45
+ const content = node_fs_1.default.readFileSync(fullPath, "utf8").trim();
46
+ return { content, filePath };
47
+ }
48
+ function isTrackedFile(cwd, filePath) {
49
+ try {
50
+ (0, node_child_process_1.execFileSync)("git", ["ls-files", "--error-unmatch", "--", filePath], {
51
+ cwd,
52
+ stdio: ["ignore", "pipe", "ignore"],
53
+ });
54
+ return true;
55
+ }
56
+ catch {
57
+ return false;
58
+ }
59
+ }
60
+ function consumeNextReleaseFile(cwd, filePath) {
61
+ const tracked = isTrackedFile(cwd, filePath);
62
+ const fullPath = node_path_1.default.join(cwd, filePath);
63
+ if (node_fs_1.default.existsSync(fullPath)) {
64
+ node_fs_1.default.rmSync(fullPath);
65
+ }
66
+ return { tracked };
67
+ }
32
68
  function listTrackedDirtyFiles(cwd) {
33
69
  const status = (0, node_child_process_1.execFileSync)("git", ["status", "--porcelain", "--untracked-files=no"], {
34
70
  cwd,
@@ -251,9 +287,18 @@ function prepareReleasePr(cwd = process.cwd(), options = {}) {
251
287
  }
252
288
  const updatedArtifactFiles = (0, artifact_rules_js_1.applyConfiguredArtifactRules)(cwd, loaded.config, plan);
253
289
  const packageReleaseMetadata = buildPackageReleaseMetadata(cwd, plan, loaded.config);
254
- const section = (0, changelog_js_1.renderReleasePlanChangelog)(plan);
290
+ const nextReleaseRead = readNextReleaseHighlights(cwd, loaded.config);
291
+ const highlights = nextReleaseRead?.content ?? "";
292
+ const section = (0, changelog_js_1.renderReleasePlanChangelog)(plan, { highlights, cwd });
255
293
  (0, changelog_js_1.prependChangelog)(cwd, plan.changelogFile, section, plan.changelogFormat);
256
294
  const updatedChangelogFiles = [plan.changelogFile];
295
+ let consumedHighlightsPath = null;
296
+ if (nextReleaseRead) {
297
+ const { tracked } = consumeNextReleaseFile(cwd, nextReleaseRead.filePath);
298
+ if (tracked) {
299
+ consumedHighlightsPath = nextReleaseRead.filePath;
300
+ }
301
+ }
257
302
  for (const packagePlan of plan.packages ?? []) {
258
303
  if (!packagePlan.nextVersion || packagePlan.path === ".") {
259
304
  continue;
@@ -296,6 +341,7 @@ function prepareReleasePr(cwd = process.cwd(), options = {}) {
296
341
  ...updatedVersionFiles,
297
342
  ...updatedArtifactFiles,
298
343
  ...updatedChangelogFiles,
344
+ ...(consumedHighlightsPath ? [consumedHighlightsPath] : []),
299
345
  ]),
300
346
  ];
301
347
  (0, node_child_process_1.execFileSync)("git", ["add", ...filesToAdd], {
@@ -325,9 +371,10 @@ function prepareReleasePr(cwd = process.cwd(), options = {}) {
325
371
  commits: plan.commits,
326
372
  plan,
327
373
  updated,
374
+ highlights,
328
375
  };
329
376
  }
330
- function renderSimpleReviewRequestBody(version, previousVersion, commits, plan = null, cwd = process.cwd()) {
377
+ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan = null, cwd = process.cwd(), highlights = "") {
331
378
  const rootPackageLabel = node_path_1.default.basename(cwd);
332
379
  const formatPackageLabel = (packagePath) => packagePath === "." ? rootPackageLabel : packagePath;
333
380
  const isDirectBump = (pkg) => pkg.bumpReason === "direct" ||
@@ -361,6 +408,7 @@ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan =
361
408
  const rootNotes = (0, changelog_js_1.renderReleasePlanChangelog)(plan, {
362
409
  headerLabel: `${formatPackageLabel(".")}: ${rootPackage.nextVersion}`,
363
410
  cwd,
411
+ highlights,
364
412
  });
365
413
  sections.push(rootNotes);
366
414
  }
@@ -393,6 +441,7 @@ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan =
393
441
  nextVersion: version,
394
442
  commits,
395
443
  cwd,
444
+ highlights,
396
445
  }, { includeFooter: true });
397
446
  }
398
447
  async function openOrUpdateReviewRequest(cwd, branch, title, version, previousVersion, commits, plan = null, options = {}) {
@@ -406,7 +455,7 @@ async function openOrUpdateReviewRequest(cwd, branch, title, version, previousVe
406
455
  baseBranch: process.env.VERSIONARY_BASE_BRANCH ?? "main",
407
456
  headBranch: branch,
408
457
  title,
409
- body: renderSimpleReviewRequestBody(version, previousVersion, commits, plan, cwd),
458
+ body: renderSimpleReviewRequestBody(version, previousVersion, commits, plan, cwd, options.highlights ?? ""),
410
459
  labels: ["release"],
411
460
  }, {
412
461
  cwd,
@@ -1,4 +1,5 @@
1
1
  export type ReleaseType = "major" | "minor" | "patch" | null;
2
+ export declare function maxReleaseType(types: ReleaseType[]): ReleaseType;
2
3
  export interface ParsedVersion {
3
4
  major: number;
4
5
  minor: number;
@@ -1,9 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.maxReleaseType = maxReleaseType;
3
4
  exports.parseVersion = parseVersion;
4
5
  exports.isValidVersion = isValidVersion;
5
6
  exports.compareVersions = compareVersions;
6
7
  exports.bumpVersion = bumpVersion;
8
+ function maxReleaseType(types) {
9
+ if (types.includes("major")) {
10
+ return "major";
11
+ }
12
+ if (types.includes("minor")) {
13
+ return "minor";
14
+ }
15
+ if (types.includes("patch")) {
16
+ return "patch";
17
+ }
18
+ return null;
19
+ }
7
20
  const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*)(?:\.(?:0|[1-9]\d*|\d*[A-Za-z-][0-9A-Za-z-]*))*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/u;
8
21
  const COMPAT_DOTTED_PRERELEASE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
9
22
  function normalizeVersionInput(version) {
@@ -15,6 +15,7 @@ export interface VersionaryPackage {
15
15
  "changelog-format"?: VersionaryChangelogFormat;
16
16
  "exclude-paths"?: string[];
17
17
  "extra-files"?: VersionaryArtifactRule[];
18
+ follows?: string[];
18
19
  }
19
20
  export interface VersionaryConfig {
20
21
  version: 1;
@@ -26,6 +27,7 @@ export interface VersionaryConfig {
26
27
  "release-reference-comments"?: ReleaseReferenceCommentsMode;
27
28
  "release-branch"?: string;
28
29
  "baseline-file"?: string;
30
+ "next-release-file"?: string;
29
31
  "bootstrap-sha"?: string;
30
32
  "monorepo-mode"?: "independent" | "fixed";
31
33
  "bump-minor-pre-major"?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "versionary",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "Automatic release framework based on conventional commits and semantic versioning",
5
5
  "keywords": [
6
6
  "releasing",