relizy 0.2.4 → 0.2.5-beta.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.
@@ -1,11 +1,11 @@
1
1
  import { logger, execPromise } from '@maz-ui/node';
2
2
  import { execSync } from 'node:child_process';
3
3
  import { existsSync, readFileSync, writeFileSync, statSync } from 'node:fs';
4
- import path, { join } from 'node:path';
4
+ import path, { join, relative } from 'node:path';
5
5
  import process$1 from 'node:process';
6
6
  import { upperFirst, formatJson } from '@maz-ui/utils';
7
7
  import { setupDotenv, loadConfig } from 'c12';
8
- import { formatCompareChanges, formatReference, resolveRepoConfig, getRepoConfig, createGithubRelease, getGitDiff, parseCommits, determineSemverChange } from 'changelogen';
8
+ import { formatCompareChanges, formatReference, resolveRepoConfig, getRepoConfig, createGithubRelease, getGitDiff, parseCommits } from 'changelogen';
9
9
  import { defu } from 'defu';
10
10
  import fastGlob from 'fast-glob';
11
11
  import { confirm, input } from '@inquirer/prompts';
@@ -13,154 +13,6 @@ import * as semver from 'semver';
13
13
  import { convert } from 'convert-gitmoji';
14
14
  import { fetch as fetch$1 } from 'node-fetch-native';
15
15
 
16
- async function generateMarkDown({
17
- commits,
18
- config,
19
- from,
20
- to
21
- }) {
22
- const typeGroups = groupBy(commits, "type");
23
- const markdown = [];
24
- const breakingChanges = [];
25
- const updatedConfig = {
26
- ...config,
27
- from,
28
- to
29
- };
30
- const versionTitle = updatedConfig.to;
31
- markdown.push("", `## ${versionTitle || `${updatedConfig.from || ""}...${updatedConfig.to}`}`, "");
32
- if (updatedConfig.repo && updatedConfig.from && versionTitle) {
33
- const formattedCompareLink = formatCompareChanges(versionTitle, updatedConfig);
34
- markdown.push(formattedCompareLink);
35
- }
36
- for (const type in updatedConfig.types) {
37
- const group = typeGroups[type];
38
- if (!group || group.length === 0) {
39
- continue;
40
- }
41
- if (typeof updatedConfig.types[type] === "boolean") {
42
- continue;
43
- }
44
- markdown.push("", `### ${updatedConfig.types[type]?.title}`, "");
45
- for (const commit of group.reverse()) {
46
- const line = formatCommit(commit, updatedConfig);
47
- markdown.push(line);
48
- if (commit.isBreaking) {
49
- breakingChanges.push(line);
50
- }
51
- }
52
- }
53
- if (breakingChanges.length > 0) {
54
- markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
55
- }
56
- const _authors = /* @__PURE__ */ new Map();
57
- for (const commit of commits) {
58
- if (!commit.author) {
59
- continue;
60
- }
61
- const name = formatName(commit.author.name);
62
- if (!name || name.includes("[bot]")) {
63
- continue;
64
- }
65
- if (updatedConfig.excludeAuthors && updatedConfig.excludeAuthors.some(
66
- (v) => name.includes(v) || commit.author.email?.includes(v)
67
- )) {
68
- continue;
69
- }
70
- if (_authors.has(name)) {
71
- const entry = _authors.get(name);
72
- entry?.email.add(commit.author.email);
73
- } else {
74
- _authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
75
- }
76
- }
77
- await Promise.all(
78
- [..._authors.keys()].map(async (authorName) => {
79
- const meta = _authors.get(authorName);
80
- if (!meta) {
81
- return;
82
- }
83
- for (const data of [...meta.email, meta.name]) {
84
- const { user } = await fetch$1(`https://ungh.cc/users/find/${data}`).then((r) => r.json()).catch(() => ({ user: null }));
85
- if (user) {
86
- meta.github = user.username;
87
- break;
88
- }
89
- }
90
- })
91
- );
92
- const authors = [..._authors.entries()].map((e) => ({
93
- name: e[0],
94
- ...e[1]
95
- }));
96
- if (authors.length > 0 && !updatedConfig.noAuthors) {
97
- markdown.push(
98
- "",
99
- "### \u2764\uFE0F Contributors",
100
- "",
101
- ...authors.map((i) => {
102
- const _email = [...i.email].find(
103
- (e) => !e.includes("noreply.github.com")
104
- );
105
- const email = updatedConfig.hideAuthorEmail !== true && _email ? ` <${_email}>` : "";
106
- const github = i.github ? ` ([@${i.github}](https://github.com/${i.github}))` : "";
107
- return `- ${i.name}${github || email || ""}`;
108
- })
109
- );
110
- }
111
- const result = convert(markdown.join("\n").trim(), true);
112
- return result;
113
- }
114
- function getCommitBody(commit) {
115
- if (!commit.body) {
116
- return "";
117
- }
118
- const lines = commit.body.split("\n");
119
- const contentLines = lines.filter((line) => {
120
- const trimmedLine = line.trim();
121
- if (!trimmedLine) {
122
- return false;
123
- }
124
- const isFileLine = /^[AMDRC]\s+/.test(trimmedLine);
125
- const R100 = /R100\s+/.test(trimmedLine);
126
- return !isFileLine && !R100;
127
- });
128
- if (contentLines.length === 0) {
129
- return "";
130
- }
131
- const indentedBody = contentLines.map((line) => ` ${line}`).join("\n");
132
- return `
133
-
134
- ${indentedBody}
135
- `;
136
- }
137
- function formatCommit(commit, config) {
138
- const body = config.changelog.includeCommitBody ? getCommitBody(commit) : "";
139
- return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(commit.description)}${formatReferences(commit.references, config)}${body}`;
140
- }
141
- function formatReferences(references, config) {
142
- const pr = references.filter((ref) => ref.type === "pull-request");
143
- const issue = references.filter((ref) => ref.type === "issue");
144
- if (pr.length > 0 || issue.length > 0) {
145
- return ` (${[...pr, ...issue].map((ref) => formatReference(ref, config.repo)).join(", ")})`;
146
- }
147
- if (references.length > 0) {
148
- return ` (${formatReference(references[0], config.repo)})`;
149
- }
150
- return "";
151
- }
152
- function formatName(name = "") {
153
- return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
154
- }
155
- function groupBy(items, key) {
156
- const groups = {};
157
- for (const item of items) {
158
- groups[item[key]] = groups[item[key]] || [];
159
- groups[item[key]]?.push(item);
160
- }
161
- return groups;
162
- }
163
-
164
16
  async function executeHook(hook, config, dryRun, params) {
165
17
  const hookInput = config.hooks?.[hook];
166
18
  if (!hookInput) {
@@ -291,23 +143,406 @@ async function executeBuildCmd({
291
143
  logger.debug("No build command specified");
292
144
  }
293
145
  }
146
+ function isBumpedPackage(pkg) {
147
+ return "oldVersion" in pkg && !!pkg.oldVersion;
148
+ }
149
+
150
+ function getGitStatus(cwd) {
151
+ return execSync("git status --porcelain", {
152
+ cwd,
153
+ encoding: "utf8"
154
+ }).trim();
155
+ }
156
+ function checkGitStatusIfDirty() {
157
+ logger.debug("Checking git status");
158
+ const dirty = getGitStatus();
159
+ if (dirty) {
160
+ logger.debug("git status:", `
161
+ ${dirty.trim().split("\n").map((line) => line.trim()).join("\n")}`);
162
+ const error = `Git status is dirty!
163
+
164
+ Please commit or stash your changes before bumping or use --no-clean flag.
165
+
166
+ Unstaged files:
167
+
168
+ ${dirty.trim()}`;
169
+ throw new Error(error);
170
+ }
171
+ }
172
+ async function fetchGitTags(cwd) {
173
+ logger.debug("Fetching git tags from remote");
174
+ try {
175
+ await execPromise("git fetch --tags", { cwd, noStderr: true, noStdout: true, noSuccess: true });
176
+ logger.debug("Git tags fetched successfully");
177
+ } catch (error) {
178
+ logger.fail("Failed to fetch some git tags from remote (tags might already exist locally)", error);
179
+ logger.info("Continuing with local tags");
180
+ }
181
+ }
182
+ function detectGitProvider(cwd = process.cwd()) {
183
+ try {
184
+ const remoteUrl = execSync("git remote get-url origin", {
185
+ cwd,
186
+ encoding: "utf8"
187
+ }).trim();
188
+ if (remoteUrl.includes("github.com")) {
189
+ return "github";
190
+ }
191
+ if (remoteUrl.includes("gitlab.com") || remoteUrl.includes("gitlab")) {
192
+ return "gitlab";
193
+ }
194
+ return null;
195
+ } catch {
196
+ return null;
197
+ }
198
+ }
199
+ function parseGitRemoteUrl(remoteUrl) {
200
+ const sshRegex = /git@[\w.-]+:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
201
+ const httpsRegex = /https?:\/\/[\w.-]+\/([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
202
+ const sshMatch = remoteUrl.match(sshRegex);
203
+ if (sshMatch) {
204
+ return {
205
+ owner: sshMatch[1],
206
+ repo: sshMatch[2]
207
+ };
208
+ }
209
+ const httpsMatch = remoteUrl.match(httpsRegex);
210
+ if (httpsMatch) {
211
+ return {
212
+ owner: httpsMatch[1],
213
+ repo: httpsMatch[2]
214
+ };
215
+ }
216
+ return null;
217
+ }
218
+ async function createCommitAndTags({
219
+ config,
220
+ noVerify,
221
+ bumpedPackages,
222
+ newVersion,
223
+ dryRun,
224
+ logLevel
225
+ }) {
226
+ const internalConfig = config || await loadRelizyConfig();
227
+ try {
228
+ await executeHook("before:commit-and-tag", internalConfig, dryRun ?? false);
229
+ const filePatternsToAdd = [
230
+ "package.json",
231
+ "lerna.json",
232
+ "CHANGELOG.md",
233
+ "**/CHANGELOG.md",
234
+ "**/package.json"
235
+ ];
236
+ logger.start("Start commit and tag");
237
+ logger.debug("Adding files to git staging area...");
238
+ for (const pattern of filePatternsToAdd) {
239
+ if (pattern === "lerna.json" && !hasLernaJson(internalConfig.cwd)) {
240
+ logger.verbose(`Skipping lerna.json as it doesn't exist`);
241
+ continue;
242
+ }
243
+ if ((pattern === "lerna.json" || pattern === "CHANGELOG.md") && !existsSync(join(internalConfig.cwd, pattern))) {
244
+ logger.verbose(`Skipping ${pattern} as it doesn't exist`);
245
+ continue;
246
+ }
247
+ if (dryRun) {
248
+ logger.info(`[dry-run] git add ${pattern}`);
249
+ continue;
250
+ }
251
+ try {
252
+ logger.debug(`git add ${pattern}`);
253
+ execSync(`git add ${pattern}`);
254
+ } catch {
255
+ }
256
+ }
257
+ const rootPackage = readPackageJson(internalConfig.cwd);
258
+ newVersion = newVersion || rootPackage.version;
259
+ const versionForMessage = internalConfig.monorepo?.versionMode === "independent" ? bumpedPackages?.map((pkg) => getIndependentTag(pkg)).join(", ") || "unknown" : newVersion || "unknown";
260
+ const commitMessage = internalConfig.templates.commitMessage?.replaceAll("{{newVersion}}", versionForMessage) || `chore(release): bump version to ${versionForMessage}`;
261
+ const noVerifyFlag = noVerify ? "--no-verify " : "";
262
+ logger.debug(`No verify: ${noVerify}`);
263
+ if (dryRun) {
264
+ logger.info(`[dry-run] git commit ${noVerifyFlag}-m "${commitMessage}"`);
265
+ } else {
266
+ logger.debug(`Executing: git commit ${noVerifyFlag}-m "${commitMessage}"`);
267
+ await execPromise(`git commit ${noVerifyFlag}-m "${commitMessage}"`, {
268
+ logLevel,
269
+ noStderr: true,
270
+ noStdout: true,
271
+ cwd: internalConfig.cwd
272
+ });
273
+ logger.success(`Committed: ${commitMessage}${noVerify ? " (--no-verify)" : ""}`);
274
+ }
275
+ const signTags = internalConfig.signTags ? "-s" : "";
276
+ logger.debug(`Sign tags: ${internalConfig.signTags}`);
277
+ const createdTags = [];
278
+ if (internalConfig.monorepo?.versionMode === "independent" && bumpedPackages && bumpedPackages.length > 0) {
279
+ logger.debug(`Creating ${bumpedPackages.length} independent package tags`);
280
+ for (const pkg of bumpedPackages) {
281
+ if (!pkg.newVersion) {
282
+ continue;
283
+ }
284
+ const tagName = getIndependentTag({ version: pkg.newVersion, name: pkg.name });
285
+ const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", pkg.newVersion) || tagName;
286
+ if (dryRun) {
287
+ logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
288
+ } else {
289
+ const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
290
+ logger.debug(`Executing: ${cmd}`);
291
+ try {
292
+ await execPromise(cmd, {
293
+ logLevel,
294
+ noStderr: true,
295
+ noStdout: true,
296
+ cwd: internalConfig.cwd
297
+ });
298
+ logger.debug(`Tag created: ${tagName}`);
299
+ } catch (error) {
300
+ logger.error(`Failed to create tag ${tagName}:`, error);
301
+ throw error;
302
+ }
303
+ }
304
+ createdTags.push(tagName);
305
+ }
306
+ logger.success(`Created ${createdTags.length} tags for independent packages, ${createdTags.join(", ")}`);
307
+ } else {
308
+ const tagName = internalConfig.templates.tagBody?.replaceAll("{{newVersion}}", newVersion);
309
+ const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", newVersion) || tagName;
310
+ if (dryRun) {
311
+ logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
312
+ } else {
313
+ const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
314
+ logger.debug(`Executing: ${cmd}`);
315
+ try {
316
+ await execPromise(cmd, {
317
+ logLevel,
318
+ noStderr: true,
319
+ noStdout: true,
320
+ cwd: internalConfig.cwd
321
+ });
322
+ logger.debug(`Tag created: ${tagName}`);
323
+ } catch (error) {
324
+ logger.error(`Failed to create tag ${tagName}:`, error);
325
+ throw error;
326
+ }
327
+ }
328
+ createdTags.push(tagName);
329
+ }
330
+ logger.debug("Created Tags:", createdTags.join(", "));
331
+ logger.success("Commit and tag completed!");
332
+ await executeHook("success:commit-and-tag", internalConfig, dryRun ?? false);
333
+ return createdTags;
334
+ } catch (error) {
335
+ logger.error("Error committing and tagging:", error);
336
+ await executeHook("error:commit-and-tag", internalConfig, dryRun ?? false);
337
+ throw error;
338
+ }
339
+ }
340
+ async function pushCommitAndTags({ dryRun, logLevel, cwd }) {
341
+ logger.start("Start push changes and tags");
342
+ if (dryRun) {
343
+ logger.info("[dry-run] git push --follow-tags");
344
+ } else {
345
+ logger.debug("Executing: git push --follow-tags");
346
+ await execPromise("git push --follow-tags", { noStderr: true, noStdout: true, logLevel, cwd });
347
+ }
348
+ logger.success("Pushing changes and tags completed!");
349
+ }
350
+ function getFirstCommit(cwd) {
351
+ const result = execSync(
352
+ "git rev-list --max-parents=0 HEAD",
353
+ {
354
+ cwd,
355
+ encoding: "utf8"
356
+ }
357
+ );
358
+ return result.trim();
359
+ }
360
+ function getCurrentGitBranch(cwd) {
361
+ const result = execSync("git rev-parse --abbrev-ref HEAD", {
362
+ cwd,
363
+ encoding: "utf8"
364
+ });
365
+ return result.trim();
366
+ }
367
+ function getCurrentGitRef(cwd) {
368
+ const branch = getCurrentGitBranch(cwd);
369
+ return branch || "HEAD";
370
+ }
371
+
372
+ async function generateMarkDown({
373
+ commits,
374
+ config,
375
+ from,
376
+ to,
377
+ isFirstCommit
378
+ }) {
379
+ const typeGroups = groupBy(commits, "type");
380
+ const markdown = [];
381
+ const breakingChanges = [];
382
+ const updatedConfig = {
383
+ ...config,
384
+ from,
385
+ to
386
+ };
387
+ const versionTitle = updatedConfig.to;
388
+ const changelogTitle = `${updatedConfig.from}...${updatedConfig.to}`;
389
+ markdown.push("", `## ${changelogTitle}`, "");
390
+ if (updatedConfig.repo && updatedConfig.from && versionTitle) {
391
+ const formattedCompareLink = formatCompareChanges(versionTitle, {
392
+ ...updatedConfig,
393
+ from: isFirstCommit ? getFirstCommit(updatedConfig.cwd) : updatedConfig.from
394
+ });
395
+ markdown.push(formattedCompareLink);
396
+ }
397
+ for (const type in updatedConfig.types) {
398
+ const group = typeGroups[type];
399
+ if (!group || group.length === 0) {
400
+ continue;
401
+ }
402
+ if (typeof updatedConfig.types[type] === "boolean") {
403
+ continue;
404
+ }
405
+ markdown.push("", `### ${updatedConfig.types[type]?.title}`, "");
406
+ for (const commit of group.reverse()) {
407
+ const line = formatCommit(commit, updatedConfig);
408
+ markdown.push(line);
409
+ if (commit.isBreaking) {
410
+ breakingChanges.push(line);
411
+ }
412
+ }
413
+ }
414
+ if (breakingChanges.length > 0) {
415
+ markdown.push("", "#### \u26A0\uFE0F Breaking Changes", "", ...breakingChanges);
416
+ }
417
+ const _authors = /* @__PURE__ */ new Map();
418
+ for (const commit of commits) {
419
+ if (!commit.author) {
420
+ continue;
421
+ }
422
+ const name = formatName(commit.author.name);
423
+ if (!name || name.includes("[bot]")) {
424
+ continue;
425
+ }
426
+ if (updatedConfig.excludeAuthors && updatedConfig.excludeAuthors.some(
427
+ (v) => name.includes(v) || commit.author.email?.includes(v)
428
+ )) {
429
+ continue;
430
+ }
431
+ if (_authors.has(name)) {
432
+ const entry = _authors.get(name);
433
+ entry?.email.add(commit.author.email);
434
+ } else {
435
+ _authors.set(name, { email: /* @__PURE__ */ new Set([commit.author.email]), name });
436
+ }
437
+ }
438
+ await Promise.all(
439
+ [..._authors.keys()].map(async (authorName) => {
440
+ const meta = _authors.get(authorName);
441
+ if (!meta) {
442
+ return;
443
+ }
444
+ for (const data of [...meta.email, meta.name]) {
445
+ const { user } = await fetch$1(`https://ungh.cc/users/find/${data}`).then((r) => r.json()).catch(() => ({ user: null }));
446
+ if (user) {
447
+ meta.github = user.username;
448
+ break;
449
+ }
450
+ }
451
+ })
452
+ );
453
+ const authors = [..._authors.entries()].map((e) => ({
454
+ name: e[0],
455
+ ...e[1]
456
+ }));
457
+ if (authors.length > 0 && !updatedConfig.noAuthors) {
458
+ markdown.push(
459
+ "",
460
+ "### \u2764\uFE0F Contributors",
461
+ "",
462
+ ...authors.map((i) => {
463
+ const _email = [...i.email].find(
464
+ (e) => !e.includes("noreply.github.com")
465
+ );
466
+ const email = updatedConfig.hideAuthorEmail !== true && _email ? ` <${_email}>` : "";
467
+ const github = i.github ? ` ([@${i.github}](https://github.com/${i.github}))` : "";
468
+ return `- ${i.name}${github || email || ""}`;
469
+ })
470
+ );
471
+ }
472
+ const result = convert(markdown.join("\n").trim(), true);
473
+ return result;
474
+ }
475
+ function getCommitBody(commit) {
476
+ if (!commit.body) {
477
+ return "";
478
+ }
479
+ const lines = commit.body.split("\n");
480
+ const contentLines = lines.filter((line) => {
481
+ const trimmedLine = line.trim();
482
+ if (!trimmedLine) {
483
+ return false;
484
+ }
485
+ const isFileLine = /^[AMDTUXB](?:\d{3})?\s+/.test(trimmedLine) || /^[RCM]\d{3}\s+/.test(trimmedLine);
486
+ return !isFileLine;
487
+ });
488
+ if (contentLines.length === 0) {
489
+ return "";
490
+ }
491
+ const indentedBody = contentLines.map((line) => ` ${line}`).join("\n");
492
+ return `
493
+
494
+ ${indentedBody}
495
+ `;
496
+ }
497
+ function formatCommit(commit, config) {
498
+ const body = config.changelog.includeCommitBody ? getCommitBody(commit) : "";
499
+ return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(commit.description)}${formatReferences(commit.references, config)}${body}`;
500
+ }
501
+ function formatReferences(references, config) {
502
+ const pr = references.filter((ref) => ref.type === "pull-request");
503
+ const issue = references.filter((ref) => ref.type === "issue");
504
+ if (pr.length > 0 || issue.length > 0) {
505
+ return ` (${[...pr, ...issue].map((ref) => formatReference(ref, config.repo)).join(", ")})`;
506
+ }
507
+ if (references.length > 0) {
508
+ return ` (${formatReference(references[0], config.repo)})`;
509
+ }
510
+ return "";
511
+ }
512
+ function formatName(name = "") {
513
+ return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
514
+ }
515
+ function groupBy(items, key) {
516
+ const groups = {};
517
+ for (const item of items) {
518
+ groups[item[key]] = groups[item[key]] || [];
519
+ groups[item[key]]?.push(item);
520
+ }
521
+ return groups;
522
+ }
294
523
 
295
524
  function fromTagIsFirstCommit(fromTag, cwd) {
296
525
  return fromTag === getFirstCommit(cwd);
297
526
  }
298
527
  async function generateChangelog({
299
528
  pkg,
300
- commits,
301
529
  config,
302
- from,
303
- dryRun
530
+ dryRun,
531
+ newVersion
304
532
  }) {
305
- let fromTag = config.from || (config.monorepo?.versionMode === "independent" ? `${pkg.name}@${pkg.currentVersion}` : config.templates.tagBody.replace("{{newVersion}}", pkg.currentVersion)) || from;
533
+ let fromTag = config.from || pkg.fromTag || getFirstCommit(config.cwd);
306
534
  const isFirstCommit = fromTagIsFirstCommit(fromTag, config.cwd);
307
535
  if (isFirstCommit) {
308
- fromTag = config.monorepo?.versionMode === "independent" ? `${pkg.name}@0.0.0` : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
536
+ fromTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: "0.0.0", name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
537
+ }
538
+ newVersion = newVersion || pkg.newVersion;
539
+ let toTag = config.to;
540
+ if (!toTag && newVersion) {
541
+ toTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: newVersion, name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", newVersion);
542
+ }
543
+ if (!toTag) {
544
+ throw new Error(`No tag found for ${pkg.name}`);
309
545
  }
310
- const toTag = config.to || (config.monorepo?.versionMode === "independent" ? `${pkg.name}@${pkg.version}` : config.templates.tagBody.replace("{{newVersion}}", pkg.version));
311
546
  logger.debug(`Generating changelog for ${pkg.name} - from ${fromTag} to ${toTag}`);
312
547
  try {
313
548
  config = {
@@ -316,25 +551,26 @@ async function generateChangelog({
316
551
  to: toTag
317
552
  };
318
553
  const generatedChangelog = await generateMarkDown({
319
- commits,
554
+ commits: pkg.commits,
320
555
  config,
321
556
  from: fromTag,
557
+ isFirstCommit,
322
558
  to: toTag
323
559
  });
324
560
  let changelog = generatedChangelog;
325
- if (commits.length === 0) {
561
+ if (pkg.commits.length === 0) {
326
562
  changelog = `${changelog}
327
563
 
328
564
  ${config.templates.emptyChangelogContent}`;
329
565
  }
330
566
  const changelogResult = await executeHook("generate:changelog", config, dryRun, {
331
- commits,
567
+ commits: pkg.commits,
332
568
  changelog
333
569
  });
334
570
  changelog = changelogResult || changelog;
335
571
  logger.verbose(`Output changelog for ${pkg.name}:
336
572
  ${changelog}`);
337
- logger.debug(`Changelog generated for ${pkg.name} (${commits.length} commits)`);
573
+ logger.debug(`Changelog generated for ${pkg.name} (${pkg.commits.length} commits)`);
338
574
  logger.verbose(`Final changelog for ${pkg.name}:
339
575
 
340
576
  ${changelog}
@@ -349,6 +585,7 @@ ${changelog}
349
585
  }
350
586
  }
351
587
  function writeChangelogToFile({
588
+ cwd,
352
589
  pkg,
353
590
  changelog,
354
591
  dryRun = false
@@ -372,11 +609,12 @@ ${changelog}
372
609
  ${existingChangelog}`;
373
610
  }
374
611
  if (dryRun) {
375
- logger.info(`[dry-run] ${pkg.name} - Write changelog to ${changelogPath}`);
612
+ const relativeChangelogPath = relative(cwd, changelogPath);
613
+ logger.info(`[dry-run] ${pkg.name} - Write changelog to ${relativeChangelogPath}`);
376
614
  } else {
377
615
  logger.debug(`Writing changelog to ${changelogPath}`);
378
616
  writeFileSync(changelogPath, updatedChangelog, "utf8");
379
- logger.info(`Changelog updated for ${pkg.name}`);
617
+ logger.info(`Changelog updated for ${pkg.name} (${"newVersion" in pkg && pkg.newVersion || pkg.version})`);
380
618
  }
381
619
  }
382
620
 
@@ -398,8 +636,8 @@ function getDefaultConfig() {
398
636
  ci: { title: "\u{1F916} CI" }
399
637
  },
400
638
  templates: {
401
- commitMessage: "chore(release): bump version to v{{newVersion}}",
402
- tagMessage: "Bump version to v{{newVersion}}",
639
+ commitMessage: "chore(release): bump version to {{newVersion}}",
640
+ tagMessage: "Bump version to {{newVersion}}",
403
641
  tagBody: "v{{newVersion}}",
404
642
  emptyChangelogContent: "No relevant changes for this release"
405
643
  },
@@ -490,7 +728,11 @@ function defineConfig(config) {
490
728
  return config;
491
729
  }
492
730
 
493
- function getPackageDependencies(packagePath, allPackageNames, dependencyTypes) {
731
+ function getPackageDependencies({
732
+ packagePath,
733
+ allPackageNames,
734
+ dependencyTypes
735
+ }) {
494
736
  const packageJsonPath = join(packagePath, "package.json");
495
737
  if (!existsSync(packageJsonPath)) {
496
738
  return [];
@@ -509,32 +751,26 @@ function getPackageDependencies(packagePath, allPackageNames, dependencyTypes) {
509
751
  }
510
752
  return deps;
511
753
  }
512
- function getPackagesWithDependencies(packages, dependencyTypes) {
513
- const allPackageNames = new Set(packages.map((p) => p.name));
514
- return packages.map((pkg) => ({
515
- ...pkg,
516
- dependencies: getPackageDependencies(pkg.path, allPackageNames, dependencyTypes)
517
- }));
518
- }
519
- function getDependentsOf(packageName, allPackages) {
754
+ function getDependentsOf({
755
+ allPackages,
756
+ packageName
757
+ }) {
520
758
  return allPackages.filter(
521
759
  (pkg) => pkg.dependencies.includes(packageName)
522
760
  );
523
761
  }
524
762
  function expandPackagesToBumpWithDependents({
525
- packagesWithCommits,
526
763
  allPackages,
527
- dependencyTypes
764
+ packagesWithCommits
528
765
  }) {
529
- const packagesWithDeps = getPackagesWithDependencies(allPackages, dependencyTypes);
530
766
  const result = /* @__PURE__ */ new Map();
531
767
  logger.debug(`Expanding packages to bump: ${packagesWithCommits.length} packages with commits, ${allPackages.length} total packages`);
532
768
  for (const pkg of packagesWithCommits) {
533
- result.set(pkg.name, {
769
+ const packageToBump = {
534
770
  ...pkg,
535
- reason: "commits",
536
- commits: pkg.commits
537
- });
771
+ reason: "commits"
772
+ };
773
+ result.set(pkg.name, packageToBump);
538
774
  }
539
775
  const toProcess = [...packagesWithCommits.map((p) => p.name)];
540
776
  const processed = /* @__PURE__ */ new Set();
@@ -544,19 +780,22 @@ function expandPackagesToBumpWithDependents({
544
780
  continue;
545
781
  }
546
782
  processed.add(currentPkgName);
547
- const dependents = getDependentsOf(currentPkgName, packagesWithDeps);
783
+ const dependents = getDependentsOf({
784
+ packageName: currentPkgName,
785
+ allPackages
786
+ });
548
787
  for (const dependent of dependents) {
549
788
  if (!result.has(dependent.name)) {
550
789
  const currentChain = result.get(currentPkgName)?.dependencyChain || [];
551
790
  const chain = [...currentChain, currentPkgName];
552
- const packageInfo = allPackages.find((p) => p.name === dependent.name);
553
- if (packageInfo) {
554
- result.set(dependent.name, {
555
- ...packageInfo,
791
+ const packageBase = allPackages.find((p) => p.name === dependent.name);
792
+ if (packageBase) {
793
+ const packageToBump = {
794
+ ...packageBase,
556
795
  reason: "dependency",
557
- dependencyChain: chain,
558
- commits: []
559
- });
796
+ dependencyChain: chain
797
+ };
798
+ result.set(dependent.name, packageToBump);
560
799
  toProcess.push(dependent.name);
561
800
  logger.debug(`${dependent.name} will be bumped (depends on ${chain.join(" \u2192 ")})`);
562
801
  }
@@ -577,7 +816,7 @@ function topologicalSort(packages) {
577
816
  if (visited.has(pkgName))
578
817
  return;
579
818
  if (visiting.has(pkgName)) {
580
- logger.warn(`Circular dependency detected involving ${pkgName}`);
819
+ logger.fail(`Circular dependency detected involving ${pkgName}`);
581
820
  return;
582
821
  }
583
822
  visiting.add(pkgName);
@@ -597,229 +836,12 @@ function topologicalSort(packages) {
597
836
  return sorted;
598
837
  }
599
838
 
600
- function getGitStatus(cwd) {
601
- return execSync("git status --porcelain", {
602
- cwd,
603
- encoding: "utf8"
604
- }).trim();
605
- }
606
- function checkGitStatusIfDirty() {
607
- logger.debug("Checking git status");
608
- const dirty = getGitStatus();
609
- if (dirty) {
610
- logger.debug("git status:", `
611
- ${dirty.trim().split("\n").map((line) => line.trim()).join("\n")}`);
612
- const error = `Git status is dirty!
613
-
614
- Please commit or stash your changes before bumping or use --no-clean flag.
615
-
616
- Unstaged files:
617
-
618
- ${dirty.trim()}`;
619
- throw new Error(error);
620
- }
621
- }
622
- async function fetchGitTags(cwd) {
623
- logger.debug("Fetching git tags from remote");
624
- try {
625
- await execPromise("git fetch --tags", { cwd, noStderr: true, noStdout: true, noSuccess: true });
626
- logger.debug("Git tags fetched successfully");
627
- } catch (error) {
628
- logger.warn("Failed to fetch some git tags from remote (tags might already exist locally)", error);
629
- logger.info("Continuing with local tags");
630
- }
631
- }
632
- function detectGitProvider(cwd = process.cwd()) {
633
- try {
634
- const remoteUrl = execSync("git remote get-url origin", {
635
- cwd,
636
- encoding: "utf8"
637
- }).trim();
638
- if (remoteUrl.includes("github.com")) {
639
- return "github";
640
- }
641
- if (remoteUrl.includes("gitlab.com") || remoteUrl.includes("gitlab")) {
642
- return "gitlab";
643
- }
644
- return null;
645
- } catch {
646
- return null;
647
- }
648
- }
649
- function parseGitRemoteUrl(remoteUrl) {
650
- const sshRegex = /git@[\w.-]+:([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
651
- const httpsRegex = /https?:\/\/[\w.-]+\/([\w.-]+)\/([\w.-]+?)(?:\.git)?$/;
652
- const sshMatch = remoteUrl.match(sshRegex);
653
- if (sshMatch) {
654
- return {
655
- owner: sshMatch[1],
656
- repo: sshMatch[2]
657
- };
658
- }
659
- const httpsMatch = remoteUrl.match(httpsRegex);
660
- if (httpsMatch) {
661
- return {
662
- owner: httpsMatch[1],
663
- repo: httpsMatch[2]
664
- };
665
- }
666
- return null;
667
- }
668
- async function createCommitAndTags({
669
- config,
670
- noVerify,
671
- bumpedPackages,
672
- newVersion,
673
- dryRun,
674
- logLevel
675
- } = {}) {
676
- const internalConfig = config || await loadRelizyConfig();
677
- try {
678
- await executeHook("before:commit-and-tag", internalConfig, dryRun ?? false);
679
- const filePatternsToAdd = [
680
- "package.json",
681
- "lerna.json",
682
- "CHANGELOG.md",
683
- "**/CHANGELOG.md",
684
- "**/package.json"
685
- ];
686
- logger.start("Start commit and tag");
687
- logger.debug("Adding files to git staging area...");
688
- for (const pattern of filePatternsToAdd) {
689
- if (pattern === "lerna.json" && !hasLernaJson(internalConfig.cwd)) {
690
- logger.verbose(`Skipping lerna.json as it doesn't exist`);
691
- continue;
692
- }
693
- if ((pattern === "lerna.json" || pattern === "CHANGELOG.md") && !existsSync(join(internalConfig.cwd, pattern))) {
694
- logger.verbose(`Skipping ${pattern} as it doesn't exist`);
695
- continue;
696
- }
697
- if (dryRun) {
698
- logger.info(`[dry-run] git add ${pattern}`);
699
- continue;
700
- }
701
- try {
702
- logger.debug(`git add ${pattern}`);
703
- execSync(`git add ${pattern}`);
704
- } catch {
705
- }
706
- }
707
- const rootPackage = getRootPackage(internalConfig.cwd);
708
- newVersion = newVersion || rootPackage.version;
709
- const versionForMessage = internalConfig.monorepo?.versionMode === "independent" ? bumpedPackages?.map((pkg) => `${pkg.name}@${pkg.version}`).join(", ") || "unknown" : newVersion || "unknown";
710
- const commitMessage = internalConfig.templates.commitMessage?.replaceAll("{{newVersion}}", versionForMessage) || `chore(release): bump version to ${versionForMessage}`;
711
- const noVerifyFlag = noVerify ? "--no-verify " : "";
712
- logger.debug(`No verify: ${noVerify}`);
713
- if (dryRun) {
714
- logger.info(`[dry-run] git commit ${noVerifyFlag}-m "${commitMessage}"`);
715
- } else {
716
- logger.debug(`Executing: git commit ${noVerifyFlag}-m "${commitMessage}"`);
717
- await execPromise(`git commit ${noVerifyFlag}-m "${commitMessage}"`, {
718
- logLevel,
719
- noStderr: true,
720
- noStdout: true,
721
- cwd: internalConfig.cwd
722
- });
723
- logger.success(`Committed: ${commitMessage}${noVerify ? " (--no-verify)" : ""}`);
724
- }
725
- const signTags = internalConfig.signTags ? "-s" : "";
726
- logger.debug(`Sign tags: ${internalConfig.signTags}`);
727
- const createdTags = [];
728
- if (internalConfig.monorepo?.versionMode === "independent" && bumpedPackages && bumpedPackages.length > 0) {
729
- logger.debug(`Creating ${bumpedPackages.length} independent package tags`);
730
- for (const pkg of bumpedPackages) {
731
- const tagName = `${pkg.name}@${pkg.version}`;
732
- const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", pkg.version || "") || tagName;
733
- if (dryRun) {
734
- logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
735
- } else {
736
- const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
737
- logger.debug(`Executing: ${cmd}`);
738
- try {
739
- await execPromise(cmd, {
740
- logLevel,
741
- noStderr: true,
742
- noStdout: true,
743
- cwd: internalConfig.cwd
744
- });
745
- logger.debug(`Tag created: ${tagName}`);
746
- } catch (error) {
747
- logger.error(`Failed to create tag ${tagName}:`, error);
748
- throw error;
749
- }
750
- }
751
- createdTags.push(tagName);
752
- }
753
- logger.success(`Created ${createdTags.length} tags for independent packages, ${createdTags.join(", ")}`);
754
- } else {
755
- const tagName = internalConfig.templates.tagBody?.replaceAll("{{newVersion}}", newVersion);
756
- const tagMessage = internalConfig.templates?.tagMessage?.replaceAll("{{newVersion}}", newVersion) || tagName;
757
- if (dryRun) {
758
- logger.info(`[dry-run] git tag ${signTags} -a ${tagName} -m "${tagMessage}"`);
759
- } else {
760
- const cmd = `git tag ${signTags} -a ${tagName} -m "${tagMessage}"`;
761
- logger.debug(`Executing: ${cmd}`);
762
- try {
763
- await execPromise(cmd, {
764
- logLevel,
765
- noStderr: true,
766
- noStdout: true,
767
- cwd: internalConfig.cwd
768
- });
769
- logger.debug(`Tag created: ${tagName}`);
770
- } catch (error) {
771
- logger.error(`Failed to create tag ${tagName}:`, error);
772
- throw error;
773
- }
774
- }
775
- createdTags.push(tagName);
776
- }
777
- logger.debug("Created Tags:", createdTags.join(", "));
778
- logger.success("Commit and tag completed!");
779
- await executeHook("success:commit-and-tag", internalConfig, dryRun ?? false);
780
- return createdTags;
781
- } catch (error) {
782
- logger.error("Error committing and tagging:", error);
783
- await executeHook("error:commit-and-tag", internalConfig, dryRun ?? false);
784
- throw error;
785
- }
786
- }
787
- async function pushCommitAndTags({ dryRun, logLevel, cwd }) {
788
- logger.start("Start push changes and tags");
789
- if (dryRun) {
790
- logger.info("[dry-run] git push --follow-tags");
791
- } else {
792
- logger.debug("Executing: git push --follow-tags");
793
- await execPromise("git push --follow-tags", { noStderr: true, noStdout: true, logLevel, cwd });
794
- }
795
- logger.success("Pushing changes and tags completed!");
796
- }
797
- function getFirstCommit(cwd) {
798
- const result = execSync(
799
- "git rev-list --max-parents=0 HEAD",
800
- {
801
- cwd,
802
- encoding: "utf8"
803
- }
804
- );
805
- return result.trim();
806
- }
807
- function getCurrentGitBranch(cwd) {
808
- const result = execSync("git rev-parse --abbrev-ref HEAD", {
809
- cwd,
810
- encoding: "utf8"
811
- });
812
- return result.trim();
813
- }
814
- function getCurrentGitRef(cwd) {
815
- const branch = getCurrentGitBranch(cwd);
816
- return branch || "HEAD";
817
- }
818
-
819
839
  async function githubIndependentMode({
820
840
  config,
821
841
  dryRun,
822
- bumpedPackages
842
+ bumpResult,
843
+ force,
844
+ suffix
823
845
  }) {
824
846
  const repoConfig = config.repo;
825
847
  if (!repoConfig) {
@@ -829,42 +851,36 @@ async function githubIndependentMode({
829
851
  if (!config.tokens.github && !config.repo?.token) {
830
852
  throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
831
853
  }
832
- const packages = bumpedPackages || getPackages({
833
- cwd: config.cwd,
854
+ const packages = bumpResult?.bumped && bumpResult?.bumpedPackages || await getPackages({
855
+ suffix,
834
856
  patterns: config.monorepo?.packages,
835
- ignorePackageNames: config.monorepo?.ignorePackageNames
857
+ config,
858
+ force
836
859
  });
837
860
  logger.info(`Creating ${packages.length} GitHub release(s)`);
838
861
  const postedReleases = [];
839
862
  for (const pkg of packages) {
840
- const to = `${pkg.name}@${pkg.version}`;
841
- const from = pkg.fromTag;
863
+ const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
864
+ const from = config.from || pkg.fromTag;
865
+ const to = config.to || getIndependentTag({ version: newVersion, name: pkg.name });
842
866
  if (!from) {
843
- logger.warn(`No fromTag found for ${pkg.name}, skipping release`);
867
+ logger.warn(`No from tag found for ${pkg.name}, skipping release`);
844
868
  continue;
845
869
  }
846
870
  const toTag = dryRun ? "HEAD" : to;
847
871
  logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${toTag}`);
848
- const commits = await getPackageCommits({
849
- pkg,
850
- to: toTag,
851
- from,
852
- config,
853
- changelog: true
854
- });
855
872
  const changelog = await generateChangelog({
856
873
  pkg,
857
- commits,
858
874
  config,
859
- from,
860
- dryRun
875
+ dryRun,
876
+ newVersion
861
877
  });
862
878
  const releaseBody = changelog.split("\n").slice(2).join("\n");
863
879
  const release = {
864
880
  tag_name: to,
865
881
  name: to,
866
882
  body: releaseBody,
867
- prerelease: isPrerelease(pkg.version)
883
+ prerelease: isPrerelease(newVersion)
868
884
  };
869
885
  logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
870
886
  if (dryRun) {
@@ -872,7 +888,7 @@ async function githubIndependentMode({
872
888
  postedReleases.push({
873
889
  name: pkg.name,
874
890
  tag: release.tag_name,
875
- version: pkg.version,
891
+ version: newVersion,
876
892
  prerelease: release.prerelease
877
893
  });
878
894
  } else {
@@ -886,7 +902,7 @@ async function githubIndependentMode({
886
902
  postedReleases.push({
887
903
  name: pkg.name,
888
904
  tag: release.tag_name,
889
- version: pkg.version,
905
+ version: newVersion,
890
906
  prerelease: release.prerelease
891
907
  });
892
908
  }
@@ -912,21 +928,12 @@ async function githubUnified({
912
928
  if (!config.tokens.github && !config.repo?.token) {
913
929
  throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
914
930
  }
915
- const to = config.templates.tagBody.replace("{{newVersion}}", bumpResult?.newVersion || rootPackage.version);
916
- const toTag = dryRun ? getCurrentGitRef(config.cwd) : to;
917
- const commits = await getPackageCommits({
918
- pkg: rootPackage,
919
- config,
920
- from: bumpResult?.fromTag || getFirstCommit(config.cwd),
921
- to: toTag,
922
- changelog: true
923
- });
931
+ const to = config.to || config.templates.tagBody.replace("{{newVersion}}", bumpResult?.bumped && bumpResult.newVersion || rootPackage.version);
924
932
  const changelog = await generateChangelog({
925
933
  pkg: rootPackage,
926
- commits,
927
934
  config,
928
- from: bumpResult?.fromTag || "v0.0.0",
929
- dryRun
935
+ dryRun,
936
+ newVersion: bumpResult?.newVersion || rootPackage.version
930
937
  });
931
938
  const releaseBody = changelog.split("\n").slice(2).join("\n");
932
939
  const release = {
@@ -947,7 +954,7 @@ async function githubUnified({
947
954
  logger.debug("Publishing release to GitHub...");
948
955
  await createGithubRelease({
949
956
  ...config,
950
- from: bumpResult?.fromTag || "v0.0.0",
957
+ from: bumpResult?.bumped && bumpResult.fromTag || "v0.0.0",
951
958
  to,
952
959
  repo: repoConfig
953
960
  }, release);
@@ -960,7 +967,7 @@ async function githubUnified({
960
967
  prerelease: release.prerelease
961
968
  }];
962
969
  }
963
- async function github(options = {}) {
970
+ async function github(options) {
964
971
  try {
965
972
  const dryRun = options.dryRun ?? false;
966
973
  logger.debug(`Dry run: ${dryRun}`);
@@ -976,18 +983,30 @@ async function github(options = {}) {
976
983
  }
977
984
  }
978
985
  });
979
- if (!options.bumpResult?.bumped) {
980
- logger.warn("No bump result found, skipping release");
981
- return [];
982
- }
983
986
  if (config.monorepo?.versionMode === "independent") {
984
- return await githubIndependentMode({ config, dryRun, bumpedPackages: options.bumpResult.bumpedPackages });
985
- }
986
- let rootPackage = getRootPackage(config.cwd);
987
- const foundedRootPackage = options.bumpResult.bumpedPackages.find((pkg) => pkg.path === rootPackage.path);
988
- if (foundedRootPackage) {
989
- rootPackage = foundedRootPackage;
987
+ return await githubIndependentMode({
988
+ config,
989
+ dryRun,
990
+ bumpResult: options.bumpResult,
991
+ force: options.force ?? false,
992
+ suffix: options.suffix
993
+ });
990
994
  }
995
+ const rootPackageBase = readPackageJson(config.cwd);
996
+ const { from, to } = await resolveTags({
997
+ config,
998
+ step: "provider-release",
999
+ newVersion: options.bumpResult?.newVersion || rootPackageBase.version,
1000
+ pkg: rootPackageBase
1001
+ });
1002
+ const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
1003
+ config,
1004
+ force: options.force ?? false,
1005
+ suffix: options.suffix,
1006
+ changelog: true,
1007
+ from,
1008
+ to
1009
+ });
991
1010
  return await githubUnified({
992
1011
  config,
993
1012
  dryRun,
@@ -1064,13 +1083,16 @@ async function createGitlabRelease({
1064
1083
  async function gitlabIndependentMode({
1065
1084
  config,
1066
1085
  dryRun,
1067
- bumpedPackages
1086
+ bumpResult,
1087
+ suffix,
1088
+ force
1068
1089
  }) {
1069
1090
  logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
1070
- const packages = bumpedPackages || getPackages({
1071
- cwd: config.cwd,
1091
+ const packages = bumpResult?.bumped && bumpResult?.bumpedPackages || await getPackages({
1072
1092
  patterns: config.monorepo?.packages,
1073
- ignorePackageNames: config.monorepo?.ignorePackageNames
1093
+ config,
1094
+ suffix,
1095
+ force
1074
1096
  });
1075
1097
  logger.info(`Creating ${packages.length} GitLab release(s) for independent packages`);
1076
1098
  logger.debug("Getting current branch...");
@@ -1082,26 +1104,19 @@ async function gitlabIndependentMode({
1082
1104
  });
1083
1105
  const postedReleases = [];
1084
1106
  for (const pkg of packages) {
1085
- const to = `${pkg.name}@${pkg.version}`;
1086
- const from = pkg.fromTag;
1107
+ const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
1108
+ const from = config.from || pkg.fromTag;
1109
+ const to = getIndependentTag({ version: newVersion, name: pkg.name });
1087
1110
  if (!from) {
1088
- logger.warn(`No fromTag found for ${pkg.name}, skipping release`);
1111
+ logger.warn(`No from tag found for ${pkg.name}, skipping release`);
1089
1112
  continue;
1090
1113
  }
1091
1114
  logger.debug(`Processing ${pkg.name}: ${from} \u2192 ${to}`);
1092
- const commits = await getPackageCommits({
1093
- pkg,
1094
- config,
1095
- from,
1096
- to,
1097
- changelog: true
1098
- });
1099
1115
  const changelog = await generateChangelog({
1100
1116
  pkg,
1101
- commits,
1102
1117
  config,
1103
- from,
1104
- dryRun
1118
+ dryRun,
1119
+ newVersion
1105
1120
  });
1106
1121
  if (!changelog) {
1107
1122
  logger.warn(`No changelog found for ${pkg.name}`);
@@ -1114,335 +1129,186 @@ async function gitlabIndependentMode({
1114
1129
  description: releaseBody,
1115
1130
  ref: currentBranch.trim()
1116
1131
  };
1117
- logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
1118
- if (dryRun) {
1119
- logger.info(`[dry-run] Publish GitLab release for ${to}`);
1120
- } else {
1121
- logger.debug(`Publishing release ${to} to GitLab...`);
1122
- await createGitlabRelease({
1123
- config,
1124
- release,
1125
- dryRun
1126
- });
1127
- postedReleases.push({
1128
- name: pkg.name,
1129
- tag: release.tag_name,
1130
- version: pkg.version,
1131
- prerelease: isPrerelease(pkg.version)
1132
- });
1133
- }
1134
- }
1135
- if (postedReleases.length === 0) {
1136
- logger.warn("No releases created");
1137
- } else {
1138
- logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitLab!`);
1139
- }
1140
- return postedReleases;
1141
- }
1142
- async function gitlabUnified({
1143
- config,
1144
- dryRun,
1145
- rootPackage,
1146
- fromTag,
1147
- oldVersion
1148
- }) {
1149
- logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
1150
- const to = config.templates.tagBody.replace("{{newVersion}}", rootPackage.version);
1151
- const commits = await getPackageCommits({
1152
- pkg: rootPackage,
1153
- config,
1154
- from: fromTag || getFirstCommit(config.cwd),
1155
- to,
1156
- changelog: true
1157
- });
1158
- logger.debug(`Found ${commits.length} commit(s)`);
1159
- const changelog = await generateChangelog({
1160
- pkg: rootPackage,
1161
- commits,
1162
- config,
1163
- from: fromTag || oldVersion || "v0.0.0",
1164
- dryRun
1165
- });
1166
- const releaseBody = changelog.split("\n").slice(2).join("\n");
1167
- logger.debug("Getting current branch...");
1168
- const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
1169
- noSuccess: true,
1170
- noStdout: true,
1171
- logLevel: config.logLevel,
1172
- cwd: config.cwd
1173
- });
1174
- const release = {
1175
- tag_name: to,
1176
- name: to,
1177
- description: releaseBody,
1178
- ref: currentBranch.trim()
1179
- };
1180
- logger.info(`Creating release for ${to} (ref: ${release.ref})`);
1181
- logger.debug("Release details:", formatJson({
1182
- tag_name: release.tag_name,
1183
- name: release.name,
1184
- ref: release.ref
1185
- }));
1186
- if (dryRun) {
1187
- logger.info("[dry-run] Publish GitLab release for", release.tag_name);
1188
- } else {
1189
- logger.debug("Publishing release to GitLab...");
1190
- await createGitlabRelease({
1191
- config,
1192
- release,
1193
- dryRun
1194
- });
1195
- }
1196
- logger.success(`Release ${to} published to GitLab!`);
1197
- return [{
1198
- name: to,
1199
- tag: to,
1200
- version: to,
1201
- prerelease: isPrerelease(rootPackage.version)
1202
- }];
1203
- }
1204
- async function gitlab(options = {}) {
1205
- try {
1206
- const dryRun = options.dryRun ?? false;
1207
- logger.debug(`Dry run: ${dryRun}`);
1208
- const config = await loadRelizyConfig({
1209
- configName: options.configName,
1210
- baseConfig: options.config,
1211
- overrides: {
1212
- from: options.from,
1213
- to: options.to,
1214
- logLevel: options.logLevel,
1215
- tokens: {
1216
- gitlab: options.token
1217
- }
1218
- }
1219
- });
1220
- if (!options.bumpResult?.bumped) {
1221
- logger.warn("No bump result found, skipping release");
1222
- return [];
1223
- }
1224
- if (config.monorepo?.versionMode === "independent") {
1225
- return await gitlabIndependentMode({
1226
- config,
1227
- dryRun,
1228
- bumpedPackages: options.bumpResult.bumpedPackages
1229
- });
1230
- }
1231
- const rootPackage = getRootPackage(process.cwd());
1232
- logger.debug(`Root package: ${rootPackage.name}@${rootPackage.version}`);
1233
- return await gitlabUnified({
1234
- config,
1235
- dryRun,
1236
- rootPackage,
1237
- fromTag: options.bumpResult.fromTag,
1238
- oldVersion: options.bumpResult.oldVersion
1239
- });
1240
- } catch (error) {
1241
- logger.error("Error publishing GitLab release:", error);
1242
- throw error;
1243
- }
1244
- }
1245
-
1246
- function getPackageInfo(packagePath, ignorePackageNames) {
1247
- const packageJsonPath = join(packagePath, "package.json");
1248
- if (!existsSync(packageJsonPath))
1249
- return null;
1250
- if (!statSync(packagePath).isDirectory())
1251
- return null;
1252
- try {
1253
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1254
- if (packageJson.private) {
1255
- logger.debug(`${packageJson.name} is private and will be ignored`);
1256
- return null;
1257
- }
1258
- if (ignorePackageNames?.includes(packageJson.name)) {
1259
- logger.debug(`${packageJson.name} ignored by config monorepo.ignorePackageNames`);
1260
- return null;
1261
- }
1262
- if (!packageJson.version) {
1263
- logger.warn(`${packageJson.name} has no version and will be ignored`);
1264
- return null;
1265
- }
1266
- return {
1267
- name: packageJson.name,
1268
- currentVersion: packageJson.version,
1269
- path: packagePath,
1270
- version: packageJson.version
1271
- };
1272
- } catch (error) {
1273
- const errorMessage = error instanceof Error ? error.message : String(error);
1274
- logger.warn(`Unable to read ${packageJsonPath}:`, errorMessage);
1275
- return null;
1276
- }
1277
- }
1278
- function getPackages({
1279
- cwd,
1280
- patterns,
1281
- ignorePackageNames
1282
- }) {
1283
- const packages = [];
1284
- const foundPaths = /* @__PURE__ */ new Set();
1285
- if (!patterns)
1286
- patterns = ["."];
1287
- logger.debug(`Getting packages from patterns: ${patterns.join(", ")}`);
1288
- for (const pattern of patterns) {
1289
- try {
1290
- const matches = fastGlob.sync(pattern, {
1291
- cwd,
1292
- onlyDirectories: true,
1293
- absolute: true,
1294
- ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
1295
- });
1296
- for (const matchPath of matches) {
1297
- if (foundPaths.has(matchPath))
1298
- continue;
1299
- const packageInfo = getPackageInfo(matchPath, ignorePackageNames);
1300
- if (packageInfo) {
1301
- foundPaths.add(matchPath);
1302
- packages.push(packageInfo);
1303
- }
1304
- }
1305
- } catch (error) {
1306
- logger.error(`Unable to match pattern "${pattern}":`, error);
1307
- }
1308
- }
1309
- return packages;
1310
- }
1311
- function isAllowedCommit({
1312
- commit,
1313
- type,
1314
- changelog
1315
- }) {
1316
- if (commit.type === "chore" && ["deps", "release"].includes(commit.scope) && !commit.isBreaking) {
1317
- return false;
1318
- }
1319
- if (typeof type === "object") {
1320
- return !!type.semver || changelog && !!type.title;
1321
- }
1322
- if (typeof type === "boolean") {
1323
- return type;
1324
- }
1325
- return false;
1326
- }
1327
- async function getPackageCommits({
1328
- pkg,
1329
- from,
1330
- to,
1331
- config,
1332
- changelog
1333
- }) {
1334
- logger.debug(`Analyzing commits for ${pkg.name} since ${from} to ${to}`);
1335
- const changelogConfig = {
1336
- ...config,
1337
- from,
1338
- to
1339
- };
1340
- const rawCommits = await getGitDiff(from, to, changelogConfig.cwd);
1341
- const allCommits = parseCommits(rawCommits, changelogConfig);
1342
- const hasBreakingChanges = allCommits.some((commit) => commit.isBreaking);
1343
- logger.debug(`Has breaking changes: ${hasBreakingChanges}`);
1344
- const rootPackage = getRootPackage(changelogConfig.cwd);
1345
- const commits = allCommits.filter((commit) => {
1346
- const type = changelogConfig?.types[commit.type];
1347
- if (!isAllowedCommit({ commit, type, changelog })) {
1348
- return false;
1349
- }
1350
- if (pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) {
1351
- return true;
1352
- }
1353
- const packageRelativePath = pkg.path.replace(`${changelogConfig.cwd}/`, "");
1354
- const scopeMatches = commit.scope === pkg.name;
1355
- const bodyContainsPath = commit.body.includes(packageRelativePath);
1356
- return scopeMatches || bodyContainsPath;
1357
- });
1358
- logger.debug(`Found ${commits.length} commit(s) for ${pkg.name} from ${from} to ${to}`);
1359
- if (commits.length > 0) {
1360
- logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
1361
- } else {
1362
- logger.debug(`${pkg.name}: No commits found`);
1363
- }
1364
- return commits;
1365
- }
1366
- function getRootPackage(rootDir) {
1367
- const packageJsonPath = join(rootDir, "package.json");
1368
- if (!existsSync(packageJsonPath)) {
1369
- throw new Error(`package.json not found at ${packageJsonPath}`);
1370
- }
1371
- try {
1372
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
1373
- return {
1374
- name: packageJson.name,
1375
- currentVersion: packageJson.version || "0.0.0",
1376
- path: rootDir,
1377
- version: packageJson.version || "0.0.0"
1378
- };
1379
- } catch (error) {
1380
- throw new Error(`Unable to read ${packageJsonPath}: ${error}`);
1132
+ logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
1133
+ if (dryRun) {
1134
+ logger.info(`[dry-run] Publish GitLab release for ${to}`);
1135
+ } else {
1136
+ logger.debug(`Publishing release ${to} to GitLab...`);
1137
+ await createGitlabRelease({
1138
+ config,
1139
+ release,
1140
+ dryRun
1141
+ });
1142
+ postedReleases.push({
1143
+ name: pkg.name,
1144
+ tag: release.tag_name,
1145
+ version: newVersion,
1146
+ prerelease: isPrerelease(newVersion)
1147
+ });
1148
+ }
1381
1149
  }
1150
+ if (postedReleases.length === 0) {
1151
+ logger.warn("No releases created");
1152
+ } else {
1153
+ logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitLab!`);
1154
+ }
1155
+ return postedReleases;
1382
1156
  }
1383
- function hasLernaJson(rootDir) {
1384
- const lernaJsonPath = join(rootDir, "lerna.json");
1385
- return existsSync(lernaJsonPath);
1386
- }
1387
- async function getPackageToBump({
1388
- packages,
1157
+ async function gitlabUnified({
1389
1158
  config,
1390
- from,
1391
- to
1159
+ dryRun,
1160
+ rootPackage,
1161
+ bumpResult
1392
1162
  }) {
1393
- const packagesWithCommits = [];
1394
- for (const pkg of packages) {
1395
- const commits = await getPackageCommits({
1396
- pkg,
1397
- from,
1398
- to,
1163
+ logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
1164
+ const to = config.templates.tagBody.replace("{{newVersion}}", rootPackage.newVersion || rootPackage.version);
1165
+ const changelog = await generateChangelog({
1166
+ pkg: rootPackage,
1167
+ config,
1168
+ dryRun,
1169
+ newVersion: bumpResult?.newVersion || rootPackage.version
1170
+ });
1171
+ const releaseBody = changelog.split("\n").slice(2).join("\n");
1172
+ logger.debug("Getting current branch...");
1173
+ const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
1174
+ noSuccess: true,
1175
+ noStdout: true,
1176
+ logLevel: config.logLevel,
1177
+ cwd: config.cwd
1178
+ });
1179
+ const release = {
1180
+ tag_name: to,
1181
+ name: to,
1182
+ description: releaseBody,
1183
+ ref: currentBranch.trim()
1184
+ };
1185
+ logger.info(`Creating release for ${to} (ref: ${release.ref})`);
1186
+ logger.debug("Release details:", formatJson({
1187
+ tag_name: release.tag_name,
1188
+ name: release.name,
1189
+ ref: release.ref
1190
+ }));
1191
+ if (dryRun) {
1192
+ logger.info("[dry-run] Publish GitLab release for", release.tag_name);
1193
+ } else {
1194
+ logger.debug("Publishing release to GitLab...");
1195
+ await createGitlabRelease({
1399
1196
  config,
1400
- changelog: false
1197
+ release,
1198
+ dryRun
1199
+ });
1200
+ }
1201
+ logger.success(`Release ${to} published to GitLab!`);
1202
+ return [{
1203
+ name: to,
1204
+ tag: to,
1205
+ version: to,
1206
+ prerelease: isPrerelease(rootPackage.version)
1207
+ }];
1208
+ }
1209
+ async function gitlab(options = {}) {
1210
+ try {
1211
+ const dryRun = options.dryRun ?? false;
1212
+ logger.debug(`Dry run: ${dryRun}`);
1213
+ const config = await loadRelizyConfig({
1214
+ configName: options.configName,
1215
+ baseConfig: options.config,
1216
+ overrides: {
1217
+ from: options.from,
1218
+ to: options.to,
1219
+ logLevel: options.logLevel,
1220
+ tokens: {
1221
+ gitlab: options.token
1222
+ }
1223
+ }
1401
1224
  });
1402
- const graduating = isGraduating(pkg.version, config.bump.type) || isChangedPreid(pkg.version, config.bump.preid);
1403
- if (commits.length > 0 || graduating) {
1404
- packagesWithCommits.push({ ...pkg, commits });
1225
+ if (config.monorepo?.versionMode === "independent") {
1226
+ return await gitlabIndependentMode({
1227
+ config,
1228
+ dryRun,
1229
+ bumpResult: options.bumpResult,
1230
+ suffix: options.suffix,
1231
+ force: options.force ?? false
1232
+ });
1405
1233
  }
1234
+ const rootPackageBase = readPackageJson(config.cwd);
1235
+ const { from, to } = await resolveTags({
1236
+ config,
1237
+ step: "provider-release",
1238
+ newVersion: options.bumpResult?.newVersion || rootPackageBase.version,
1239
+ pkg: rootPackageBase
1240
+ });
1241
+ const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
1242
+ config,
1243
+ force: options.force ?? false,
1244
+ suffix: options.suffix,
1245
+ changelog: true,
1246
+ from,
1247
+ to
1248
+ });
1249
+ logger.debug(`Root package: ${getIndependentTag(rootPackage)}`);
1250
+ return await gitlabUnified({
1251
+ config,
1252
+ dryRun,
1253
+ rootPackage,
1254
+ bumpResult: options.bumpResult
1255
+ });
1256
+ } catch (error) {
1257
+ logger.error("Error publishing GitLab release:", error);
1258
+ throw error;
1406
1259
  }
1407
- const allPackagesToBump = expandPackagesToBumpWithDependents({
1408
- allPackages: packages,
1409
- packagesWithCommits,
1410
- dependencyTypes: config.bump?.dependencyTypes
1411
- });
1412
- return allPackagesToBump;
1413
1260
  }
1414
1261
 
1415
- function detectReleaseTypeFromCommits(commits, config) {
1416
- return determineSemverChange(commits, config);
1262
+ function determineSemverChange(commits, types) {
1263
+ let [hasMajor, hasMinor, hasPatch] = [false, false, false];
1264
+ for (const commit of commits) {
1265
+ const commitType = types[commit.type];
1266
+ if (!commitType) {
1267
+ continue;
1268
+ }
1269
+ const semverType = commitType.semver;
1270
+ if (semverType === "major" || commit.isBreaking) {
1271
+ hasMajor = true;
1272
+ } else if (semverType === "minor") {
1273
+ hasMinor = true;
1274
+ } else if (semverType === "patch") {
1275
+ hasPatch = true;
1276
+ }
1277
+ }
1278
+ return hasMajor ? "major" : hasMinor ? "minor" : hasPatch ? "patch" : void 0;
1279
+ }
1280
+ function detectReleaseTypeFromCommits(commits, types) {
1281
+ return determineSemverChange(commits, types);
1417
1282
  }
1418
1283
  function validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType) {
1419
1284
  if (configuredType !== "prerelease" || !targetPreid || !isPrerelease(currentVersion)) {
1420
1285
  return;
1421
1286
  }
1422
1287
  const testVersion = semver.inc(currentVersion, "prerelease", targetPreid);
1423
- if (testVersion && !semver.gt(testVersion, currentVersion)) {
1288
+ const isNotUpgrade = testVersion && !semver.gt(testVersion, currentVersion);
1289
+ if (isNotUpgrade) {
1424
1290
  throw new Error(`Unable to graduate from ${currentVersion} to ${testVersion}, it's not a valid prerelease`);
1425
1291
  }
1426
1292
  }
1427
- function handleStableVersionWithReleaseType(commits, config, force) {
1293
+ function handleStableVersionWithReleaseType(commits, types, force) {
1428
1294
  if (!commits?.length && !force) {
1429
1295
  logger.debug('No commits found for stable version with "release" type, skipping bump');
1430
- return null;
1296
+ return void 0;
1431
1297
  }
1432
- const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, config) : null;
1298
+ const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
1433
1299
  if (!detectedType && !force) {
1434
1300
  logger.debug("No significant commits found, skipping bump");
1435
- return null;
1301
+ return void 0;
1436
1302
  }
1437
1303
  logger.debug(`Auto-detected release type from commits: ${detectedType}`);
1438
1304
  return detectedType;
1439
1305
  }
1440
- function handleStableVersionWithPrereleaseType(commits, config, force) {
1306
+ function handleStableVersionWithPrereleaseType(commits, types, force) {
1441
1307
  if (!commits?.length && !force) {
1442
1308
  logger.debug('No commits found for stable version with "prerelease" type, skipping bump');
1443
- return null;
1309
+ return void 0;
1444
1310
  }
1445
- const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, config) : null;
1311
+ const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
1446
1312
  if (!detectedType) {
1447
1313
  logger.debug("No significant commits found, using prepatch as default");
1448
1314
  return "prepatch";
@@ -1455,83 +1321,73 @@ function handlePrereleaseVersionToStable(currentVersion) {
1455
1321
  logger.debug(`Graduating from prerelease ${currentVersion} to stable release`);
1456
1322
  return "release";
1457
1323
  }
1458
- function handlePrereleaseVersionWithPrereleaseType(currentVersion, targetPreid, commits, config, force) {
1324
+ function handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force }) {
1459
1325
  const currentPreid = getPreid(currentVersion);
1460
- const hasChangedPreid = targetPreid && currentPreid && currentPreid !== targetPreid;
1326
+ const hasChangedPreid = preid && currentPreid && currentPreid !== preid;
1461
1327
  if (hasChangedPreid) {
1462
- const testVersion = semver.inc(currentVersion, "prerelease", targetPreid);
1328
+ const testVersion = semver.inc(currentVersion, "prerelease", preid);
1463
1329
  if (!testVersion) {
1464
- throw new Error(`Unable to change preid from ${currentPreid} to ${targetPreid} for version ${currentVersion}`);
1330
+ throw new Error(`Unable to change preid from ${currentPreid} to ${preid} for version ${currentVersion}`);
1465
1331
  }
1466
1332
  const isUpgrade = semver.gt(testVersion, currentVersion);
1467
1333
  if (!isUpgrade) {
1468
- throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${targetPreid})`);
1334
+ throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${preid})`);
1469
1335
  }
1470
1336
  return "prerelease";
1471
1337
  }
1472
1338
  if (!commits?.length && !force) {
1473
1339
  logger.debug("No commits found for prerelease version, skipping bump");
1474
- return null;
1340
+ return void 0;
1475
1341
  }
1476
1342
  logger.debug(`Incrementing prerelease version: ${currentVersion}`);
1477
1343
  return "prerelease";
1478
1344
  }
1479
- function handleExplicitReleaseType(configuredType, currentVersion) {
1345
+ function handleExplicitReleaseType({
1346
+ releaseType,
1347
+ currentVersion
1348
+ }) {
1480
1349
  const isCurrentPrerelease = isPrerelease(currentVersion);
1481
- const isGraduatingToStable = isCurrentPrerelease && isStableReleaseType(configuredType);
1350
+ const isGraduatingToStable = isCurrentPrerelease && isStableReleaseType(releaseType);
1482
1351
  if (isGraduatingToStable) {
1483
- logger.debug(`Graduating from prerelease ${currentVersion} to stable with type: ${configuredType}`);
1352
+ logger.debug(`Graduating from prerelease ${currentVersion} to stable with type: ${releaseType}`);
1484
1353
  } else {
1485
- logger.debug(`Using explicit release type: ${configuredType}`);
1354
+ logger.debug(`Using explicit release type: ${releaseType}`);
1486
1355
  }
1487
- return configuredType;
1356
+ return releaseType;
1488
1357
  }
1489
1358
  function determineReleaseType({
1490
1359
  currentVersion,
1491
- from,
1492
- to,
1493
1360
  commits,
1494
- config,
1361
+ releaseType,
1362
+ preid,
1363
+ types,
1495
1364
  force
1496
1365
  }) {
1497
- const configWithRange = {
1498
- ...config,
1499
- from,
1500
- to
1501
- };
1502
- const configuredType = configWithRange.bump.type;
1503
- const targetPreid = configWithRange.bump.preid;
1504
- if (configuredType === "release" && targetPreid) {
1366
+ if (releaseType === "release" && preid) {
1505
1367
  throw new Error('You cannot use a "release" type with a "preid", to use a preid you must use a "prerelease" type');
1506
1368
  }
1507
- validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType);
1369
+ validatePrereleaseDowngrade(currentVersion, preid, releaseType);
1508
1370
  if (force) {
1509
- logger.debug(`Force flag enabled, using configured type: ${configuredType}`);
1510
- return configuredType;
1371
+ logger.debug(`Force flag enabled, using configured type: ${releaseType}`);
1372
+ return releaseType;
1511
1373
  }
1512
1374
  const isCurrentPrerelease = isPrerelease(currentVersion);
1513
1375
  if (!isCurrentPrerelease) {
1514
- if (configuredType === "release") {
1515
- return handleStableVersionWithReleaseType(commits, configWithRange, force);
1376
+ if (releaseType === "release") {
1377
+ return handleStableVersionWithReleaseType(commits, types, force);
1516
1378
  }
1517
- if (configuredType === "prerelease") {
1518
- return handleStableVersionWithPrereleaseType(commits, configWithRange, force);
1379
+ if (releaseType === "prerelease") {
1380
+ return handleStableVersionWithPrereleaseType(commits, types, force);
1519
1381
  }
1520
- return handleExplicitReleaseType(configuredType, currentVersion);
1382
+ return handleExplicitReleaseType({ releaseType, currentVersion });
1521
1383
  }
1522
- if (configuredType === "release") {
1384
+ if (releaseType === "release") {
1523
1385
  return handlePrereleaseVersionToStable(currentVersion);
1524
1386
  }
1525
- if (configuredType === "prerelease") {
1526
- return handlePrereleaseVersionWithPrereleaseType(
1527
- currentVersion,
1528
- targetPreid,
1529
- commits,
1530
- configWithRange,
1531
- force
1532
- );
1387
+ if (releaseType === "prerelease") {
1388
+ return handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force });
1533
1389
  }
1534
- return handleExplicitReleaseType(configuredType, currentVersion);
1390
+ return handleExplicitReleaseType({ releaseType, currentVersion });
1535
1391
  }
1536
1392
  function writeVersion(pkgPath, version, dryRun = false) {
1537
1393
  const packageJsonPath = join(pkgPath, "package.json");
@@ -1552,33 +1408,32 @@ function writeVersion(pkgPath, version, dryRun = false) {
1552
1408
  throw new Error(`Unable to write version to ${packageJsonPath}: ${error}`);
1553
1409
  }
1554
1410
  }
1555
- function bumpPackageVersion({
1411
+ function getPackageNewVersion({
1556
1412
  currentVersion,
1557
1413
  releaseType,
1558
1414
  preid,
1559
1415
  suffix
1560
1416
  }) {
1561
- try {
1562
- let newVersion = semver.inc(currentVersion, releaseType, preid);
1563
- if (!newVersion) {
1564
- throw new Error(`Unable to bump version "${currentVersion}" with release type "${releaseType}"
1417
+ let newVersion = semver.inc(currentVersion, releaseType, preid);
1418
+ if (!newVersion) {
1419
+ throw new Error(`Unable to bump version "${currentVersion}" with release type "${releaseType}"
1565
1420
 
1566
1421
  You should use an explicit release type (use flag: --major, --minor, --patch, --premajor, --preminor, --prepatch, --prerelease)`);
1567
- }
1568
- if (isPrereleaseReleaseType(releaseType) && suffix) {
1569
- newVersion = newVersion.replace(/\.(\d+)$/, `.${suffix}`);
1570
- }
1571
- const isValidVersion = semver.gt(newVersion, currentVersion);
1572
- if (!isValidVersion) {
1573
- throw new Error(`Unable to bump version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
1574
- }
1575
- if (isGraduating(currentVersion, releaseType)) {
1576
- logger.info(`Graduating from prerelease ${currentVersion} to stable ${newVersion}`);
1577
- }
1578
- return newVersion;
1579
- } catch (error) {
1580
- throw new Error(`Unable to bump version: ${error}`);
1581
1422
  }
1423
+ if (isPrereleaseReleaseType(releaseType) && suffix) {
1424
+ newVersion = newVersion.replace(/\.(\d+)$/, `.${suffix}`);
1425
+ }
1426
+ const isValidVersion = semver.gt(newVersion, currentVersion);
1427
+ if (!isValidVersion) {
1428
+ throw new Error(`Unable to bump version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
1429
+ }
1430
+ if (isGraduating(currentVersion, releaseType)) {
1431
+ logger.info(`Graduating from prerelease ${currentVersion} to stable ${newVersion}`);
1432
+ }
1433
+ if (isChangedPreid(currentVersion, preid)) {
1434
+ logger.info(`Graduating from ${getPreid(currentVersion)} to ${preid}`);
1435
+ }
1436
+ return newVersion;
1582
1437
  }
1583
1438
  function updateLernaVersion({
1584
1439
  rootDir,
@@ -1612,7 +1467,7 @@ function updateLernaVersion({
1612
1467
  `, "utf8");
1613
1468
  logger.success(`Updated lerna.json: ${oldVersion} \u2192 ${version}`);
1614
1469
  } catch (error) {
1615
- logger.warn(`Unable to update lerna.json: ${error}`);
1470
+ logger.fail(`Unable to update lerna.json: ${error}`);
1616
1471
  }
1617
1472
  }
1618
1473
  function extractVersionFromPackageTag(tag) {
@@ -1658,13 +1513,16 @@ function isChangedPreid(currentVersion, targetPreid) {
1658
1513
  }
1659
1514
  return currentPreid !== targetPreid;
1660
1515
  }
1661
- function bumpPackageIndependently({
1516
+ function getBumpedPackageIndependently({
1662
1517
  pkg,
1663
1518
  dryRun
1664
1519
  }) {
1665
1520
  logger.debug(`Analyzing ${pkg.name}`);
1666
1521
  const currentVersion = pkg.version || "0.0.0";
1667
- const newVersion = pkg.version;
1522
+ const newVersion = pkg.newVersion;
1523
+ if (!newVersion) {
1524
+ return { bumped: false };
1525
+ }
1668
1526
  logger.debug(`Bumping ${pkg.name} from ${currentVersion} to ${newVersion}`);
1669
1527
  writeVersion(pkg.path, newVersion, dryRun);
1670
1528
  return { bumped: true, newVersion, oldVersion: currentVersion };
@@ -1690,9 +1548,9 @@ function displayUnifiedModePackages({
1690
1548
  newVersion,
1691
1549
  force
1692
1550
  }) {
1693
- logger.log(`${packages.length} package(s)${force ? " (force)" : ""}:`);
1551
+ logger.log(`${packages.length} package(s):`);
1694
1552
  packages.forEach((pkg) => {
1695
- logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion}`);
1553
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
1696
1554
  });
1697
1555
  logger.log("");
1698
1556
  }
@@ -1702,14 +1560,15 @@ function displaySelectiveModePackages({
1702
1560
  force
1703
1561
  }) {
1704
1562
  if (force) {
1705
- logger.log(`${packages.length} package(s) (force):`);
1563
+ logger.log(`${packages.length} package(s):`);
1706
1564
  packages.forEach((pkg) => {
1707
- logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion}`);
1565
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (force)`);
1708
1566
  });
1709
1567
  logger.log("");
1710
1568
  } else {
1711
1569
  const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
1712
1570
  const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
1571
+ const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
1713
1572
  if (packagesWithCommits.length > 0) {
1714
1573
  logger.log(`${packagesWithCommits.length} package(s) with commits:`);
1715
1574
  packagesWithCommits.forEach((pkg) => {
@@ -1724,33 +1583,47 @@ function displaySelectiveModePackages({
1724
1583
  });
1725
1584
  logger.log("");
1726
1585
  }
1586
+ if (packagesAsGraduation.length > 0) {
1587
+ logger.log(`${packagesAsGraduation.length} graduation package(s):`);
1588
+ packagesAsGraduation.forEach((pkg) => {
1589
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion}`);
1590
+ });
1591
+ logger.log("");
1592
+ }
1727
1593
  }
1728
1594
  }
1729
1595
  function displayIndependentModePackages({
1730
1596
  packages,
1731
- force,
1732
- dryRun
1597
+ force
1733
1598
  }) {
1734
1599
  if (force) {
1735
- logger.log(`${dryRun ? "[dry-run] " : ""}${packages.length} package(s) (force):`);
1600
+ logger.log(`${packages.length} package(s):`);
1736
1601
  packages.forEach((pkg) => {
1737
- logger.log(` \u2022 ${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.version}`);
1602
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (force)`);
1738
1603
  });
1739
1604
  logger.log("");
1740
1605
  } else {
1741
1606
  const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
1742
1607
  const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
1608
+ const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
1743
1609
  if (packagesWithCommits.length > 0) {
1744
- logger.log(`${dryRun ? "[dry-run] " : ""}${packagesWithCommits.length} package(s) with commits:`);
1610
+ logger.log(`${packagesWithCommits.length} package(s) with commits:`);
1745
1611
  packagesWithCommits.forEach((pkg) => {
1746
- logger.log(` \u2022 ${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.version}`);
1612
+ pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion}`);
1747
1613
  });
1748
1614
  logger.log("");
1749
1615
  }
1750
1616
  if (packagesAsDependents.length > 0) {
1751
- logger.log(`${dryRun ? "[dry-run] " : ""}${packagesAsDependents.length} dependent package(s):`);
1617
+ logger.log(`${packagesAsDependents.length} dependent package(s):`);
1752
1618
  packagesAsDependents.forEach((pkg) => {
1753
- logger.log(` \u2022 ${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.version}`);
1619
+ pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion}`);
1620
+ });
1621
+ logger.log("");
1622
+ }
1623
+ if (packagesAsGraduation.length > 0) {
1624
+ logger.log(`${packagesAsGraduation.length} graduation package(s):`);
1625
+ packagesAsGraduation.forEach((pkg) => {
1626
+ pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion}`);
1754
1627
  });
1755
1628
  logger.log("");
1756
1629
  }
@@ -1765,6 +1638,10 @@ async function confirmBump({
1765
1638
  newVersion,
1766
1639
  dryRun
1767
1640
  }) {
1641
+ if (packages.length === 0) {
1642
+ logger.debug("No packages to bump");
1643
+ return;
1644
+ }
1768
1645
  const lernaJsonExists = hasLernaJson(config.cwd);
1769
1646
  logger.log("");
1770
1647
  logger.info(`${dryRun ? "[dry-run] " : ""}The following packages will be updated:
@@ -1776,220 +1653,100 @@ async function confirmBump({
1776
1653
  lernaJsonExists,
1777
1654
  dryRun
1778
1655
  });
1779
- if (versionMode === "unified" && newVersion) {
1656
+ if (versionMode === "unified") {
1657
+ if (!newVersion) {
1658
+ logger.error("Cannot confirm bump in unified mode without a new version");
1659
+ process.exit(1);
1660
+ }
1780
1661
  displayUnifiedModePackages({ packages, newVersion, force });
1781
- } else if (versionMode === "selective" && newVersion) {
1662
+ } else if (versionMode === "selective") {
1663
+ if (!newVersion) {
1664
+ logger.error("Cannot confirm bump in selective mode without a new version");
1665
+ process.exit(1);
1666
+ }
1782
1667
  displaySelectiveModePackages({ packages, newVersion, force });
1783
1668
  } else if (versionMode === "independent") {
1784
- displayIndependentModePackages({ packages, force, dryRun });
1669
+ displayIndependentModePackages({ packages, force });
1785
1670
  }
1786
1671
  try {
1787
1672
  const confirmed = await confirm({
1788
1673
  message: `${dryRun ? "[dry-run] " : ""}Do you want to proceed with these version updates?`,
1789
1674
  default: true
1790
1675
  });
1791
- if (!confirmed) {
1792
- logger.warn("Bump cancelled by user");
1793
- process.exit(0);
1794
- }
1795
- } catch {
1796
- logger.error("Error while confirming bump");
1797
- process.exit(1);
1798
- }
1799
- logger.log("");
1800
- }
1801
- async function findPackagesWithCommits({
1802
- packages,
1803
- config,
1804
- force
1805
- }) {
1806
- const packagesWithCommits = [];
1807
- logger.debug(`Checking for commits in ${packages.length} package(s)`);
1808
- for (const pkg of packages) {
1809
- const { from, to } = await resolveTags({
1810
- config,
1811
- versionMode: "independent",
1812
- step: "bump",
1813
- currentVersion: pkg.version,
1814
- newVersion: void 0,
1815
- pkg,
1816
- logLevel: config.logLevel
1817
- });
1818
- const commits = await getPackageCommits({
1819
- pkg,
1820
- from,
1821
- to,
1822
- config,
1823
- changelog: false
1824
- });
1825
- if (commits.length <= 0 && !force) {
1826
- logger.debug(`${pkg.name}: No commits found, skipping`);
1827
- continue;
1828
- }
1829
- packagesWithCommits.push({ ...pkg, commits });
1830
- logger.debug(`${pkg.name}: ${commits.length} commit(s)`);
1831
- }
1832
- return packagesWithCommits;
1833
- }
1834
- async function calculatePackageNewVersion({
1835
- pkg,
1836
- config,
1837
- force,
1838
- suffix
1839
- }) {
1840
- const releaseType = config.bump.type;
1841
- const { from, to } = await resolveTags({
1842
- config,
1843
- versionMode: "independent",
1844
- step: "bump",
1845
- currentVersion: pkg.version,
1846
- newVersion: void 0,
1847
- pkg,
1848
- logLevel: config.logLevel
1849
- });
1850
- const forcedBump = pkg.reason === "dependency";
1851
- let forcedBumpType;
1852
- if (forcedBump) {
1853
- if (isStableReleaseType(releaseType)) {
1854
- forcedBumpType = "patch";
1855
- } else {
1856
- forcedBumpType = isPrerelease(pkg.version) ? "prerelease" : "prepatch";
1857
- }
1858
- }
1859
- const commits = pkg.commits?.length > 0 ? pkg.commits : await getPackageCommits({
1860
- pkg,
1861
- from,
1862
- to,
1863
- config,
1864
- changelog: false
1865
- });
1866
- let calculatedReleaseType = null;
1867
- if (forcedBumpType) {
1868
- calculatedReleaseType = forcedBumpType;
1869
- } else if (commits.length === 0 && !force) {
1870
- return null;
1871
- } else {
1872
- calculatedReleaseType = determineReleaseType({ from, to, commits, config, force, currentVersion: pkg.version });
1873
- if (!calculatedReleaseType) {
1874
- return null;
1875
- }
1876
- }
1877
- const currentVersion = pkg.version || "0.0.0";
1878
- const newVersion = bumpPackageVersion({
1879
- currentVersion,
1880
- releaseType: calculatedReleaseType,
1881
- preid: config.bump.preid,
1882
- suffix
1883
- });
1884
- return {
1885
- name: pkg.name,
1886
- path: pkg.path,
1887
- currentVersion,
1888
- version: newVersion,
1889
- fromTag: from,
1890
- reason: pkg.reason,
1891
- commits,
1892
- dependencyChain: pkg.dependencyChain
1893
- };
1894
- }
1895
- async function calculateNewVersionsForPackages({
1896
- allPackagesToBump,
1897
- config,
1898
- force,
1899
- suffix
1900
- }) {
1901
- const packagesWithNewVersions = [];
1902
- for (const pkgToBump of allPackagesToBump) {
1903
- const packageWithNewVersion = await calculatePackageNewVersion({
1904
- pkg: pkgToBump,
1905
- config,
1906
- force,
1907
- suffix
1908
- });
1909
- if (packageWithNewVersion) {
1910
- packagesWithNewVersions.push(packageWithNewVersion);
1911
- }
1912
- }
1913
- return packagesWithNewVersions;
1914
- }
1915
- async function findPackagesWithCommitsAndCalculateVersions({
1916
- packages,
1917
- config,
1918
- force,
1919
- suffix
1920
- }) {
1921
- const packagesWithCommits = await findPackagesWithCommits({
1922
- packages,
1923
- config,
1924
- force
1925
- });
1926
- if (packagesWithCommits.length === 0) {
1927
- return [];
1676
+ if (!confirmed) {
1677
+ logger.log("");
1678
+ logger.fail("Bump refused");
1679
+ process.exit(0);
1680
+ }
1681
+ } catch (error) {
1682
+ const userHasExited = error instanceof Error && error.name === "ExitPromptError";
1683
+ if (userHasExited) {
1684
+ logger.log("");
1685
+ logger.fail("Bump cancelled");
1686
+ process.exit(0);
1687
+ }
1688
+ logger.fail("Error while confirming bump");
1689
+ process.exit(1);
1928
1690
  }
1929
- const allPackagesToBump = expandPackagesToBumpWithDependents({
1930
- allPackages: packages,
1931
- packagesWithCommits,
1932
- dependencyTypes: config.bump.dependencyTypes
1933
- });
1934
- logger.debug(`Total packages to bump (including dependents): ${allPackagesToBump.length}`);
1935
- const packagesWithNewVersions = await calculateNewVersionsForPackages({
1936
- allPackagesToBump,
1937
- config,
1938
- force,
1939
- suffix
1940
- });
1941
- logger.debug(`Found ${packagesWithNewVersions.length} package(s) to bump`);
1942
- return packagesWithNewVersions;
1691
+ logger.log("");
1943
1692
  }
1944
- function bumpIndependentPackages({
1693
+ function getBumpedIndependentPackages({
1945
1694
  packages,
1946
1695
  dryRun
1947
1696
  }) {
1948
1697
  const bumpedPackages = [];
1949
1698
  for (const pkgToBump of packages) {
1950
- logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.currentVersion} to ${pkgToBump.version} (reason: ${pkgToBump.reason})`);
1951
- const result = bumpPackageIndependently({
1699
+ logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.version} to ${pkgToBump.newVersion} (reason: ${pkgToBump.reason})`);
1700
+ const result = getBumpedPackageIndependently({
1952
1701
  pkg: pkgToBump,
1953
1702
  dryRun
1954
1703
  });
1955
1704
  if (result.bumped) {
1956
1705
  bumpedPackages.push({
1957
1706
  ...pkgToBump,
1958
- version: result.newVersion
1707
+ version: result.oldVersion
1959
1708
  });
1960
1709
  }
1961
1710
  }
1962
1711
  return bumpedPackages;
1963
1712
  }
1964
1713
 
1965
- async function getLastRepoTag(options) {
1966
- let lastTag = null;
1967
- if (options?.onlyStable) {
1968
- const { stdout: stdout2 } = await execPromise(
1969
- `git tag --sort=-creatordate | grep -E '^[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+$' | head -n 1`,
1970
- {
1971
- logLevel: options?.logLevel,
1972
- noStderr: true,
1973
- noStdout: true,
1974
- noSuccess: true,
1975
- cwd: options?.cwd
1976
- }
1977
- );
1978
- lastTag = stdout2.trim();
1979
- logger.debug("Last stable tag:", lastTag || "No stable tags found");
1980
- return lastTag;
1981
- }
1714
+ function getIndependentTag({ version, name }) {
1715
+ return `${name}@${version}`;
1716
+ }
1717
+ async function getLastStableTag({ logLevel, cwd }) {
1718
+ const { stdout } = await execPromise(
1719
+ `git tag --sort=-creatordate | grep -E '^[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+$' | head -n 1`,
1720
+ {
1721
+ logLevel,
1722
+ noStderr: true,
1723
+ noStdout: true,
1724
+ noSuccess: true,
1725
+ cwd
1726
+ }
1727
+ );
1728
+ const lastTag = stdout.trim();
1729
+ logger.debug("Last stable tag:", lastTag || "No stable tags found");
1730
+ return lastTag;
1731
+ }
1732
+ async function getLastTag({ logLevel, cwd }) {
1982
1733
  const { stdout } = await execPromise(`git tag --sort=-creatordate | head -n 1`, {
1983
- logLevel: options?.logLevel,
1734
+ logLevel,
1984
1735
  noStderr: true,
1985
1736
  noStdout: true,
1986
1737
  noSuccess: true,
1987
- cwd: options?.cwd
1738
+ cwd
1988
1739
  });
1989
- lastTag = stdout.trim();
1740
+ const lastTag = stdout.trim();
1990
1741
  logger.debug("Last tag:", lastTag || "No tags found");
1991
1742
  return lastTag;
1992
1743
  }
1744
+ function getLastRepoTag(options) {
1745
+ if (options?.onlyStable) {
1746
+ return getLastStableTag({ logLevel: options?.logLevel, cwd: options?.cwd });
1747
+ }
1748
+ return getLastTag({ logLevel: options?.logLevel, cwd: options?.cwd });
1749
+ }
1993
1750
  async function getLastPackageTag({
1994
1751
  packageName,
1995
1752
  onlyStable,
@@ -2020,88 +1777,111 @@ async function getLastPackageTag({
2020
1777
  return null;
2021
1778
  }
2022
1779
  }
2023
- async function resolveTagsIndependent({
1780
+ async function resolveFromTagIndependent({
2024
1781
  cwd,
2025
- pkg,
1782
+ packageName,
2026
1783
  graduating,
2027
- newVersion,
2028
- step,
2029
1784
  logLevel
2030
1785
  }) {
2031
- let to;
2032
- if (step === "bump") {
2033
- to = getCurrentGitRef(cwd);
2034
- } else {
2035
- to = newVersion ? `${pkg.name}@${newVersion}` : getCurrentGitRef(cwd);
2036
- }
2037
1786
  const lastPackageTag = await getLastPackageTag({
2038
- packageName: pkg.name,
1787
+ packageName,
2039
1788
  onlyStable: graduating,
2040
1789
  logLevel
2041
1790
  });
2042
1791
  if (!lastPackageTag) {
2043
- const from = await getLastRepoTag({ logLevel }) || getFirstCommit(cwd);
2044
- return { from, to };
1792
+ return getFirstCommit(cwd);
2045
1793
  }
2046
- return { from: lastPackageTag, to };
1794
+ return lastPackageTag;
2047
1795
  }
2048
- async function resolveTagsUnified({
1796
+ async function resolveFromTagUnified({
2049
1797
  config,
2050
- newVersion,
2051
1798
  graduating,
2052
- step,
2053
1799
  logLevel
2054
1800
  }) {
2055
- let to;
2056
- if (step === "bump") {
2057
- to = getCurrentGitRef(config.cwd);
2058
- } else {
2059
- to = newVersion ? config.templates.tagBody.replace("{{newVersion}}", newVersion) : getCurrentGitRef(config.cwd);
2060
- }
2061
1801
  const from = await getLastRepoTag({ onlyStable: graduating, logLevel }) || getFirstCommit(config.cwd);
2062
- return { from, to };
1802
+ return from;
2063
1803
  }
2064
- async function resolveTags({
1804
+ async function resolveFromTag({
2065
1805
  config,
2066
1806
  versionMode,
2067
1807
  step,
2068
- pkg,
2069
- currentVersion,
2070
- newVersion,
1808
+ packageName,
1809
+ graduating,
2071
1810
  logLevel
2072
1811
  }) {
2073
- logger.debug(`[${versionMode}](${step}) Resolving tags`);
2074
- const graduating = currentVersion && isGraduating(currentVersion, config.bump.type) || false;
2075
- const newTags = newVersion && config.templates.tagBody.replace("{{newVersion}}", newVersion) || null;
2076
- if (config.from) {
2077
- const tags2 = {
2078
- from: config.from,
2079
- to: config.to || newTags || getCurrentGitRef(config.cwd)
2080
- };
2081
- logger.debug(`[${versionMode}](${step}) Using specified tags: ${tags2.from} \u2192 ${tags2.to}`);
2082
- return tags2;
2083
- }
1812
+ let from;
2084
1813
  if (versionMode === "independent") {
2085
- const tags2 = await resolveTagsIndependent({
1814
+ if (!packageName) {
1815
+ throw new Error("Package name is required for independent version mode");
1816
+ }
1817
+ from = await resolveFromTagIndependent({
2086
1818
  cwd: config.cwd,
2087
- pkg,
1819
+ packageName,
1820
+ graduating,
1821
+ logLevel
1822
+ });
1823
+ } else {
1824
+ from = await resolveFromTagUnified({
1825
+ config,
2088
1826
  graduating,
2089
- newVersion,
2090
- step,
2091
1827
  logLevel
2092
1828
  });
2093
- logger.debug(`[${versionMode}](${step}) Using tags: ${tags2.from} \u2192 ${tags2.to}`);
2094
- return tags2;
2095
1829
  }
2096
- const tags = await resolveTagsUnified({
1830
+ logger.debug(`[${versionMode}](${step}) Using from tag: ${from}`);
1831
+ return config.from || from;
1832
+ }
1833
+ function resolveToTag({
1834
+ config,
1835
+ versionMode,
1836
+ newVersion,
1837
+ step,
1838
+ packageName
1839
+ }) {
1840
+ const isUntaggedStep = step === "bump" || step === "changelog";
1841
+ let to;
1842
+ if (isUntaggedStep) {
1843
+ to = getCurrentGitRef(config.cwd);
1844
+ } else if (versionMode === "independent") {
1845
+ if (!packageName) {
1846
+ throw new Error("Package name is required for independent version mode");
1847
+ }
1848
+ if (!newVersion) {
1849
+ throw new Error("New version is required for independent version mode");
1850
+ }
1851
+ to = getIndependentTag({ version: newVersion, name: packageName });
1852
+ } else {
1853
+ to = newVersion ? config.templates.tagBody.replace("{{newVersion}}", newVersion) : getCurrentGitRef(config.cwd);
1854
+ }
1855
+ logger.debug(`[${versionMode}](${step}) Using to tag: ${to}`);
1856
+ return config.to || to;
1857
+ }
1858
+ async function resolveTags({
1859
+ config,
1860
+ step,
1861
+ pkg,
1862
+ newVersion
1863
+ }) {
1864
+ const versionMode = config.monorepo?.versionMode || "standalone";
1865
+ const logLevel = config.logLevel;
1866
+ logger.debug(`[${versionMode}](${step}) Resolving tags`);
1867
+ const releaseType = config.bump.type;
1868
+ const from = await resolveFromTag({
2097
1869
  config,
2098
- newVersion,
2099
- graduating,
1870
+ versionMode,
2100
1871
  step,
1872
+ packageName: pkg.name,
1873
+ graduating: isGraduating(pkg.version, releaseType),
2101
1874
  logLevel
2102
1875
  });
2103
- logger.debug(`[${versionMode}](${step}) Using tags: ${tags.from} \u2192 ${tags.to}`);
2104
- return tags;
1876
+ const to = resolveToTag({
1877
+ config,
1878
+ versionMode,
1879
+ newVersion,
1880
+ step,
1881
+ packageName: pkg.name
1882
+ });
1883
+ logger.debug(`[${versionMode}](${step}) Using tags: ${from} \u2192 ${to}`);
1884
+ return { from, to };
2105
1885
  }
2106
1886
 
2107
1887
  let sessionOtp;
@@ -2120,7 +1900,8 @@ function detectPackageManager(cwd = process.cwd()) {
2120
1900
  }
2121
1901
  }
2122
1902
  } catch (e) {
2123
- logger.warn(`Failed to parse package.json: ${e.message}`);
1903
+ const errorString = e instanceof Error ? e.message : String(e);
1904
+ logger.debug(`Failed to parse package.json: ${errorString}`);
2124
1905
  }
2125
1906
  }
2126
1907
  const lockFiles = {
@@ -2146,7 +1927,7 @@ function detectPackageManager(cwd = process.cwd()) {
2146
1927
  logger.debug("No package manager detected, defaulting to npm");
2147
1928
  return "npm";
2148
1929
  } catch (error) {
2149
- logger.warn(`Error detecting package manager: ${error}, defaulting to npm`);
1930
+ logger.fail(`Error detecting package manager: ${error}, defaulting to npm`);
2150
1931
  return "npm";
2151
1932
  }
2152
1933
  }
@@ -2180,23 +1961,13 @@ async function getPackagesToPublishInIndependentMode(sortedPackages, config) {
2180
1961
  for (const pkg of sortedPackages) {
2181
1962
  const { from, to } = await resolveTags({
2182
1963
  config,
2183
- versionMode: "independent",
2184
1964
  step: "publish",
2185
1965
  pkg,
2186
- newVersion: pkg.version,
2187
- currentVersion: void 0,
2188
- logLevel: config.logLevel
2189
- });
2190
- const commits = await getPackageCommits({
2191
- pkg,
2192
- from,
2193
- to,
2194
- config,
2195
- changelog: false
1966
+ newVersion: pkg.version
2196
1967
  });
2197
- if (commits.length > 0) {
1968
+ if (pkg.commits.length > 0) {
2198
1969
  packagesToPublish.push(pkg);
2199
- logger.debug(`${pkg.name}: ${commits.length} commit(s) since ${from} \u2192 ${to}`);
1970
+ logger.debug(`${pkg.name}: ${pkg.commits.length} commit(s) since ${from} \u2192 ${to}`);
2200
1971
  }
2201
1972
  }
2202
1973
  return packagesToPublish;
@@ -2301,7 +2072,7 @@ async function publishPackage({
2301
2072
  dryRun
2302
2073
  }) {
2303
2074
  const tag = determinePublishTag(pkg.version, config.publish.tag);
2304
- const packageNameAndVersion = `${pkg.name}@${pkg.version}`;
2075
+ const packageNameAndVersion = getIndependentTag(pkg);
2305
2076
  const baseCommand = packageManager === "yarn" && isYarnBerry() ? "yarn npm" : packageManager;
2306
2077
  logger.debug(`Building publish command for ${pkg.name}`);
2307
2078
  let dynamicOtp;
@@ -2342,198 +2113,412 @@ async function publishPackage({
2342
2113
  }
2343
2114
  }
2344
2115
 
2116
+ function readPackageJson(packagePath) {
2117
+ const packageJsonPath = join(packagePath, "package.json");
2118
+ if (!existsSync(packageJsonPath))
2119
+ throw new Error(`package.json not found at ${packageJsonPath}`);
2120
+ if (!statSync(packagePath).isDirectory())
2121
+ throw new Error(`Not a directory: ${packagePath}`);
2122
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
2123
+ if (!packageJson.name || !packageJson.version) {
2124
+ throw new Error(`Invalid package.json at ${packagePath}`);
2125
+ }
2126
+ return {
2127
+ name: packageJson.name,
2128
+ version: packageJson.version,
2129
+ private: packageJson.private || false,
2130
+ path: packagePath
2131
+ };
2132
+ }
2133
+ async function getRootPackage({
2134
+ config,
2135
+ force,
2136
+ from,
2137
+ to,
2138
+ suffix,
2139
+ changelog
2140
+ }) {
2141
+ try {
2142
+ const packageJson = readPackageJson(config.cwd);
2143
+ const commits = await getPackageCommits({
2144
+ pkg: packageJson,
2145
+ from,
2146
+ to,
2147
+ config,
2148
+ changelog
2149
+ });
2150
+ const releaseType = determineReleaseType({
2151
+ currentVersion: packageJson.version,
2152
+ commits,
2153
+ releaseType: config.bump.type,
2154
+ preid: config.bump.preid,
2155
+ types: config.types,
2156
+ force
2157
+ });
2158
+ if (!releaseType) {
2159
+ logger.fail("No commits require a version bump");
2160
+ process.exit(0);
2161
+ }
2162
+ const newVersion = getPackageNewVersion({
2163
+ currentVersion: packageJson.version,
2164
+ releaseType,
2165
+ preid: config.bump.preid,
2166
+ suffix
2167
+ });
2168
+ return {
2169
+ ...packageJson,
2170
+ path: config.cwd,
2171
+ fromTag: from,
2172
+ commits,
2173
+ newVersion
2174
+ };
2175
+ } catch (error) {
2176
+ const errorMessage = error instanceof Error ? error.message : String(error);
2177
+ throw new Error(errorMessage);
2178
+ }
2179
+ }
2180
+ function readPackages({
2181
+ cwd,
2182
+ patterns,
2183
+ ignorePackageNames
2184
+ }) {
2185
+ const packages = [];
2186
+ const foundPaths = /* @__PURE__ */ new Set();
2187
+ if (!patterns)
2188
+ patterns = ["."];
2189
+ logger.debug(`Getting packages from patterns: ${patterns.join(", ")}`);
2190
+ for (const pattern of patterns) {
2191
+ try {
2192
+ const matches = fastGlob.sync(pattern, {
2193
+ cwd,
2194
+ onlyDirectories: true,
2195
+ absolute: true,
2196
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
2197
+ });
2198
+ for (const matchPath of matches) {
2199
+ if (foundPaths.has(matchPath))
2200
+ continue;
2201
+ const packageBase = readPackageJson(matchPath);
2202
+ if (!packageBase || packageBase.private || ignorePackageNames?.includes(packageBase.name))
2203
+ continue;
2204
+ foundPaths.add(matchPath);
2205
+ packages.push({
2206
+ ...packageBase,
2207
+ path: matchPath
2208
+ });
2209
+ }
2210
+ } catch (error) {
2211
+ logger.error(error);
2212
+ }
2213
+ }
2214
+ return packages;
2215
+ }
2216
+ function getPackageReleaseType({
2217
+ pkg,
2218
+ config,
2219
+ force
2220
+ }) {
2221
+ const releaseType = config.bump.type;
2222
+ if (force) {
2223
+ return determineReleaseType({
2224
+ currentVersion: pkg.version,
2225
+ commits: pkg.commits,
2226
+ releaseType,
2227
+ preid: config.bump.preid,
2228
+ types: config.types,
2229
+ force
2230
+ });
2231
+ }
2232
+ if (pkg.reason === "dependency") {
2233
+ if (isStableReleaseType(releaseType))
2234
+ return "patch";
2235
+ if (isPrerelease(pkg.version))
2236
+ return "prerelease";
2237
+ return "prepatch";
2238
+ }
2239
+ return determineReleaseType({
2240
+ currentVersion: pkg.version,
2241
+ commits: pkg.commits,
2242
+ releaseType,
2243
+ preid: config.bump.preid,
2244
+ types: config.types,
2245
+ force
2246
+ });
2247
+ }
2248
+ async function getPackages({
2249
+ patterns,
2250
+ config,
2251
+ suffix,
2252
+ force
2253
+ }) {
2254
+ const readedPackages = readPackages({
2255
+ cwd: config.cwd,
2256
+ patterns,
2257
+ ignorePackageNames: config.monorepo?.ignorePackageNames
2258
+ });
2259
+ const packages = /* @__PURE__ */ new Map();
2260
+ const foundPaths = /* @__PURE__ */ new Set();
2261
+ if (!patterns)
2262
+ patterns = ["."];
2263
+ logger.debug(`Getting packages from patterns: ${patterns.join(", ")}`);
2264
+ const patternsSet = new Set(patterns);
2265
+ for (const pattern of patternsSet) {
2266
+ const matches = fastGlob.sync(pattern, {
2267
+ cwd: config.cwd,
2268
+ onlyDirectories: true,
2269
+ absolute: true,
2270
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
2271
+ });
2272
+ for (const matchPath of matches) {
2273
+ if (foundPaths.has(matchPath))
2274
+ continue;
2275
+ const packageBase = readPackageJson(matchPath);
2276
+ if (packageBase.private) {
2277
+ logger.debug(`${packageBase.name} is private and will be ignored`);
2278
+ continue;
2279
+ }
2280
+ if (config.monorepo?.ignorePackageNames?.includes(packageBase.name)) {
2281
+ logger.debug(`${packageBase.name} ignored by config (monorepo.ignorePackageNames)`);
2282
+ continue;
2283
+ }
2284
+ if (!packageBase.version) {
2285
+ logger.warn(`${packageBase.name} has no version and will be ignored`);
2286
+ continue;
2287
+ }
2288
+ const { from, to } = await resolveTags({
2289
+ config,
2290
+ step: "bump",
2291
+ pkg: packageBase,
2292
+ newVersion: void 0
2293
+ });
2294
+ const commits = await getPackageCommits({
2295
+ pkg: packageBase,
2296
+ from,
2297
+ to,
2298
+ config,
2299
+ changelog: false
2300
+ });
2301
+ foundPaths.add(matchPath);
2302
+ const dependencies = getPackageDependencies({
2303
+ packagePath: matchPath,
2304
+ allPackageNames: new Set(readedPackages.map((p) => p.name)),
2305
+ dependencyTypes: config.bump?.dependencyTypes
2306
+ });
2307
+ packages.set(packageBase.name, {
2308
+ ...packageBase,
2309
+ path: matchPath,
2310
+ fromTag: from,
2311
+ dependencies,
2312
+ commits,
2313
+ reason: commits.length > 0 ? "commits" : void 0,
2314
+ dependencyChain: void 0,
2315
+ newVersion: void 0
2316
+ });
2317
+ }
2318
+ }
2319
+ const packagesArray = Array.from(packages.values());
2320
+ const packagesWithCommits = packagesArray.filter((p) => p.commits.length > 0);
2321
+ const expandedPackages = expandPackagesToBumpWithDependents({
2322
+ allPackages: packagesArray,
2323
+ packagesWithCommits
2324
+ });
2325
+ for (const pkg of expandedPackages) {
2326
+ packages.set(pkg.name, pkg);
2327
+ }
2328
+ for (const pkg of Array.from(packages.values())) {
2329
+ const releaseType = getPackageReleaseType({
2330
+ pkg,
2331
+ config,
2332
+ force
2333
+ });
2334
+ const newVersion = releaseType ? getPackageNewVersion({
2335
+ currentVersion: pkg.version,
2336
+ releaseType,
2337
+ preid: config.bump.preid,
2338
+ suffix
2339
+ }) : void 0;
2340
+ const graduating = releaseType && isGraduating(pkg.version, releaseType) || isChangedPreid(pkg.version, config.bump.preid);
2341
+ packages.set(pkg.name, {
2342
+ ...pkg,
2343
+ newVersion,
2344
+ reason: pkg.reason || releaseType && graduating && "graduation" || void 0
2345
+ });
2346
+ }
2347
+ const packagesToBump = Array.from(packages.values()).filter((p) => p.reason || force);
2348
+ if (packagesToBump.length === 0) {
2349
+ logger.debug("No packages to bump");
2350
+ return [];
2351
+ }
2352
+ return packagesToBump;
2353
+ }
2354
+ function isAllowedCommit({
2355
+ commit,
2356
+ type,
2357
+ changelog
2358
+ }) {
2359
+ if (commit.type === "chore" && ["deps", "release"].includes(commit.scope) && !commit.isBreaking) {
2360
+ return false;
2361
+ }
2362
+ if (typeof type === "object") {
2363
+ return !!type.semver || changelog && !!type.title;
2364
+ }
2365
+ if (typeof type === "boolean") {
2366
+ return type;
2367
+ }
2368
+ return false;
2369
+ }
2370
+ async function getPackageCommits({
2371
+ pkg,
2372
+ from,
2373
+ to,
2374
+ config,
2375
+ changelog
2376
+ }) {
2377
+ logger.debug(`Analyzing commits for ${pkg.name} since ${from} to ${to}`);
2378
+ const changelogConfig = {
2379
+ ...config,
2380
+ from,
2381
+ to
2382
+ };
2383
+ const rawCommits = await getGitDiff(from, to, changelogConfig.cwd);
2384
+ const allCommits = parseCommits(rawCommits, changelogConfig);
2385
+ const hasBreakingChanges = allCommits.some((commit) => commit.isBreaking);
2386
+ logger.debug(`Has breaking changes: ${hasBreakingChanges}`);
2387
+ const rootPackage = readPackageJson(changelogConfig.cwd);
2388
+ const commits = allCommits.filter((commit) => {
2389
+ const type = changelogConfig?.types[commit.type];
2390
+ if (!isAllowedCommit({ commit, type, changelog })) {
2391
+ return false;
2392
+ }
2393
+ if (pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) {
2394
+ return true;
2395
+ }
2396
+ const packageRelativePath = relative(changelogConfig.cwd, pkg.path);
2397
+ const scopeMatches = commit.scope === pkg.name;
2398
+ const bodyContainsPath = commit.body.includes(packageRelativePath);
2399
+ return scopeMatches || bodyContainsPath;
2400
+ });
2401
+ logger.debug(`Found ${commits.length} commit(s) for ${pkg.name} from ${from} to ${to}`);
2402
+ if (commits.length > 0) {
2403
+ logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
2404
+ } else {
2405
+ logger.debug(`${pkg.name}: No commits found`);
2406
+ }
2407
+ return commits;
2408
+ }
2409
+ function hasLernaJson(rootDir) {
2410
+ const lernaJsonPath = join(rootDir, "lerna.json");
2411
+ return existsSync(lernaJsonPath);
2412
+ }
2413
+
2345
2414
  async function bumpUnifiedMode({
2346
2415
  config,
2347
- packages,
2348
2416
  dryRun,
2349
2417
  force,
2350
2418
  suffix
2351
2419
  }) {
2352
2420
  logger.debug("Starting bump in unified mode");
2353
- const rootPackage = getRootPackage(config.cwd);
2354
- const currentVersion = rootPackage.version;
2421
+ const rootPackageBase = readPackageJson(config.cwd);
2355
2422
  const { from, to } = await resolveTags({
2356
2423
  config,
2357
- versionMode: "unified",
2358
2424
  step: "bump",
2359
2425
  newVersion: void 0,
2360
- pkg: void 0,
2361
- currentVersion,
2362
- logLevel: config.logLevel
2426
+ pkg: rootPackageBase
2363
2427
  });
2364
- logger.debug(`Bump from ${from} to ${to}`);
2365
- const commits = await getPackageCommits({
2366
- pkg: rootPackage,
2428
+ const rootPackage = await getRootPackage({
2429
+ config,
2430
+ force,
2367
2431
  from,
2368
2432
  to,
2369
- config,
2433
+ suffix,
2370
2434
  changelog: false
2371
2435
  });
2372
- logger.debug(`Found ${commits.length} commits since ${from}`);
2373
- const releaseType = determineReleaseType({
2374
- from,
2375
- to,
2376
- currentVersion,
2377
- commits,
2436
+ const currentVersion = rootPackage.version;
2437
+ const newVersion = rootPackage.newVersion;
2438
+ if (!newVersion) {
2439
+ throw new Error(`Could not determine a new version for ${rootPackage.name} (root)`);
2440
+ }
2441
+ logger.debug(`Bump from ${from} to ${to}`);
2442
+ logger.debug(`${currentVersion} \u2192 ${newVersion} (${config.monorepo?.versionMode || "standalone"} mode)`);
2443
+ const packages = await getPackages({
2378
2444
  config,
2445
+ patterns: config.monorepo?.packages,
2446
+ suffix,
2379
2447
  force
2380
2448
  });
2381
- if (!releaseType) {
2382
- logger.debug("No commits require a version bump");
2383
- return { bumped: false };
2384
- }
2385
- if (config.bump.type && force) {
2386
- logger.debug(`Using specified release type: ${releaseType}`);
2387
- } else {
2388
- logger.debug(`Detected release type from commits: ${releaseType}`);
2389
- }
2390
- const newVersion = bumpPackageVersion({
2391
- currentVersion,
2392
- releaseType,
2393
- preid: config.bump.preid,
2394
- suffix
2395
- });
2396
- logger.debug(`${currentVersion} \u2192 ${newVersion} (unified mode)`);
2397
- const allPackages = [rootPackage, ...packages];
2398
2449
  if (!config.bump.yes) {
2399
2450
  await confirmBump({
2400
2451
  versionMode: "unified",
2401
- config,
2402
2452
  packages,
2453
+ config,
2403
2454
  force,
2404
2455
  currentVersion,
2405
2456
  newVersion,
2406
2457
  dryRun
2407
2458
  });
2408
2459
  } else {
2409
- logger.info(`${allPackages.length === 1 ? allPackages[0].name : allPackages.length} package(s) bumped from ${currentVersion} to ${newVersion} (unified mode)`);
2460
+ logger.info(`${packages.length === 1 ? packages[0].name : packages.length} package(s) bumped from ${currentVersion} to ${newVersion} (${config.monorepo?.versionMode || "standalone"} mode)`);
2410
2461
  }
2411
- for (const pkg of allPackages) {
2462
+ for (const pkg of [rootPackage, ...packages]) {
2412
2463
  writeVersion(pkg.path, newVersion, dryRun);
2413
2464
  }
2414
- if (newVersion) {
2415
- updateLernaVersion({
2416
- rootDir: config.cwd,
2417
- versionMode: config.monorepo?.versionMode,
2418
- version: newVersion,
2419
- dryRun
2420
- });
2421
- }
2465
+ updateLernaVersion({
2466
+ rootDir: config.cwd,
2467
+ versionMode: config.monorepo?.versionMode,
2468
+ version: newVersion,
2469
+ dryRun
2470
+ });
2422
2471
  if (!dryRun) {
2423
- logger.info(`All ${allPackages.length} package(s) bumped to ${newVersion}`);
2472
+ logger.info(`All ${packages.length} package(s) bumped to ${newVersion}`);
2424
2473
  }
2425
2474
  return {
2426
2475
  bumped: true,
2427
2476
  oldVersion: currentVersion,
2428
2477
  fromTag: from,
2429
2478
  newVersion,
2479
+ rootPackage,
2430
2480
  bumpedPackages: packages.map((pkg) => ({
2431
2481
  ...pkg,
2432
- currentVersion: pkg.version,
2433
- version: newVersion,
2434
- fromTag: from
2482
+ oldVersion: pkg.version,
2483
+ newVersion,
2484
+ fromTag: from,
2485
+ reason: "commits"
2435
2486
  }))
2436
2487
  };
2437
2488
  }
2438
- async function bumpIndependentMode({
2439
- config,
2440
- packages,
2441
- dryRun,
2442
- force,
2443
- suffix
2444
- }) {
2445
- logger.debug("Starting bump in independent mode");
2446
- const packagesWithNewVersions = await findPackagesWithCommitsAndCalculateVersions({
2447
- packages,
2448
- config,
2449
- force,
2450
- suffix
2451
- });
2452
- if (packagesWithNewVersions.length === 0) {
2453
- logger.debug("No packages have commits");
2454
- return { bumped: false };
2455
- }
2456
- if (!config.bump.yes) {
2457
- await confirmBump({
2458
- versionMode: "independent",
2459
- config,
2460
- packages: packagesWithNewVersions,
2461
- force,
2462
- dryRun
2463
- });
2464
- } else {
2465
- const bumpedByCommits2 = packagesWithNewVersions.filter((p) => p.reason === "commits").length;
2466
- const bumpedByDependency2 = packagesWithNewVersions.length - bumpedByCommits2;
2467
- logger.info(
2468
- `${bumpedByCommits2 + bumpedByDependency2} package(s) will be bumped independently (${bumpedByCommits2} from commits, ${bumpedByDependency2} from dependencies)`
2469
- );
2470
- }
2471
- const bumpedPackages = bumpIndependentPackages({
2472
- packages: packagesWithNewVersions,
2473
- dryRun
2474
- });
2475
- const bumpedByCommits = bumpedPackages.filter(
2476
- (p) => packagesWithNewVersions.find((pkg) => pkg.name === p.name)?.reason === "commits"
2477
- ).length;
2478
- const bumpedByDependency = bumpedPackages.length - bumpedByCommits;
2479
- if (bumpedPackages.length === 0) {
2480
- logger.debug("No packages were bumped");
2481
- } else {
2482
- logger.info(
2483
- `${dryRun ? "[dry-run] " : ""}${bumpedPackages.length} package(s) bumped independently (${bumpedByCommits} from commits, ${bumpedByDependency} from dependencies)`
2484
- );
2485
- }
2486
- return {
2487
- bumped: bumpedPackages.length > 0,
2488
- bumpedPackages
2489
- };
2490
- }
2491
2489
  async function bumpSelectiveMode({
2492
2490
  config,
2493
- packages,
2494
2491
  dryRun,
2495
2492
  force,
2496
2493
  suffix
2497
2494
  }) {
2498
2495
  logger.debug("Starting bump in selective mode");
2499
- const rootPackage = getRootPackage(config.cwd);
2500
- const currentVersion = rootPackage.version;
2496
+ const rootPackageBase = readPackageJson(config.cwd);
2501
2497
  const { from, to } = await resolveTags({
2502
2498
  config,
2503
- versionMode: "selective",
2504
2499
  step: "bump",
2505
- currentVersion,
2506
- newVersion: void 0,
2507
- pkg: void 0,
2508
- logLevel: config.logLevel
2500
+ pkg: rootPackageBase,
2501
+ newVersion: void 0
2509
2502
  });
2510
- logger.debug(`Bump from ${from} to ${to}`);
2511
- const commits = await getPackageCommits({
2512
- pkg: rootPackage,
2503
+ const rootPackage = await getRootPackage({
2504
+ config,
2505
+ force,
2513
2506
  from,
2514
2507
  to,
2515
- config,
2508
+ suffix,
2516
2509
  changelog: false
2517
2510
  });
2518
- const releaseType = determineReleaseType({ from, to, commits, config, force, currentVersion });
2519
- if (!releaseType) {
2520
- logger.debug("No commits require a version bump");
2521
- return { bumped: false };
2522
- }
2523
- const newVersion = bumpPackageVersion({
2524
- currentVersion,
2525
- releaseType,
2526
- preid: config.bump.preid,
2527
- suffix
2528
- });
2511
+ const currentVersion = rootPackage.version;
2512
+ const newVersion = rootPackage.newVersion;
2513
+ logger.debug(`Bump from ${currentVersion} to ${newVersion}`);
2529
2514
  logger.debug("Determining packages to bump...");
2530
- const packagesToBump = force ? packages : await getPackageToBump({
2531
- packages,
2532
- from,
2533
- to,
2534
- config
2515
+ const packages = await getPackages({
2516
+ config,
2517
+ patterns: config.monorepo?.packages,
2518
+ suffix,
2519
+ force
2535
2520
  });
2536
- if (packagesToBump.length === 0) {
2521
+ if (packages.length === 0) {
2537
2522
  logger.debug("No packages have commits, skipping bump");
2538
2523
  return { bumped: false };
2539
2524
  }
@@ -2541,7 +2526,7 @@ async function bumpSelectiveMode({
2541
2526
  await confirmBump({
2542
2527
  versionMode: "selective",
2543
2528
  config,
2544
- packages: packagesToBump,
2529
+ packages,
2545
2530
  force,
2546
2531
  currentVersion,
2547
2532
  newVersion,
@@ -2549,18 +2534,18 @@ async function bumpSelectiveMode({
2549
2534
  });
2550
2535
  } else {
2551
2536
  if (force) {
2552
- logger.info(`${packagesToBump.length} package(s) bumped to ${newVersion} (force)`);
2537
+ logger.info(`${packages.length} package(s) bumped to ${newVersion} (force)`);
2553
2538
  } else {
2554
- const bumpedByCommits = packagesToBump.filter((p) => "reason" in p && p.reason === "commits").length;
2555
- const bumpedByDependency = packagesToBump.filter((p) => "reason" in p && p.reason === "dependency").length;
2539
+ const bumpedByCommits = packages.filter((p) => "reason" in p && p.reason === "commits").length;
2540
+ const bumpedByDependency = packages.filter((p) => "reason" in p && p.reason === "dependency").length;
2541
+ const bumpedByGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation").length;
2556
2542
  logger.info(
2557
- `${currentVersion} \u2192 ${newVersion} (selective mode: ${bumpedByCommits} with commits, ${bumpedByDependency} as dependents)`
2543
+ `${currentVersion} \u2192 ${newVersion} (selective mode: ${bumpedByCommits} with commits, ${bumpedByDependency} as dependents, ${bumpedByGraduation} from graduation)`
2558
2544
  );
2559
2545
  }
2560
2546
  }
2561
- logger.debug(`Writing version to ${packagesToBump.length} package(s)`);
2562
- writeVersion(rootPackage.path, newVersion, dryRun);
2563
- for (const pkg of packagesToBump) {
2547
+ logger.debug(`Writing version to ${packages.length} package(s)`);
2548
+ for (const pkg of [rootPackage, ...packages]) {
2564
2549
  writeVersion(pkg.path, newVersion, dryRun);
2565
2550
  }
2566
2551
  updateLernaVersion({
@@ -2571,19 +2556,76 @@ async function bumpSelectiveMode({
2571
2556
  });
2572
2557
  if (!dryRun) {
2573
2558
  logger.info(
2574
- `${packagesToBump.length} package(s) bumped to ${newVersion} (${packages.length - packagesToBump.length} skipped)`
2559
+ `${packages.length} package(s) bumped to ${newVersion}`
2575
2560
  );
2576
2561
  }
2577
2562
  return {
2578
2563
  bumped: true,
2579
2564
  oldVersion: currentVersion,
2565
+ rootPackage,
2580
2566
  fromTag: from,
2581
2567
  newVersion,
2582
- bumpedPackages: packagesToBump.map((pkg) => ({
2568
+ bumpedPackages: packages.map((pkg) => ({
2583
2569
  ...pkg,
2584
- currentVersion: pkg.version,
2585
- version: newVersion,
2586
- fromTag: from
2570
+ oldVersion: pkg.version,
2571
+ fromTag: from,
2572
+ newVersion
2573
+ }))
2574
+ };
2575
+ }
2576
+ async function bumpIndependentMode({
2577
+ config,
2578
+ dryRun,
2579
+ suffix,
2580
+ force
2581
+ }) {
2582
+ logger.debug("Starting bump in independent mode");
2583
+ const packagesToBump = await getPackages({
2584
+ config,
2585
+ patterns: config.monorepo?.packages,
2586
+ suffix,
2587
+ force
2588
+ });
2589
+ if (packagesToBump.length === 0) {
2590
+ logger.debug("No packages have commits");
2591
+ return { bumped: false };
2592
+ }
2593
+ if (!config.bump.yes) {
2594
+ await confirmBump({
2595
+ versionMode: "independent",
2596
+ config,
2597
+ packages: packagesToBump,
2598
+ force,
2599
+ dryRun
2600
+ });
2601
+ } else {
2602
+ const bumpedByCommits2 = packagesToBump.filter((p) => p.reason === "commits").length;
2603
+ const bumpedByDependency2 = packagesToBump.filter((p) => p.reason === "dependency").length;
2604
+ const bumpedByGraduation = packagesToBump.filter((p) => p.reason === "graduation").length;
2605
+ logger.info(
2606
+ `${bumpedByCommits2 + bumpedByDependency2 + bumpedByGraduation} package(s) will be bumped independently (${bumpedByCommits2} from commits, ${bumpedByDependency2} from dependencies, ${bumpedByGraduation} from graduation)`
2607
+ );
2608
+ }
2609
+ const bumpedPackages = getBumpedIndependentPackages({
2610
+ packages: packagesToBump,
2611
+ dryRun
2612
+ });
2613
+ const bumpedByCommits = bumpedPackages.filter(
2614
+ (p) => packagesToBump.find((pkg) => pkg.name === p.name)?.reason === "commits"
2615
+ ).length;
2616
+ const bumpedByDependency = bumpedPackages.length - bumpedByCommits;
2617
+ if (bumpedPackages.length === 0) {
2618
+ logger.debug("No packages were bumped");
2619
+ } else {
2620
+ logger.info(
2621
+ `${dryRun ? "[dry-run] " : ""}${bumpedPackages.length} package(s) bumped independently (${bumpedByCommits} from commits, ${bumpedByDependency} from dependencies)`
2622
+ );
2623
+ }
2624
+ return {
2625
+ bumped: bumpedPackages.length > 0,
2626
+ bumpedPackages: bumpedPackages.map((pkg) => ({
2627
+ ...pkg,
2628
+ oldVersion: pkg.version
2587
2629
  }))
2588
2630
  };
2589
2631
  }
@@ -2613,7 +2655,7 @@ async function bump(options = {}) {
2613
2655
  }
2614
2656
  await fetchGitTags(config.cwd);
2615
2657
  logger.info(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
2616
- const packages = getPackages({
2658
+ const packages = readPackages({
2617
2659
  cwd: config.cwd,
2618
2660
  patterns: config.monorepo?.packages,
2619
2661
  ignorePackageNames: config.monorepo?.ignorePackageNames
@@ -2622,7 +2664,6 @@ async function bump(options = {}) {
2622
2664
  let result;
2623
2665
  const payload = {
2624
2666
  config,
2625
- packages,
2626
2667
  dryRun,
2627
2668
  force,
2628
2669
  suffix: options.suffix
@@ -2651,17 +2692,20 @@ async function bump(options = {}) {
2651
2692
  }
2652
2693
  }
2653
2694
 
2654
- function getPackagesToGenerateChangelogFor({
2695
+ async function getPackagesToGenerateChangelogFor({
2655
2696
  config,
2656
- bumpedPackages
2697
+ bumpResult,
2698
+ suffix,
2699
+ force
2657
2700
  }) {
2658
- if (bumpedPackages && bumpedPackages.length > 0) {
2659
- return bumpedPackages;
2701
+ if (bumpResult?.bumpedPackages && bumpResult.bumpedPackages.length > 0) {
2702
+ return bumpResult.bumpedPackages;
2660
2703
  }
2661
- return getPackages({
2662
- cwd: config.cwd,
2663
- ignorePackageNames: config.monorepo?.ignorePackageNames,
2664
- patterns: config.monorepo?.packages
2704
+ return await getPackages({
2705
+ config,
2706
+ patterns: config.monorepo?.packages,
2707
+ suffix,
2708
+ force
2665
2709
  });
2666
2710
  }
2667
2711
  async function generateIndependentRootChangelog({
@@ -2674,39 +2718,16 @@ async function generateIndependentRootChangelog({
2674
2718
  return;
2675
2719
  }
2676
2720
  logger.debug("Generating aggregated root changelog for independent mode");
2677
- const rootPackage = getRootPackage(config.cwd);
2678
2721
  const packageChangelogs = [];
2679
2722
  for (const pkg of packages) {
2680
- const { from, to } = await resolveTags({
2681
- config,
2682
- versionMode: "independent",
2683
- step: "changelog",
2684
- currentVersion: pkg.version,
2685
- newVersion: void 0,
2686
- pkg,
2687
- logLevel: config.logLevel
2688
- });
2689
- const lastTag = from;
2690
- logger.debug(`Generating changelog for ${pkg.name} (${lastTag}...${to})`);
2691
- const commits = await getPackageCommits({
2692
- pkg,
2693
- from: lastTag,
2694
- to,
2695
- config,
2696
- changelog: true
2697
- });
2698
2723
  const changelog2 = await generateChangelog({
2699
- config,
2700
2724
  pkg,
2701
- commits,
2702
- from: lastTag,
2703
- dryRun
2725
+ config,
2726
+ dryRun,
2727
+ newVersion: isBumpedPackage(pkg) && pkg.newVersion || pkg.version
2704
2728
  });
2705
2729
  if (changelog2) {
2706
- const cleanedChangelog = changelog2.split("\n").slice(2).join("\n");
2707
- packageChangelogs.push(`## ${pkg.name}@${pkg.version}
2708
-
2709
- ${cleanedChangelog}`);
2730
+ packageChangelogs.push(changelog2);
2710
2731
  }
2711
2732
  }
2712
2733
  if (packageChangelogs.length === 0) {
@@ -2718,7 +2739,9 @@ ${cleanedChangelog}`);
2718
2739
 
2719
2740
  ${packageChangelogs.join("\n\n")}`;
2720
2741
  logger.verbose(`Aggregated root changelog: ${aggregatedChangelog}`);
2742
+ const rootPackage = readPackageJson(config.cwd);
2721
2743
  writeChangelogToFile({
2744
+ cwd: config.cwd,
2722
2745
  pkg: rootPackage,
2723
2746
  changelog: aggregatedChangelog,
2724
2747
  dryRun
@@ -2727,41 +2750,43 @@ ${packageChangelogs.join("\n\n")}`;
2727
2750
  }
2728
2751
  async function generateSimpleRootChangelog({
2729
2752
  config,
2730
- dryRun
2753
+ dryRun,
2754
+ force,
2755
+ suffix,
2756
+ bumpResult
2731
2757
  }) {
2732
2758
  if (!config.changelog?.rootChangelog) {
2733
2759
  logger.debug("Skipping root changelog generation");
2734
2760
  return;
2735
2761
  }
2736
2762
  logger.debug("Generating simple root changelog");
2737
- const rootPackage = getRootPackage(config.cwd);
2763
+ const rootPackageRead = readPackageJson(config.cwd);
2738
2764
  const { from, to } = await resolveTags({
2739
2765
  config,
2740
- versionMode: config.monorepo?.versionMode || "unified",
2741
2766
  step: "changelog",
2742
- currentVersion: rootPackage.version,
2743
2767
  newVersion: void 0,
2744
- pkg: void 0,
2745
- logLevel: config.logLevel
2768
+ pkg: rootPackageRead
2746
2769
  });
2747
- logger.debug(`Generating root changelog (${from}...${to})`);
2748
- logger.debug(`Root package: ${rootPackage.name} at ${rootPackage.path}`);
2749
- const rootCommits = await getPackageCommits({
2750
- pkg: rootPackage,
2751
- from,
2752
- to,
2770
+ const fromTag = bumpResult?.fromTag || from;
2771
+ const rootPackage = bumpResult?.rootPackage || await getRootPackage({
2753
2772
  config,
2754
- changelog: true
2773
+ force,
2774
+ suffix,
2775
+ changelog: true,
2776
+ from: fromTag,
2777
+ to
2755
2778
  });
2779
+ logger.debug(`Generating ${rootPackage.name} changelog (${fromTag}...${to})`);
2780
+ const newVersion = bumpResult?.newVersion || rootPackage.version;
2756
2781
  const rootChangelog = await generateChangelog({
2757
2782
  pkg: rootPackage,
2758
- commits: rootCommits,
2759
2783
  config,
2760
- from,
2761
- dryRun
2784
+ dryRun,
2785
+ newVersion
2762
2786
  });
2763
2787
  if (rootChangelog) {
2764
2788
  writeChangelogToFile({
2789
+ cwd: config.cwd,
2765
2790
  changelog: rootChangelog,
2766
2791
  pkg: rootPackage,
2767
2792
  dryRun
@@ -2791,56 +2816,57 @@ async function changelog(options = {}) {
2791
2816
  try {
2792
2817
  await executeHook("before:changelog", config, dryRun);
2793
2818
  logger.start("Start generating changelogs");
2794
- const packages = getPackagesToGenerateChangelogFor({
2795
- config,
2796
- bumpedPackages: options.bumpedPackages
2797
- });
2798
2819
  if (config.changelog?.rootChangelog && config.monorepo) {
2799
2820
  if (config.monorepo.versionMode === "independent") {
2821
+ const packages2 = await getPackagesToGenerateChangelogFor({
2822
+ config,
2823
+ bumpResult: options.bumpResult,
2824
+ suffix: options.suffix,
2825
+ force: options.force ?? false
2826
+ });
2800
2827
  await generateIndependentRootChangelog({
2801
- packages,
2828
+ packages: packages2,
2802
2829
  config,
2803
2830
  dryRun
2804
2831
  });
2805
2832
  } else {
2806
2833
  await generateSimpleRootChangelog({
2807
2834
  config,
2808
- dryRun
2835
+ dryRun,
2836
+ bumpResult: options.bumpResult,
2837
+ suffix: options.suffix,
2838
+ force: options.force ?? false
2809
2839
  });
2810
2840
  }
2811
2841
  } else {
2812
2842
  logger.debug("Skipping root changelog generation");
2813
2843
  }
2814
2844
  logger.debug("Generating package changelogs...");
2845
+ const packages = options.bumpResult?.bumpedPackages ? options.bumpResult.bumpedPackages : await getPackages({
2846
+ config,
2847
+ patterns: config.monorepo?.packages,
2848
+ suffix: options.suffix,
2849
+ force: options.force ?? false
2850
+ });
2815
2851
  logger.debug(`Processing ${packages.length} package(s)`);
2816
2852
  let generatedCount = 0;
2817
2853
  for await (const pkg of packages) {
2818
2854
  const { from, to } = await resolveTags({
2819
2855
  config,
2820
- versionMode: config.monorepo?.versionMode || "unified",
2821
2856
  step: "changelog",
2822
- currentVersion: pkg.version,
2823
- newVersion: void 0,
2824
2857
  pkg,
2825
- logLevel: config.logLevel
2858
+ newVersion: void 0
2826
2859
  });
2827
2860
  logger.debug(`Processing ${pkg.name} (${from}...${to})`);
2828
- const commits = await getPackageCommits({
2829
- pkg,
2830
- from,
2831
- to,
2832
- config,
2833
- changelog: true
2834
- });
2835
2861
  const changelog2 = await generateChangelog({
2836
2862
  pkg,
2837
- commits,
2838
2863
  config,
2839
- from,
2840
- dryRun
2864
+ dryRun,
2865
+ newVersion: options.bumpResult?.bumpedPackages?.find((p) => p.name === pkg.name)?.newVersion
2841
2866
  });
2842
2867
  if (changelog2) {
2843
2868
  writeChangelogToFile({
2869
+ cwd: config.cwd,
2844
2870
  pkg,
2845
2871
  changelog: changelog2,
2846
2872
  dryRun
@@ -2889,7 +2915,7 @@ async function providerRelease(options = {}) {
2889
2915
  configName: options.configName,
2890
2916
  baseConfig: options.config,
2891
2917
  overrides: {
2892
- from: options.from || (options.bumpResult?.bumped === true ? options.bumpResult.fromTag : void 0),
2918
+ from: options.from,
2893
2919
  to: options.to,
2894
2920
  tokens: {
2895
2921
  github: options.token,
@@ -2903,12 +2929,12 @@ async function providerRelease(options = {}) {
2903
2929
  logger.debug(`Dry run: ${dryRun}`);
2904
2930
  logger.info(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
2905
2931
  try {
2906
- await executeHook("before:provider-release", config, dryRun);
2907
2932
  const detectedProvider = options.provider || detectGitProvider();
2908
2933
  providerReleaseSafetyCheck({ config, provider: detectedProvider });
2934
+ await executeHook("before:provider-release", config, dryRun);
2909
2935
  logger.start("Start provider release");
2910
2936
  if (!detectedProvider) {
2911
- logger.warn("Unable to detect Git provider. Skipping release publication.");
2937
+ logger.fail("Unable to detect Git provider. Skipping release publication.");
2912
2938
  throw new Error("Unable to detect Git provider");
2913
2939
  } else {
2914
2940
  logger.info(
@@ -2917,19 +2943,21 @@ async function providerRelease(options = {}) {
2917
2943
  }
2918
2944
  let postedReleases = [];
2919
2945
  const payload = {
2920
- from: config.from,
2946
+ from: config.from || options.bumpResult?.fromTag,
2921
2947
  to: config.to,
2922
2948
  dryRun,
2923
2949
  config,
2924
2950
  logLevel: config.logLevel,
2925
- bumpResult: options.bumpResult
2951
+ bumpResult: options.bumpResult,
2952
+ force: options.force ?? false,
2953
+ suffix: options.suffix
2926
2954
  };
2927
2955
  if (detectedProvider === "github") {
2928
2956
  postedReleases = await github(payload);
2929
2957
  } else if (detectedProvider === "gitlab") {
2930
2958
  postedReleases = await gitlab(payload);
2931
2959
  } else {
2932
- logger.warn(`Unsupported Git provider: ${detectedProvider}`);
2960
+ logger.error(`Unsupported Git provider: ${detectedProvider}`);
2933
2961
  }
2934
2962
  await executeHook("success:provider-release", config, dryRun);
2935
2963
  return {
@@ -2973,17 +3001,17 @@ async function publish(options = {}) {
2973
3001
  }
2974
3002
  try {
2975
3003
  await executeHook("before:publish", config, dryRun);
3004
+ const rootPackage = readPackageJson(config.cwd);
2976
3005
  logger.start("Start publishing packages");
2977
- const packages = options.bumpedPackages || getPackages({
2978
- cwd: config.cwd,
3006
+ const packages = options.bumpedPackages || await getPackages({
3007
+ config,
2979
3008
  patterns: config.publish.packages ?? config.monorepo?.packages,
2980
- ignorePackageNames: config.monorepo?.ignorePackageNames
3009
+ suffix: options.suffix,
3010
+ force: options.force ?? false
2981
3011
  });
2982
- const rootPackage = getRootPackage(config.cwd);
2983
3012
  logger.debug(`Found ${packages.length} package(s)`);
2984
3013
  logger.debug("Building dependency graph and sorting...");
2985
- const packagesWithDeps = getPackagesWithDependencies(packages, config.bump.dependencyTypes);
2986
- const sortedPackages = topologicalSort(packagesWithDeps);
3014
+ const sortedPackages = topologicalSort(packages);
2987
3015
  let publishedPackages = packages || [];
2988
3016
  if (publishedPackages.length === 0 && config.monorepo?.versionMode === "independent") {
2989
3017
  logger.debug("Determining packages to publish in independent mode...");
@@ -3000,7 +3028,7 @@ async function publish(options = {}) {
3000
3028
  logger.info(`Publishing ${publishedPackages.length} package(s) (unified mode)`);
3001
3029
  }
3002
3030
  if (publishedPackages.length === 0) {
3003
- logger.warn("No packages need to be published");
3031
+ logger.fail("No packages need to be published");
3004
3032
  return;
3005
3033
  }
3006
3034
  await executeBuildCmd({
@@ -3009,7 +3037,7 @@ async function publish(options = {}) {
3009
3037
  });
3010
3038
  for (const pkg of sortedPackages) {
3011
3039
  if (publishedPackages.some((p) => p.name === pkg.name)) {
3012
- logger.debug(`Publishing ${pkg.name}@${pkg.version}...`);
3040
+ logger.debug(`Publishing ${getIndependentTag(pkg)}...`);
3013
3041
  await publishPackage({
3014
3042
  pkg,
3015
3043
  config,
@@ -3104,7 +3132,8 @@ async function release(options = {}) {
3104
3132
  config,
3105
3133
  force,
3106
3134
  clean: config.release.clean,
3107
- configName: options.configName
3135
+ configName: options.configName,
3136
+ suffix: options.suffix
3108
3137
  });
3109
3138
  if (!bumpResult.bumped) {
3110
3139
  logger.debug("No packages bumped");
@@ -3118,10 +3147,12 @@ async function release(options = {}) {
3118
3147
  dryRun,
3119
3148
  formatCmd: config.changelog.formatCmd,
3120
3149
  rootChangelog: config.changelog.rootChangelog,
3121
- bumpedPackages: bumpResult.bumpedPackages,
3150
+ bumpResult,
3122
3151
  config,
3123
3152
  logLevel: config.logLevel,
3124
- configName: options.configName
3153
+ configName: options.configName,
3154
+ force,
3155
+ suffix: options.suffix
3125
3156
  });
3126
3157
  } else {
3127
3158
  logger.info("Skipping changelog generation (--no-changelog)");
@@ -3164,7 +3195,9 @@ async function release(options = {}) {
3164
3195
  bumpedPackages: bumpResult.bumpedPackages,
3165
3196
  dryRun,
3166
3197
  config,
3167
- configName: options.configName
3198
+ configName: options.configName,
3199
+ suffix: options.suffix,
3200
+ force
3168
3201
  });
3169
3202
  } else {
3170
3203
  logger.info("Skipping publish (--no-publish)");
@@ -3184,7 +3217,9 @@ async function release(options = {}) {
3184
3217
  config,
3185
3218
  logLevel: config.logLevel,
3186
3219
  bumpResult,
3187
- configName: options.configName
3220
+ configName: options.configName,
3221
+ force,
3222
+ suffix: options.suffix
3188
3223
  });
3189
3224
  provider = response.detectedProvider;
3190
3225
  postedReleases = response.postedReleases;
@@ -3195,7 +3230,7 @@ async function release(options = {}) {
3195
3230
  logger.info("Skipping release (--no-provider-release)");
3196
3231
  }
3197
3232
  const publishedPackageCount = publishResponse?.publishedPackages.length ?? 0;
3198
- const versionDisplay = config.monorepo?.versionMode === "independent" ? `${bumpResult.bumpedPackages.length} packages bumped independently` : bumpResult.newVersion || getRootPackage(config.cwd).version;
3233
+ const versionDisplay = config.monorepo?.versionMode === "independent" ? `${bumpResult.bumpedPackages.length} packages bumped independently` : bumpResult.newVersion || readPackageJson(config.cwd).version;
3199
3234
  logger.box(`Release workflow completed!
3200
3235
 
3201
3236
  Version: ${versionDisplay}
@@ -3212,4 +3247,4 @@ Git provider: ${provider}`);
3212
3247
  }
3213
3248
  }
3214
3249
 
3215
- export { isStableReleaseType as $, github as A, createGitlabRelease as B, gitlab as C, getPackages as D, getPackageCommits as E, getRootPackage as F, hasLernaJson as G, getPackageToBump as H, detectPackageManager as I, determinePublishTag as J, getPackagesToPublishInSelectiveMode as K, getPackagesToPublishInIndependentMode as L, publishPackage as M, getLastRepoTag as N, getLastPackageTag as O, resolveTags as P, executeHook as Q, isInCI as R, getCIName as S, executeFormatCmd as T, executeBuildCmd as U, determineReleaseType as V, writeVersion as W, bumpPackageVersion as X, updateLernaVersion as Y, extractVersionFromPackageTag as Z, isPrerelease as _, providerRelease as a, isPrereleaseReleaseType as a0, isGraduating as a1, getPreid as a2, isChangedPreid as a3, bumpPackageIndependently as a4, confirmBump as a5, findPackagesWithCommitsAndCalculateVersions as a6, bumpIndependentPackages as a7, bump as b, changelog as c, publish as d, getDefaultConfig as e, defineConfig as f, generateChangelog as g, getPackageDependencies as h, getPackagesWithDependencies as i, getDependentsOf as j, expandPackagesToBumpWithDependents as k, loadRelizyConfig as l, getGitStatus as m, checkGitStatusIfDirty as n, fetchGitTags as o, providerReleaseSafetyCheck as p, detectGitProvider as q, release as r, parseGitRemoteUrl as s, topologicalSort as t, createCommitAndTags as u, pushCommitAndTags as v, writeChangelogToFile as w, getFirstCommit as x, getCurrentGitBranch as y, getCurrentGitRef as z };
3250
+ export { writeVersion as $, createGitlabRelease as A, gitlab as B, detectPackageManager as C, determinePublishTag as D, getPackagesToPublishInSelectiveMode as E, getPackagesToPublishInIndependentMode as F, publishPackage as G, readPackageJson as H, getRootPackage as I, readPackages as J, getPackages as K, getPackageCommits as L, hasLernaJson as M, getIndependentTag as N, getLastStableTag as O, getLastTag as P, getLastRepoTag as Q, getLastPackageTag as R, resolveTags as S, executeHook as T, isInCI as U, getCIName as V, executeFormatCmd as W, executeBuildCmd as X, isBumpedPackage as Y, determineSemverChange as Z, determineReleaseType as _, providerRelease as a, getPackageNewVersion as a0, updateLernaVersion as a1, extractVersionFromPackageTag as a2, isPrerelease as a3, isStableReleaseType as a4, isPrereleaseReleaseType as a5, isGraduating as a6, getPreid as a7, isChangedPreid as a8, getBumpedPackageIndependently as a9, confirmBump as aa, getBumpedIndependentPackages as ab, bump as b, changelog as c, publish as d, getDefaultConfig as e, defineConfig as f, generateChangelog as g, getPackageDependencies as h, getDependentsOf as i, expandPackagesToBumpWithDependents as j, getGitStatus as k, loadRelizyConfig as l, checkGitStatusIfDirty as m, fetchGitTags as n, detectGitProvider as o, providerReleaseSafetyCheck as p, parseGitRemoteUrl as q, release as r, createCommitAndTags as s, topologicalSort as t, pushCommitAndTags as u, getFirstCommit as v, writeChangelogToFile as w, getCurrentGitBranch as x, getCurrentGitRef as y, github as z };