versionary 0.12.0 → 0.13.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
@@ -86,8 +86,10 @@ Current ecosystem policy defaults:
86
86
 
87
87
  - changelog source for publish:
88
88
  - root target uses root `changelog-file`
89
- - package target uses `packages.<path>.changelog-file` when configured, else
90
- root `changelog-file`
89
+ - package target uses package changelog defaults:
90
+ - `packages.<path>.changelog-file` when configured
91
+ - otherwise `NEWS.md` for R targets or `CHANGELOG.md` for other targets at
92
+ `<package-path>/<default-file>`
91
93
  - lockfiles:
92
94
  - Node strategy updates root `package-lock.json`/`npm-shrinkwrap.json` when
93
95
  present
package/dist/cli/index.js CHANGED
@@ -52,7 +52,7 @@ async function main() {
52
52
  }).trim();
53
53
  if ((0, pr_js_1.isReleaseCommitMessage)(commitMessage)) {
54
54
  if (flags["dry-run"] && !flags.json) {
55
- const release = await (0, release_js_1.runSimpleReleaseDetailed)(process.cwd(), {
55
+ const release = await (0, release_js_1.runReleaseDetailed)(process.cwd(), {
56
56
  logger,
57
57
  "dry-run": true,
58
58
  });
@@ -62,7 +62,7 @@ async function main() {
62
62
  }
63
63
  }
64
64
  if (flags.json) {
65
- const release = await (0, release_js_1.runSimpleReleaseDetailed)(process.cwd(), {
65
+ const release = await (0, release_js_1.runReleaseDetailed)(process.cwd(), {
66
66
  logger,
67
67
  "dry-run": flags["dry-run"],
68
68
  });
@@ -93,11 +93,11 @@ async function main() {
93
93
  });
94
94
  return 0;
95
95
  }
96
- const message = await (0, release_js_1.runSimpleRelease)(process.cwd());
96
+ const message = await (0, release_js_1.runRelease)(process.cwd());
97
97
  console.log(message);
98
98
  return 0;
99
99
  }
100
- const plan = (0, plan_js_1.createSimplePlan)();
100
+ const plan = (0, plan_js_1.createReleasePlan)();
101
101
  if (!plan.nextVersion) {
102
102
  const message = "No releasable commits found. Nothing to do.";
103
103
  if (flags.json) {
@@ -140,9 +140,9 @@ async function main() {
140
140
  console.log(dryRunMessage);
141
141
  return 0;
142
142
  }
143
- const pr = (0, pr_js_1.prepareSimpleReleasePr)(process.cwd(), { logger });
143
+ const pr = (0, pr_js_1.prepareReleasePr)(process.cwd(), { logger });
144
144
  (0, pr_js_1.pushReleaseBranch)(process.cwd(), pr.branch);
145
- const reviewResult = await (0, pr_js_1.openOrUpdateSimpleReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan, { logger });
145
+ const reviewResult = await (0, pr_js_1.openOrUpdateReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan, { logger });
146
146
  const message = `Prepared release PR branch ${pr.branch}`;
147
147
  if (flags.json) {
148
148
  emitJson({
@@ -165,18 +165,18 @@ async function main() {
165
165
  return printVerifyResult();
166
166
  }
167
167
  if (command === "plan") {
168
- const plan = (0, plan_js_1.createSimplePlan)();
168
+ const plan = (0, plan_js_1.createReleasePlan)();
169
169
  console.log(JSON.stringify(plan, null, 2));
170
170
  return 0;
171
171
  }
172
172
  if (command === "changelog") {
173
173
  const write = args.includes("--write");
174
- const plan = (0, plan_js_1.createSimplePlan)();
174
+ const plan = (0, plan_js_1.createReleasePlan)();
175
175
  if (!plan.nextVersion) {
176
176
  console.log("No releasable commits found.");
177
177
  return 0;
178
178
  }
179
- const section = (0, changelog_js_1.renderSimpleChangelog)(plan);
179
+ const section = (0, changelog_js_1.renderReleasePlanChangelog)(plan);
180
180
  if (!write) {
181
181
  console.log(section);
182
182
  return 0;
@@ -187,7 +187,7 @@ async function main() {
187
187
  }
188
188
  if (command === "pr") {
189
189
  if (flags["dry-run"]) {
190
- const plan = (0, plan_js_1.createSimplePlan)();
190
+ const plan = (0, plan_js_1.createReleasePlan)();
191
191
  if (!plan.nextVersion) {
192
192
  console.log("No releasable commits found. Nothing to do.");
193
193
  return 0;
@@ -195,9 +195,9 @@ async function main() {
195
195
  console.log(`Dry run: would prepare release PR branch ${plan.releaseBranchPrefix} for ${plan.nextVersion}`);
196
196
  return 0;
197
197
  }
198
- const pr = (0, pr_js_1.prepareSimpleReleasePr)(process.cwd(), { logger: console });
198
+ const pr = (0, pr_js_1.prepareReleasePr)(process.cwd(), { logger: console });
199
199
  (0, pr_js_1.pushReleaseBranch)(process.cwd(), pr.branch);
200
- const reviewResult = await (0, pr_js_1.openOrUpdateSimpleReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan);
200
+ const reviewResult = await (0, pr_js_1.openOrUpdateReviewRequest)(process.cwd(), pr.branch, pr.title, pr.version, pr.previousVersion, pr.commits, pr.plan);
201
201
  console.log(`Prepared release PR branch ${pr.branch}`);
202
202
  console.log(`Title: ${pr.title}`);
203
203
  console.log(reviewResult);
@@ -205,7 +205,7 @@ async function main() {
205
205
  }
206
206
  if (command === "release") {
207
207
  if (flags["dry-run"]) {
208
- const result = await (0, release_js_1.runSimpleReleaseDetailed)(process.cwd(), {
208
+ const result = await (0, release_js_1.runReleaseDetailed)(process.cwd(), {
209
209
  logger,
210
210
  "dry-run": true,
211
211
  });
@@ -220,7 +220,7 @@ async function main() {
220
220
  }
221
221
  return 0;
222
222
  }
223
- const message = await (0, release_js_1.runSimpleRelease)(process.cwd());
223
+ const message = await (0, release_js_1.runRelease)(process.cwd());
224
224
  console.log(message);
225
225
  return 0;
226
226
  }
@@ -228,7 +228,7 @@ async function main() {
228
228
  console.log("Commands:");
229
229
  console.log(" run [--json] [--dry-run] Auto-dispatch release PR/update or release publish by context");
230
230
  console.log(" verify Validate config and basic repository shape");
231
- console.log(" plan Print release plan (simple mode)");
231
+ console.log(" plan Print release plan");
232
232
  console.log(" changelog [--write] Print or write changelog section");
233
233
  console.log(" pr [--dry-run] Prepare release PR commit and branch");
234
234
  console.log(" release [--dry-run] Publish release metadata for release commit context");
@@ -1,6 +1,6 @@
1
1
  import { type ParsedCommit } from "../git/commits.js";
2
2
  import type { VersionaryChangelogFormat } from "../types/config.js";
3
- import type { SimplePlan } from "./plan.js";
3
+ import type { ReleasePlan, SimplePlan } from "./plan.js";
4
4
  export declare function renderSimpleReleaseNotes(input: {
5
5
  currentVersion: string;
6
6
  nextVersion: string;
@@ -13,6 +13,8 @@ export declare function renderSimpleReleaseNotes(input: {
13
13
  }, options?: {
14
14
  includeFooter?: boolean;
15
15
  }): string;
16
+ export declare function renderReleasePlanChangelog(plan: ReleasePlan): string;
17
+ /** @deprecated Use renderReleasePlanChangelog. */
16
18
  export declare function renderSimpleChangelog(plan: SimplePlan): string;
17
19
  export declare function renderPackageChangelogSection(input: {
18
20
  currentVersion: string;
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.renderSimpleReleaseNotes = renderSimpleReleaseNotes;
7
+ exports.renderReleasePlanChangelog = renderReleasePlanChangelog;
7
8
  exports.renderSimpleChangelog = renderSimpleChangelog;
8
9
  exports.renderPackageChangelogSection = renderPackageChangelogSection;
9
10
  exports.prependChangelog = prependChangelog;
@@ -112,7 +113,7 @@ function renderSimpleReleaseNotes(input, options = {}) {
112
113
  }
113
114
  return lines.join("\n");
114
115
  }
115
- function renderSimpleChangelog(plan) {
116
+ function renderReleasePlanChangelog(plan) {
116
117
  if (!plan.nextVersion) {
117
118
  return "";
118
119
  }
@@ -145,6 +146,10 @@ function renderSimpleChangelog(plan) {
145
146
  dependencies,
146
147
  });
147
148
  }
149
+ /** @deprecated Use renderReleasePlanChangelog. */
150
+ function renderSimpleChangelog(plan) {
151
+ return renderReleasePlanChangelog(plan);
152
+ }
148
153
  function renderPackageChangelogSection(input) {
149
154
  const repoUrl = (0, repo_url_js_1.resolveRepositoryWebBaseUrl)(input.cwd ?? process.cwd());
150
155
  const header = repoUrl
@@ -172,15 +177,16 @@ function prependChangelog(cwd, changelogFile, section, format = "markdown-change
172
177
  ? node_fs_1.default.readFileSync(changelogPath, "utf8")
173
178
  : "";
174
179
  if (format === "r-news") {
175
- const separator = existing.length > 0 ? "\n\n" : "";
176
- const next = `${section}${separator}${existing}`.trimEnd() + "\n";
180
+ const bodyWithoutDevHeader = existing.replace(/^#\s+.+\s+\(development version\)\s*(?:\r?\n)*/u, "");
181
+ const separator = bodyWithoutDevHeader.length > 0 ? "\n\n" : "";
182
+ const next = `${`${section}${separator}${bodyWithoutDevHeader}`.trimEnd()}\n`;
177
183
  node_fs_1.default.writeFileSync(changelogPath, next, "utf8");
178
184
  return;
179
185
  }
180
186
  const heading = "# Changelog\n\n";
181
187
  const bodyWithoutHeading = existing.replace(/^# Changelog\s*/u, "");
182
188
  const separator = existing.length > 0 ? "\n" : "";
183
- const next = `${heading}${section}${separator}${bodyWithoutHeading}`.trimEnd() + "\n";
189
+ const next = `${`${heading}${section}${separator}${bodyWithoutHeading}`.trimEnd()}\n`;
184
190
  node_fs_1.default.writeFileSync(changelogPath, next, "utf8");
185
191
  }
186
192
  function renderRNewsReleaseNotes(input) {
@@ -1,7 +1,7 @@
1
1
  import { type ParsedCommit } from "../git/commits.js";
2
2
  import type { VersionaryChangelogFormat, VersionaryConfig } from "../types/config.js";
3
3
  import { type ReleaseType } from "./semver.js";
4
- export interface SimplePlan {
4
+ export interface ReleasePlan {
5
5
  mode: "simple";
6
6
  releaseType: ReleaseType;
7
7
  currentVersion: string;
@@ -22,6 +22,8 @@ export interface SimplePlan {
22
22
  commits: ParsedCommit[];
23
23
  }>;
24
24
  }
25
+ /** @deprecated Use ReleasePlan. */
26
+ export type SimplePlan = ReleasePlan;
25
27
  export declare function getChangelogDefaults(config: {
26
28
  "release-type"?: VersionaryConfig["release-type"];
27
29
  "changelog-file"?: VersionaryConfig["changelog-file"];
@@ -30,4 +32,6 @@ export declare function getChangelogDefaults(config: {
30
32
  changelogFile: string;
31
33
  changelogFormat: VersionaryChangelogFormat;
32
34
  };
33
- export declare function createSimplePlan(cwd?: string): SimplePlan;
35
+ export declare function createReleasePlan(cwd?: string): ReleasePlan;
36
+ /** @deprecated Use createReleasePlan. */
37
+ export declare function createSimplePlan(cwd?: string): ReleasePlan;
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.getChangelogDefaults = getChangelogDefaults;
7
+ exports.createReleasePlan = createReleasePlan;
7
8
  exports.createSimplePlan = createSimplePlan;
8
9
  const node_fs_1 = __importDefault(require("node:fs"));
9
10
  const node_path_1 = __importDefault(require("node:path"));
@@ -23,7 +24,7 @@ function getChangelogDefaults(config) {
23
24
  (changelogFormat === "r-news" ? "NEWS.md" : "CHANGELOG.md");
24
25
  return { changelogFile, changelogFormat };
25
26
  }
26
- function createSimplePlan(cwd = process.cwd()) {
27
+ function createReleasePlan(cwd = process.cwd()) {
27
28
  const loaded = (0, load_config_js_1.loadConfig)(cwd);
28
29
  const strategy = (0, resolve_js_1.resolveVersionStrategy)(loaded.config);
29
30
  const versionFile = strategy.getVersionFile(loaded.config);
@@ -189,3 +190,7 @@ function createSimplePlan(cwd = process.cwd()) {
189
190
  packages: adjustedPackages,
190
191
  };
191
192
  }
193
+ /** @deprecated Use createReleasePlan. */
194
+ function createSimplePlan(cwd = process.cwd()) {
195
+ return createReleasePlan(cwd);
196
+ }
@@ -1,11 +1,11 @@
1
1
  import type { ParsedCommit } from "../git/commits.js";
2
2
  import type { VersionaryPluginContext } from "../types/plugins.js";
3
- import { type SimplePlan } from "./plan.js";
3
+ import { type ReleasePlan, type SimplePlan } from "./plan.js";
4
4
  export declare function splitSafeDirtyFiles(files: string[]): {
5
5
  ignored: string[];
6
6
  blocking: string[];
7
7
  };
8
- export declare function prepareSimpleReleasePr(cwd?: string, options?: {
8
+ export declare function prepareReleasePr(cwd?: string, options?: {
9
9
  logger?: VersionaryPluginContext["logger"];
10
10
  }): {
11
11
  branch: string;
@@ -13,9 +13,17 @@ export declare function prepareSimpleReleasePr(cwd?: string, options?: {
13
13
  version: string;
14
14
  previousVersion: string;
15
15
  commits: ParsedCommit[];
16
- plan: SimplePlan;
16
+ plan: ReleasePlan;
17
17
  };
18
18
  export declare function renderSimpleReviewRequestBody(version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, cwd?: string): string;
19
+ export declare function openOrUpdateReviewRequest(cwd: string, branch: string, title: string, version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, options?: {
20
+ logger?: VersionaryPluginContext["logger"];
21
+ }): Promise<string>;
22
+ /** @deprecated Use prepareReleasePr. */
23
+ export declare function prepareSimpleReleasePr(cwd?: string, options?: {
24
+ logger?: VersionaryPluginContext["logger"];
25
+ }): ReturnType<typeof prepareReleasePr>;
26
+ /** @deprecated Use openOrUpdateReviewRequest. */
19
27
  export declare function openOrUpdateSimpleReviewRequest(cwd: string, branch: string, title: string, version: string, previousVersion: string, commits: ParsedCommit[], plan?: SimplePlan | null, options?: {
20
28
  logger?: VersionaryPluginContext["logger"];
21
29
  }): Promise<string>;
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.splitSafeDirtyFiles = splitSafeDirtyFiles;
7
- exports.prepareSimpleReleasePr = prepareSimpleReleasePr;
7
+ exports.prepareReleasePr = prepareReleasePr;
8
8
  exports.renderSimpleReviewRequestBody = renderSimpleReviewRequestBody;
9
+ exports.openOrUpdateReviewRequest = openOrUpdateReviewRequest;
10
+ exports.prepareSimpleReleasePr = prepareSimpleReleasePr;
9
11
  exports.openOrUpdateSimpleReviewRequest = openOrUpdateSimpleReviewRequest;
10
12
  exports.pushReleaseBranch = pushReleaseBranch;
11
13
  exports.isReleaseCommitMessage = isReleaseCommitMessage;
@@ -202,8 +204,8 @@ function formatReleaseCommitTitle(releaseTargets) {
202
204
  }
203
205
  return `chore(release): ${tags[0]} (+${tags.length - 1} more)`;
204
206
  }
205
- function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
206
- const plan = (0, plan_js_1.createSimplePlan)(cwd);
207
+ function prepareReleasePr(cwd = process.cwd(), options = {}) {
208
+ const plan = (0, plan_js_1.createReleasePlan)(cwd);
207
209
  const loaded = (0, load_config_js_1.loadConfig)(cwd);
208
210
  const strategy = (0, resolve_js_1.resolveVersionStrategy)(loaded.config);
209
211
  if (!plan.nextVersion) {
@@ -253,7 +255,7 @@ function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
253
255
  const updatedArtifactFiles = (0, artifact_rules_js_1.applyConfiguredArtifactRules)(cwd, loaded.config, plan);
254
256
  const updatedRustLockFiles = ensureCargoLockUpToDate(cwd);
255
257
  const packageReleaseMetadata = buildPackageReleaseMetadata(cwd, plan, loaded.config);
256
- const section = (0, changelog_js_1.renderSimpleChangelog)(plan);
258
+ const section = (0, changelog_js_1.renderReleasePlanChangelog)(plan);
257
259
  (0, changelog_js_1.prependChangelog)(cwd, plan.changelogFile, section, plan.changelogFormat);
258
260
  const updatedChangelogFiles = [plan.changelogFile];
259
261
  for (const packagePlan of plan.packages ?? []) {
@@ -261,10 +263,11 @@ function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
261
263
  continue;
262
264
  }
263
265
  const packageConfig = loaded.config.packages?.[packagePlan.path] ?? {};
264
- const packageChangelogFile = packageConfig["changelog-file"];
265
- if (!packageChangelogFile) {
266
- continue;
267
- }
266
+ const { changelogFile: packageChangelogFile } = (0, plan_js_1.getChangelogDefaults)({
267
+ "release-type": packageConfig["release-type"] ?? loaded.config["release-type"],
268
+ "changelog-file": packageConfig["changelog-file"] ?? loaded.config["changelog-file"],
269
+ "changelog-format": packageConfig["changelog-format"] ?? loaded.config["changelog-format"],
270
+ });
268
271
  const packageMetadata = packageReleaseMetadata[packagePlan.path];
269
272
  if (!packageMetadata) {
270
273
  continue;
@@ -342,9 +345,28 @@ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan =
342
345
  .sort((a, b) => a.name.localeCompare(b.name));
343
346
  return directSources;
344
347
  };
348
+ const relabelSectionHeader = (notes, packageLabel, nextVersion) => {
349
+ const linkedHeader = notes.match(/^##\s+\[([^\]]+)\]\(([^)]+)\)\s+\(([^)]+)\)/u);
350
+ if (linkedHeader) {
351
+ const [, , compareUrl, date] = linkedHeader;
352
+ return notes.replace(/^##\s+\[[^\]]+\]\([^)]+\)\s+\([^)]+\)/u, `## [${packageLabel}: ${nextVersion}](${compareUrl}) (${date})`);
353
+ }
354
+ const plainHeader = notes.match(/^##\s+([^\s]+)\s+\(([^)]+)\)/u);
355
+ if (plainHeader) {
356
+ const [, , date] = plainHeader;
357
+ return notes.replace(/^##\s+[^\s]+\s+\([^)]+\)/u, `## ${packageLabel}: ${nextVersion} (${date})`);
358
+ }
359
+ return notes;
360
+ };
345
361
  if (plan?.packages && plan.packages.length > 1) {
346
- const sections = plan.packages
347
- .filter((pkg) => pkg.nextVersion)
362
+ const sections = [];
363
+ const rootPackage = plan.packages.find((pkg) => pkg.path === "." && pkg.nextVersion);
364
+ if (rootPackage?.nextVersion) {
365
+ const rootNotes = (0, changelog_js_1.renderReleasePlanChangelog)(plan);
366
+ sections.push(relabelSectionHeader(rootNotes, formatPackageLabel("."), rootPackage.nextVersion));
367
+ }
368
+ const packageSections = plan.packages
369
+ .filter((pkg) => pkg.path !== "." && pkg.nextVersion)
348
370
  .map((pkg) => {
349
371
  const packageLabel = formatPackageLabel(pkg.path);
350
372
  const propagatedDependencies = findPropagatedDependencies(pkg.path, plan.packages ?? []);
@@ -355,20 +377,14 @@ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan =
355
377
  cwd,
356
378
  dependencies: propagatedDependencies,
357
379
  }, { includeFooter: false });
358
- const linkedHeader = notes.match(/^##\s+\[([^\]]+)\]\(([^)]+)\)\s+\(([^)]+)\)/u);
359
- if (linkedHeader) {
360
- const [, , compareUrl, date] = linkedHeader;
361
- return notes.replace(/^##\s+\[[^\]]+\]\([^)]+\)\s+\([^)]+\)/u, `## [${packageLabel}: ${pkg.nextVersion ?? ""}](${compareUrl}) (${date})`);
362
- }
363
- const plainHeader = notes.match(/^##\s+([^\s]+)\s+\(([^)]+)\)/u);
364
- if (plainHeader) {
365
- const [, , date] = plainHeader;
366
- return notes.replace(/^##\s+[^\s]+\s+\([^)]+\)/u, `## ${packageLabel}: ${pkg.nextVersion ?? ""} (${date})`);
367
- }
368
- return notes;
369
- })
370
- .join("\n\n");
371
- return `${sections}\n\nThis PR was generated by Versionary.`;
380
+ return relabelSectionHeader(notes, packageLabel, pkg.nextVersion ?? "");
381
+ });
382
+ sections.push(...packageSections);
383
+ const bodySections = sections.join("\n\n");
384
+ if (bodySections.length === 0) {
385
+ return "This PR was generated by Versionary.";
386
+ }
387
+ return `${bodySections}\n\nThis PR was generated by Versionary.`;
372
388
  }
373
389
  return (0, changelog_js_1.renderSimpleReleaseNotes)({
374
390
  currentVersion: previousVersion,
@@ -377,7 +393,7 @@ function renderSimpleReviewRequestBody(version, previousVersion, commits, plan =
377
393
  cwd,
378
394
  }, { includeFooter: true });
379
395
  }
380
- async function openOrUpdateSimpleReviewRequest(cwd, branch, title, version, previousVersion, commits, plan = null, options = {}) {
396
+ async function openOrUpdateReviewRequest(cwd, branch, title, version, previousVersion, commits, plan = null, options = {}) {
381
397
  const loaded = (0, load_config_js_1.loadConfig)(cwd);
382
398
  const releaseFlow = loaded.config["review-mode"] ?? "pr";
383
399
  if (releaseFlow === "direct") {
@@ -396,6 +412,14 @@ async function openOrUpdateSimpleReviewRequest(cwd, branch, title, version, prev
396
412
  });
397
413
  return result.url;
398
414
  }
415
+ /** @deprecated Use prepareReleasePr. */
416
+ function prepareSimpleReleasePr(cwd = process.cwd(), options = {}) {
417
+ return prepareReleasePr(cwd, options);
418
+ }
419
+ /** @deprecated Use openOrUpdateReviewRequest. */
420
+ async function openOrUpdateSimpleReviewRequest(cwd, branch, title, version, previousVersion, commits, plan = null, options = {}) {
421
+ return openOrUpdateReviewRequest(cwd, branch, title, version, previousVersion, commits, plan, options);
422
+ }
399
423
  function pushReleaseBranch(cwd, branch) {
400
424
  (0, node_child_process_1.execFileSync)("git", ["push", "--force-with-lease", "origin", branch], {
401
425
  cwd,
@@ -1,8 +1,9 @@
1
1
  import type { VersionaryConfig } from "../types/config.js";
2
2
  import type { VersionaryPluginContext } from "../types/plugins.js";
3
+ export declare function extractReleaseNotes(content: string, version: string, changelogFormat: "markdown-changelog" | "r-news"): string;
3
4
  export declare function resolveTargetChangelogFile(config: VersionaryConfig, rootChangelogFile: string, targetPath: string): string;
4
- export declare function runSimpleRelease(cwd?: string): Promise<string>;
5
- export type SimpleRunReleaseResult = {
5
+ export declare function runRelease(cwd?: string): Promise<string>;
6
+ export type RunReleaseResult = {
6
7
  action: "release-skipped";
7
8
  reason: string;
8
9
  } | {
@@ -22,8 +23,16 @@ export type SimpleRunReleaseResult = {
22
23
  metadataStatus: "created" | "exists";
23
24
  }[];
24
25
  };
25
- export interface RunSimpleReleaseOptions {
26
+ export interface RunReleaseOptions {
26
27
  logger?: VersionaryPluginContext["logger"];
27
28
  "dry-run"?: boolean;
28
29
  }
30
+ export declare function runReleaseDetailed(cwd?: string, options?: RunReleaseOptions): Promise<RunReleaseResult>;
31
+ /** @deprecated Use RunReleaseResult. */
32
+ export type SimpleRunReleaseResult = RunReleaseResult;
33
+ /** @deprecated Use RunReleaseOptions. */
34
+ export type RunSimpleReleaseOptions = RunReleaseOptions;
35
+ /** @deprecated Use runRelease. */
36
+ export declare function runSimpleRelease(cwd?: string): Promise<string>;
37
+ /** @deprecated Use runReleaseDetailed. */
29
38
  export declare function runSimpleReleaseDetailed(cwd?: string, options?: RunSimpleReleaseOptions): Promise<SimpleRunReleaseResult>;
@@ -3,7 +3,10 @@ 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.extractReleaseNotes = extractReleaseNotes;
6
7
  exports.resolveTargetChangelogFile = resolveTargetChangelogFile;
8
+ exports.runRelease = runRelease;
9
+ exports.runReleaseDetailed = runReleaseDetailed;
7
10
  exports.runSimpleRelease = runSimpleRelease;
8
11
  exports.runSimpleReleaseDetailed = runSimpleReleaseDetailed;
9
12
  const node_child_process_1 = require("node:child_process");
@@ -16,60 +19,91 @@ const plan_js_1 = require("./plan.js");
16
19
  const pr_js_1 = require("./pr.js");
17
20
  const recovery_js_1 = require("./recovery.js");
18
21
  const state_js_1 = require("./state.js");
19
- function getHeadCommitMessage(cwd) {
20
- return (0, node_child_process_1.execFileSync)("git", ["log", "-1", "--pretty=%B"], {
21
- cwd,
22
- encoding: "utf8",
23
- stdio: ["ignore", "pipe", "ignore"],
24
- }).trim();
22
+ function escapeRegExp(input) {
23
+ return input.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&");
25
24
  }
26
- function readReleaseNotes(cwd, version, changelogFile) {
27
- const changelogPath = node_path_1.default.join(cwd, changelogFile);
28
- if (!node_fs_1.default.existsSync(changelogPath)) {
29
- return `Automated release for v${version}`;
30
- }
31
- const content = node_fs_1.default.readFileSync(changelogPath, "utf8");
25
+ function extractReleaseNotes(content, version, changelogFormat) {
32
26
  const lines = content.split("\n");
33
27
  const shortVersion = version.replace(/\.\d+$/u, "");
34
- const start = lines.findIndex((line) => line.startsWith(`## ${version} -`) ||
35
- line.startsWith(`## [${version}](`) ||
36
- line.startsWith(`# ${shortVersion} -`) ||
37
- line.startsWith(`# [${shortVersion}](`) ||
38
- new RegExp(`^#\\s+.+\\s+${shortVersion}\\s*$`, "u").test(line));
28
+ const start = changelogFormat === "r-news"
29
+ ? lines.findIndex((line) => {
30
+ const fullMatch = new RegExp(`^#\\s+.+\\s+${escapeRegExp(version)}\\s*$`, "u");
31
+ const shortMatch = new RegExp(`^#\\s+.+\\s+${escapeRegExp(shortVersion)}\\s*$`, "u");
32
+ return fullMatch.test(line) || shortMatch.test(line);
33
+ })
34
+ : lines.findIndex((line) => line.startsWith(`## ${version} -`) ||
35
+ line.startsWith(`## [${version}](`) ||
36
+ line.startsWith(`# ${shortVersion} -`) ||
37
+ line.startsWith(`# [${shortVersion}](`) ||
38
+ new RegExp(`^#\\s+.+\\s+${shortVersion}\\s*$`, "u").test(line));
39
39
  if (start < 0) {
40
- return `Automated release for v${version}`;
40
+ return "";
41
41
  }
42
42
  let end = lines.length;
43
43
  for (let idx = start + 1; idx < lines.length; idx += 1) {
44
- if (lines[idx]?.startsWith("## ")) {
44
+ const line = lines[idx] ?? "";
45
+ if (changelogFormat === "r-news") {
46
+ if (/^#(?!#)\s+/u.test(line)) {
47
+ end = idx;
48
+ break;
49
+ }
50
+ continue;
51
+ }
52
+ if (line.startsWith("## ")) {
45
53
  end = idx;
46
54
  break;
47
55
  }
48
56
  }
49
- const notes = lines
57
+ return lines
50
58
  .slice(start + 1, end)
51
59
  .join("\n")
52
60
  .trim();
61
+ }
62
+ function getHeadCommitMessage(cwd) {
63
+ return (0, node_child_process_1.execFileSync)("git", ["log", "-1", "--pretty=%B"], {
64
+ cwd,
65
+ encoding: "utf8",
66
+ stdio: ["ignore", "pipe", "ignore"],
67
+ }).trim();
68
+ }
69
+ function readReleaseNotes(cwd, version, changelogFile, changelogFormat) {
70
+ const changelogPath = node_path_1.default.join(cwd, changelogFile);
71
+ if (!node_fs_1.default.existsSync(changelogPath)) {
72
+ return `Automated release for v${version}`;
73
+ }
74
+ const content = node_fs_1.default.readFileSync(changelogPath, "utf8");
75
+ const notes = extractReleaseNotes(content, version, changelogFormat);
53
76
  return notes.length > 0 ? notes : `Automated release for v${version}`;
54
77
  }
55
78
  function resolveTargetChangelogFile(config, rootChangelogFile, targetPath) {
56
79
  if (targetPath === ".") {
57
80
  return rootChangelogFile;
58
81
  }
59
- const packageChangelogFile = config.packages?.[targetPath]?.["changelog-file"];
60
- if (!packageChangelogFile) {
61
- return rootChangelogFile;
62
- }
82
+ const packageConfig = config.packages?.[targetPath];
83
+ const { changelogFile: packageChangelogFile } = (0, plan_js_1.getChangelogDefaults)({
84
+ "release-type": packageConfig?.["release-type"] ?? config["release-type"],
85
+ "changelog-file": packageConfig?.["changelog-file"] ?? config["changelog-file"],
86
+ "changelog-format": packageConfig?.["changelog-format"] ?? config["changelog-format"],
87
+ });
63
88
  return node_path_1.default.posix.join(targetPath, packageChangelogFile);
64
89
  }
65
- async function runSimpleRelease(cwd = process.cwd()) {
66
- const result = await runSimpleReleaseDetailed(cwd, { logger: console });
90
+ function resolveTargetChangelogFormat(config, targetPath) {
91
+ const packageConfig = targetPath === "." ? undefined : config.packages?.[targetPath];
92
+ const { changelogFormat } = (0, plan_js_1.getChangelogDefaults)({
93
+ "release-type": packageConfig?.["release-type"] ?? config["release-type"],
94
+ "changelog-file": packageConfig?.["changelog-file"] ?? config["changelog-file"],
95
+ "changelog-format": packageConfig?.["changelog-format"] ?? config["changelog-format"],
96
+ });
97
+ return changelogFormat;
98
+ }
99
+ async function runRelease(cwd = process.cwd()) {
100
+ const result = await runReleaseDetailed(cwd, { logger: console });
67
101
  if (result.action === "release-skipped") {
68
102
  return result.reason;
69
103
  }
70
104
  return result.message;
71
105
  }
72
- async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
106
+ async function runReleaseDetailed(cwd = process.cwd(), options = {}) {
73
107
  const commitMessage = getHeadCommitMessage(cwd);
74
108
  if (!(0, pr_js_1.isReleaseCommitMessage)(commitMessage)) {
75
109
  return {
@@ -107,10 +141,11 @@ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
107
141
  const releases = [];
108
142
  for (const target of targets) {
109
143
  const targetChangelogFile = resolveTargetChangelogFile(loaded.config, changelogFile, target.path);
144
+ const targetChangelogFormat = resolveTargetChangelogFormat(loaded.config, target.path);
110
145
  const outcome = await (0, recovery_js_1.executeIdempotentReleaseTarget)(cwd, {
111
146
  tag: target.tag,
112
147
  version: target.version,
113
- notes: readReleaseNotes(cwd, target.version, targetChangelogFile),
148
+ notes: readReleaseNotes(cwd, target.version, targetChangelogFile, targetChangelogFormat),
114
149
  draft: loaded.config["release-draft"] ?? false,
115
150
  }, {
116
151
  createReleaseMetadata: (input) => scmClient.createReleaseMetadata(input, {
@@ -133,3 +168,11 @@ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
133
168
  message: `Published releases ${published.join(", ")}`,
134
169
  };
135
170
  }
171
+ /** @deprecated Use runRelease. */
172
+ async function runSimpleRelease(cwd = process.cwd()) {
173
+ return runRelease(cwd);
174
+ }
175
+ /** @deprecated Use runReleaseDetailed. */
176
+ async function runSimpleReleaseDetailed(cwd = process.cwd(), options = {}) {
177
+ return runReleaseDetailed(cwd, options);
178
+ }
@@ -5,10 +5,21 @@ exports.isValidVersion = isValidVersion;
5
5
  exports.compareVersions = compareVersions;
6
6
  exports.bumpVersion = bumpVersion;
7
7
  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
+ const COMPAT_DOTTED_PRERELEASE_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/u;
9
+ function normalizeVersionInput(version) {
10
+ const trimmed = version.trim();
11
+ const compatMatch = trimmed.match(COMPAT_DOTTED_PRERELEASE_PATTERN);
12
+ if (!compatMatch) {
13
+ return trimmed;
14
+ }
15
+ const [, major, minor, patch, prerelease] = compatMatch;
16
+ return `${major}.${minor}.${patch}-${prerelease}`;
17
+ }
8
18
  function parseVersion(version) {
9
- const match = version.trim().match(SEMVER_PATTERN);
19
+ const normalized = normalizeVersionInput(version);
20
+ const match = normalized.match(SEMVER_PATTERN);
10
21
  if (!match) {
11
- throw new Error(`Invalid version in version file: "${version}". Expected SemVer 2.0.0 format.`);
22
+ throw new Error(`Invalid version in version file: "${version}". Expected SemVer 2.0.0 format (or compatibility form X.Y.Z.W).`);
12
23
  }
13
24
  return {
14
25
  major: Number(match[1]),
@@ -19,7 +30,7 @@ function parseVersion(version) {
19
30
  };
20
31
  }
21
32
  function isValidVersion(version) {
22
- return SEMVER_PATTERN.test(version.trim());
33
+ return SEMVER_PATTERN.test(normalizeVersionInput(version));
23
34
  }
24
35
  function isNumericIdentifier(identifier) {
25
36
  return /^(0|[1-9]\d*)$/u.test(identifier);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "versionary",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "Automatic release framework based on conventional commits and semantic versioning",
5
5
  "keywords": [
6
6
  "releasing",