relizy 0.2.3 → 0.2.5-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,407 @@ 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 = /^[AMDRC]\s+/.test(trimmedLine);
486
+ const R100 = /R100\s+/.test(trimmedLine);
487
+ return !isFileLine && !R100;
488
+ });
489
+ if (contentLines.length === 0) {
490
+ return "";
491
+ }
492
+ const indentedBody = contentLines.map((line) => ` ${line}`).join("\n");
493
+ return `
494
+
495
+ ${indentedBody}
496
+ `;
497
+ }
498
+ function formatCommit(commit, config) {
499
+ const body = config.changelog.includeCommitBody ? getCommitBody(commit) : "";
500
+ return `- ${commit.scope ? `**${commit.scope.trim()}:** ` : ""}${commit.isBreaking ? "\u26A0\uFE0F " : ""}${upperFirst(commit.description)}${formatReferences(commit.references, config)}${body}`;
501
+ }
502
+ function formatReferences(references, config) {
503
+ const pr = references.filter((ref) => ref.type === "pull-request");
504
+ const issue = references.filter((ref) => ref.type === "issue");
505
+ if (pr.length > 0 || issue.length > 0) {
506
+ return ` (${[...pr, ...issue].map((ref) => formatReference(ref, config.repo)).join(", ")})`;
507
+ }
508
+ if (references.length > 0) {
509
+ return ` (${formatReference(references[0], config.repo)})`;
510
+ }
511
+ return "";
512
+ }
513
+ function formatName(name = "") {
514
+ return name.split(" ").map((p) => upperFirst(p.trim())).join(" ");
515
+ }
516
+ function groupBy(items, key) {
517
+ const groups = {};
518
+ for (const item of items) {
519
+ groups[item[key]] = groups[item[key]] || [];
520
+ groups[item[key]]?.push(item);
521
+ }
522
+ return groups;
523
+ }
294
524
 
295
525
  function fromTagIsFirstCommit(fromTag, cwd) {
296
526
  return fromTag === getFirstCommit(cwd);
297
527
  }
298
528
  async function generateChangelog({
299
529
  pkg,
300
- commits,
301
530
  config,
302
- from,
303
- dryRun
531
+ dryRun,
532
+ newVersion
304
533
  }) {
305
- let fromTag = config.from || (config.monorepo?.versionMode === "independent" ? `${pkg.name}@${pkg.currentVersion}` : config.templates.tagBody.replace("{{newVersion}}", pkg.currentVersion)) || from;
534
+ let fromTag = config.from || pkg.fromTag || getFirstCommit(config.cwd);
306
535
  const isFirstCommit = fromTagIsFirstCommit(fromTag, config.cwd);
307
536
  if (isFirstCommit) {
308
- fromTag = config.monorepo?.versionMode === "independent" ? `${pkg.name}@0.0.0` : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
537
+ fromTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: "0.0.0", name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", "0.0.0");
538
+ }
539
+ newVersion = newVersion || pkg.newVersion;
540
+ let toTag = config.to;
541
+ if (!toTag && newVersion) {
542
+ toTag = config.monorepo?.versionMode === "independent" ? getIndependentTag({ version: newVersion, name: pkg.name }) : config.templates.tagBody.replace("{{newVersion}}", newVersion);
543
+ }
544
+ if (!toTag) {
545
+ throw new Error(`No tag found for ${pkg.name}`);
309
546
  }
310
- const toTag = config.to || (config.monorepo?.versionMode === "independent" ? `${pkg.name}@${pkg.version}` : config.templates.tagBody.replace("{{newVersion}}", pkg.version));
311
547
  logger.debug(`Generating changelog for ${pkg.name} - from ${fromTag} to ${toTag}`);
312
548
  try {
313
549
  config = {
@@ -316,25 +552,26 @@ async function generateChangelog({
316
552
  to: toTag
317
553
  };
318
554
  const generatedChangelog = await generateMarkDown({
319
- commits,
555
+ commits: pkg.commits,
320
556
  config,
321
557
  from: fromTag,
558
+ isFirstCommit,
322
559
  to: toTag
323
560
  });
324
561
  let changelog = generatedChangelog;
325
- if (commits.length === 0) {
562
+ if (pkg.commits.length === 0) {
326
563
  changelog = `${changelog}
327
564
 
328
565
  ${config.templates.emptyChangelogContent}`;
329
566
  }
330
567
  const changelogResult = await executeHook("generate:changelog", config, dryRun, {
331
- commits,
568
+ commits: pkg.commits,
332
569
  changelog
333
570
  });
334
571
  changelog = changelogResult || changelog;
335
572
  logger.verbose(`Output changelog for ${pkg.name}:
336
573
  ${changelog}`);
337
- logger.debug(`Changelog generated for ${pkg.name} (${commits.length} commits)`);
574
+ logger.debug(`Changelog generated for ${pkg.name} (${pkg.commits.length} commits)`);
338
575
  logger.verbose(`Final changelog for ${pkg.name}:
339
576
 
340
577
  ${changelog}
@@ -349,6 +586,7 @@ ${changelog}
349
586
  }
350
587
  }
351
588
  function writeChangelogToFile({
589
+ cwd,
352
590
  pkg,
353
591
  changelog,
354
592
  dryRun = false
@@ -372,11 +610,12 @@ ${changelog}
372
610
  ${existingChangelog}`;
373
611
  }
374
612
  if (dryRun) {
375
- logger.info(`[dry-run] ${pkg.name} - Write changelog to ${changelogPath}`);
613
+ const relativeChangelogPath = relative(cwd, changelogPath);
614
+ logger.info(`[dry-run] ${pkg.name} - Write changelog to ${relativeChangelogPath}`);
376
615
  } else {
377
616
  logger.debug(`Writing changelog to ${changelogPath}`);
378
617
  writeFileSync(changelogPath, updatedChangelog, "utf8");
379
- logger.info(`Changelog updated for ${pkg.name}`);
618
+ logger.info(`Changelog updated for ${pkg.name} (${"newVersion" in pkg && pkg.newVersion || pkg.version})`);
380
619
  }
381
620
  }
382
621
 
@@ -399,7 +638,7 @@ function getDefaultConfig() {
399
638
  },
400
639
  templates: {
401
640
  commitMessage: "chore(release): bump version to {{newVersion}}",
402
- tagMessage: "Bump version to v{{newVersion}}",
641
+ tagMessage: "Bump version to {{newVersion}}",
403
642
  tagBody: "v{{newVersion}}",
404
643
  emptyChangelogContent: "No relevant changes for this release"
405
644
  },
@@ -490,7 +729,11 @@ function defineConfig(config) {
490
729
  return config;
491
730
  }
492
731
 
493
- function getPackageDependencies(packagePath, allPackageNames, dependencyTypes) {
732
+ function getPackageDependencies({
733
+ packagePath,
734
+ allPackageNames,
735
+ dependencyTypes
736
+ }) {
494
737
  const packageJsonPath = join(packagePath, "package.json");
495
738
  if (!existsSync(packageJsonPath)) {
496
739
  return [];
@@ -509,32 +752,26 @@ function getPackageDependencies(packagePath, allPackageNames, dependencyTypes) {
509
752
  }
510
753
  return deps;
511
754
  }
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) {
755
+ function getDependentsOf({
756
+ allPackages,
757
+ packageName
758
+ }) {
520
759
  return allPackages.filter(
521
760
  (pkg) => pkg.dependencies.includes(packageName)
522
761
  );
523
762
  }
524
763
  function expandPackagesToBumpWithDependents({
525
- packagesWithCommits,
526
764
  allPackages,
527
- dependencyTypes
765
+ packagesWithCommits
528
766
  }) {
529
- const packagesWithDeps = getPackagesWithDependencies(allPackages, dependencyTypes);
530
767
  const result = /* @__PURE__ */ new Map();
531
768
  logger.debug(`Expanding packages to bump: ${packagesWithCommits.length} packages with commits, ${allPackages.length} total packages`);
532
769
  for (const pkg of packagesWithCommits) {
533
- result.set(pkg.name, {
770
+ const packageToBump = {
534
771
  ...pkg,
535
- reason: "commits",
536
- commits: pkg.commits
537
- });
772
+ reason: "commits"
773
+ };
774
+ result.set(pkg.name, packageToBump);
538
775
  }
539
776
  const toProcess = [...packagesWithCommits.map((p) => p.name)];
540
777
  const processed = /* @__PURE__ */ new Set();
@@ -544,19 +781,22 @@ function expandPackagesToBumpWithDependents({
544
781
  continue;
545
782
  }
546
783
  processed.add(currentPkgName);
547
- const dependents = getDependentsOf(currentPkgName, packagesWithDeps);
784
+ const dependents = getDependentsOf({
785
+ packageName: currentPkgName,
786
+ allPackages
787
+ });
548
788
  for (const dependent of dependents) {
549
789
  if (!result.has(dependent.name)) {
550
790
  const currentChain = result.get(currentPkgName)?.dependencyChain || [];
551
791
  const chain = [...currentChain, currentPkgName];
552
- const packageInfo = allPackages.find((p) => p.name === dependent.name);
553
- if (packageInfo) {
554
- result.set(dependent.name, {
555
- ...packageInfo,
792
+ const packageBase = allPackages.find((p) => p.name === dependent.name);
793
+ if (packageBase) {
794
+ const packageToBump = {
795
+ ...packageBase,
556
796
  reason: "dependency",
557
- dependencyChain: chain,
558
- commits: []
559
- });
797
+ dependencyChain: chain
798
+ };
799
+ result.set(dependent.name, packageToBump);
560
800
  toProcess.push(dependent.name);
561
801
  logger.debug(`${dependent.name} will be bumped (depends on ${chain.join(" \u2192 ")})`);
562
802
  }
@@ -577,7 +817,7 @@ function topologicalSort(packages) {
577
817
  if (visited.has(pkgName))
578
818
  return;
579
819
  if (visiting.has(pkgName)) {
580
- logger.warn(`Circular dependency detected involving ${pkgName}`);
820
+ logger.fail(`Circular dependency detected involving ${pkgName}`);
581
821
  return;
582
822
  }
583
823
  visiting.add(pkgName);
@@ -597,229 +837,12 @@ function topologicalSort(packages) {
597
837
  return sorted;
598
838
  }
599
839
 
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
840
  async function githubIndependentMode({
820
841
  config,
821
842
  dryRun,
822
- bumpedPackages
843
+ bumpResult,
844
+ force,
845
+ suffix
823
846
  }) {
824
847
  const repoConfig = config.repo;
825
848
  if (!repoConfig) {
@@ -829,42 +852,36 @@ async function githubIndependentMode({
829
852
  if (!config.tokens.github && !config.repo?.token) {
830
853
  throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
831
854
  }
832
- const packages = bumpedPackages || getPackages({
833
- cwd: config.cwd,
855
+ const packages = bumpResult?.bumped && bumpResult?.bumpedPackages || await getPackages({
856
+ suffix,
834
857
  patterns: config.monorepo?.packages,
835
- ignorePackageNames: config.monorepo?.ignorePackageNames
858
+ config,
859
+ force
836
860
  });
837
861
  logger.info(`Creating ${packages.length} GitHub release(s)`);
838
862
  const postedReleases = [];
839
863
  for (const pkg of packages) {
840
- const to = `${pkg.name}@${pkg.version}`;
841
- const from = pkg.fromTag;
864
+ const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
865
+ const from = config.from || pkg.fromTag;
866
+ const to = config.to || getIndependentTag({ version: newVersion, name: pkg.name });
842
867
  if (!from) {
843
- logger.warn(`No fromTag found for ${pkg.name}, skipping release`);
868
+ logger.warn(`No from tag found for ${pkg.name}, skipping release`);
844
869
  continue;
845
870
  }
846
871
  const toTag = dryRun ? "HEAD" : to;
847
872
  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
873
  const changelog = await generateChangelog({
856
874
  pkg,
857
- commits,
858
875
  config,
859
- from,
860
- dryRun
876
+ dryRun,
877
+ newVersion
861
878
  });
862
879
  const releaseBody = changelog.split("\n").slice(2).join("\n");
863
880
  const release = {
864
881
  tag_name: to,
865
882
  name: to,
866
883
  body: releaseBody,
867
- prerelease: isPrerelease(pkg.version)
884
+ prerelease: isPrerelease(newVersion)
868
885
  };
869
886
  logger.debug(`Creating release for ${to}${release.prerelease ? " (prerelease)" : ""}`);
870
887
  if (dryRun) {
@@ -872,7 +889,7 @@ async function githubIndependentMode({
872
889
  postedReleases.push({
873
890
  name: pkg.name,
874
891
  tag: release.tag_name,
875
- version: pkg.version,
892
+ version: newVersion,
876
893
  prerelease: release.prerelease
877
894
  });
878
895
  } else {
@@ -886,7 +903,7 @@ async function githubIndependentMode({
886
903
  postedReleases.push({
887
904
  name: pkg.name,
888
905
  tag: release.tag_name,
889
- version: pkg.version,
906
+ version: newVersion,
890
907
  prerelease: release.prerelease
891
908
  });
892
909
  }
@@ -912,21 +929,12 @@ async function githubUnified({
912
929
  if (!config.tokens.github && !config.repo?.token) {
913
930
  throw new Error("No GitHub token specified. Set GITHUB_TOKEN or GH_TOKEN environment variable.");
914
931
  }
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
- });
932
+ const to = config.to || config.templates.tagBody.replace("{{newVersion}}", bumpResult?.bumped && bumpResult.newVersion || rootPackage.version);
924
933
  const changelog = await generateChangelog({
925
934
  pkg: rootPackage,
926
- commits,
927
935
  config,
928
- from: bumpResult?.fromTag || "v0.0.0",
929
- dryRun
936
+ dryRun,
937
+ newVersion: bumpResult?.newVersion || rootPackage.version
930
938
  });
931
939
  const releaseBody = changelog.split("\n").slice(2).join("\n");
932
940
  const release = {
@@ -947,7 +955,7 @@ async function githubUnified({
947
955
  logger.debug("Publishing release to GitHub...");
948
956
  await createGithubRelease({
949
957
  ...config,
950
- from: bumpResult?.fromTag || "v0.0.0",
958
+ from: bumpResult?.bumped && bumpResult.fromTag || "v0.0.0",
951
959
  to,
952
960
  repo: repoConfig
953
961
  }, release);
@@ -960,7 +968,7 @@ async function githubUnified({
960
968
  prerelease: release.prerelease
961
969
  }];
962
970
  }
963
- async function github(options = {}) {
971
+ async function github(options) {
964
972
  try {
965
973
  const dryRun = options.dryRun ?? false;
966
974
  logger.debug(`Dry run: ${dryRun}`);
@@ -976,18 +984,30 @@ async function github(options = {}) {
976
984
  }
977
985
  }
978
986
  });
979
- if (!options.bumpResult?.bumped) {
980
- logger.warn("No bump result found, skipping release");
981
- return [];
982
- }
983
987
  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;
988
+ return await githubIndependentMode({
989
+ config,
990
+ dryRun,
991
+ bumpResult: options.bumpResult,
992
+ force: options.force ?? false,
993
+ suffix: options.suffix
994
+ });
990
995
  }
996
+ const rootPackageBase = readPackageJson(config.cwd);
997
+ const { from, to } = await resolveTags({
998
+ config,
999
+ step: "provider-release",
1000
+ newVersion: options.bumpResult?.newVersion || rootPackageBase.version,
1001
+ pkg: rootPackageBase
1002
+ });
1003
+ const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
1004
+ config,
1005
+ force: options.force ?? false,
1006
+ suffix: options.suffix,
1007
+ changelog: true,
1008
+ from,
1009
+ to
1010
+ });
991
1011
  return await githubUnified({
992
1012
  config,
993
1013
  dryRun,
@@ -1064,13 +1084,16 @@ async function createGitlabRelease({
1064
1084
  async function gitlabIndependentMode({
1065
1085
  config,
1066
1086
  dryRun,
1067
- bumpedPackages
1087
+ bumpResult,
1088
+ suffix,
1089
+ force
1068
1090
  }) {
1069
1091
  logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
1070
- const packages = bumpedPackages || getPackages({
1071
- cwd: config.cwd,
1092
+ const packages = bumpResult?.bumped && bumpResult?.bumpedPackages || await getPackages({
1072
1093
  patterns: config.monorepo?.packages,
1073
- ignorePackageNames: config.monorepo?.ignorePackageNames
1094
+ config,
1095
+ suffix,
1096
+ force
1074
1097
  });
1075
1098
  logger.info(`Creating ${packages.length} GitLab release(s) for independent packages`);
1076
1099
  logger.debug("Getting current branch...");
@@ -1082,26 +1105,19 @@ async function gitlabIndependentMode({
1082
1105
  });
1083
1106
  const postedReleases = [];
1084
1107
  for (const pkg of packages) {
1085
- const to = `${pkg.name}@${pkg.version}`;
1086
- const from = pkg.fromTag;
1108
+ const newVersion = isBumpedPackage(pkg) && pkg.newVersion || pkg.version;
1109
+ const from = config.from || pkg.fromTag;
1110
+ const to = getIndependentTag({ version: newVersion, name: pkg.name });
1087
1111
  if (!from) {
1088
- logger.warn(`No fromTag found for ${pkg.name}, skipping release`);
1112
+ logger.warn(`No from tag found for ${pkg.name}, skipping release`);
1089
1113
  continue;
1090
1114
  }
1091
1115
  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
1116
  const changelog = await generateChangelog({
1100
1117
  pkg,
1101
- commits,
1102
1118
  config,
1103
- from,
1104
- dryRun
1119
+ dryRun,
1120
+ newVersion
1105
1121
  });
1106
1122
  if (!changelog) {
1107
1123
  logger.warn(`No changelog found for ${pkg.name}`);
@@ -1114,335 +1130,186 @@ async function gitlabIndependentMode({
1114
1130
  description: releaseBody,
1115
1131
  ref: currentBranch.trim()
1116
1132
  };
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}`);
1133
+ logger.debug(`Creating release for ${to} (ref: ${release.ref})`);
1134
+ if (dryRun) {
1135
+ logger.info(`[dry-run] Publish GitLab release for ${to}`);
1136
+ } else {
1137
+ logger.debug(`Publishing release ${to} to GitLab...`);
1138
+ await createGitlabRelease({
1139
+ config,
1140
+ release,
1141
+ dryRun
1142
+ });
1143
+ postedReleases.push({
1144
+ name: pkg.name,
1145
+ tag: release.tag_name,
1146
+ version: newVersion,
1147
+ prerelease: isPrerelease(newVersion)
1148
+ });
1149
+ }
1381
1150
  }
1151
+ if (postedReleases.length === 0) {
1152
+ logger.warn("No releases created");
1153
+ } else {
1154
+ logger.success(`Releases ${postedReleases.map((r) => r.tag).join(", ")} published to GitLab!`);
1155
+ }
1156
+ return postedReleases;
1382
1157
  }
1383
- function hasLernaJson(rootDir) {
1384
- const lernaJsonPath = join(rootDir, "lerna.json");
1385
- return existsSync(lernaJsonPath);
1386
- }
1387
- async function getPackageToBump({
1388
- packages,
1158
+ async function gitlabUnified({
1389
1159
  config,
1390
- from,
1391
- to
1160
+ dryRun,
1161
+ rootPackage,
1162
+ bumpResult
1392
1163
  }) {
1393
- const packagesWithCommits = [];
1394
- for (const pkg of packages) {
1395
- const commits = await getPackageCommits({
1396
- pkg,
1397
- from,
1398
- to,
1164
+ logger.debug(`GitLab token: ${config.tokens.gitlab || config.repo?.token ? "\u2713 provided" : "\u2717 missing"}`);
1165
+ const to = config.templates.tagBody.replace("{{newVersion}}", rootPackage.newVersion || rootPackage.version);
1166
+ const changelog = await generateChangelog({
1167
+ pkg: rootPackage,
1168
+ config,
1169
+ dryRun,
1170
+ newVersion: bumpResult?.newVersion || rootPackage.version
1171
+ });
1172
+ const releaseBody = changelog.split("\n").slice(2).join("\n");
1173
+ logger.debug("Getting current branch...");
1174
+ const { stdout: currentBranch } = await execPromise("git rev-parse --abbrev-ref HEAD", {
1175
+ noSuccess: true,
1176
+ noStdout: true,
1177
+ logLevel: config.logLevel,
1178
+ cwd: config.cwd
1179
+ });
1180
+ const release = {
1181
+ tag_name: to,
1182
+ name: to,
1183
+ description: releaseBody,
1184
+ ref: currentBranch.trim()
1185
+ };
1186
+ logger.info(`Creating release for ${to} (ref: ${release.ref})`);
1187
+ logger.debug("Release details:", formatJson({
1188
+ tag_name: release.tag_name,
1189
+ name: release.name,
1190
+ ref: release.ref
1191
+ }));
1192
+ if (dryRun) {
1193
+ logger.info("[dry-run] Publish GitLab release for", release.tag_name);
1194
+ } else {
1195
+ logger.debug("Publishing release to GitLab...");
1196
+ await createGitlabRelease({
1399
1197
  config,
1400
- changelog: false
1198
+ release,
1199
+ dryRun
1200
+ });
1201
+ }
1202
+ logger.success(`Release ${to} published to GitLab!`);
1203
+ return [{
1204
+ name: to,
1205
+ tag: to,
1206
+ version: to,
1207
+ prerelease: isPrerelease(rootPackage.version)
1208
+ }];
1209
+ }
1210
+ async function gitlab(options = {}) {
1211
+ try {
1212
+ const dryRun = options.dryRun ?? false;
1213
+ logger.debug(`Dry run: ${dryRun}`);
1214
+ const config = await loadRelizyConfig({
1215
+ configName: options.configName,
1216
+ baseConfig: options.config,
1217
+ overrides: {
1218
+ from: options.from,
1219
+ to: options.to,
1220
+ logLevel: options.logLevel,
1221
+ tokens: {
1222
+ gitlab: options.token
1223
+ }
1224
+ }
1401
1225
  });
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 });
1226
+ if (config.monorepo?.versionMode === "independent") {
1227
+ return await gitlabIndependentMode({
1228
+ config,
1229
+ dryRun,
1230
+ bumpResult: options.bumpResult,
1231
+ suffix: options.suffix,
1232
+ force: options.force ?? false
1233
+ });
1405
1234
  }
1235
+ const rootPackageBase = readPackageJson(config.cwd);
1236
+ const { from, to } = await resolveTags({
1237
+ config,
1238
+ step: "provider-release",
1239
+ newVersion: options.bumpResult?.newVersion || rootPackageBase.version,
1240
+ pkg: rootPackageBase
1241
+ });
1242
+ const rootPackage = options.bumpResult?.rootPackage || await getRootPackage({
1243
+ config,
1244
+ force: options.force ?? false,
1245
+ suffix: options.suffix,
1246
+ changelog: true,
1247
+ from,
1248
+ to
1249
+ });
1250
+ logger.debug(`Root package: ${getIndependentTag(rootPackage)}`);
1251
+ return await gitlabUnified({
1252
+ config,
1253
+ dryRun,
1254
+ rootPackage,
1255
+ bumpResult: options.bumpResult
1256
+ });
1257
+ } catch (error) {
1258
+ logger.error("Error publishing GitLab release:", error);
1259
+ throw error;
1406
1260
  }
1407
- const allPackagesToBump = expandPackagesToBumpWithDependents({
1408
- allPackages: packages,
1409
- packagesWithCommits,
1410
- dependencyTypes: config.bump?.dependencyTypes
1411
- });
1412
- return allPackagesToBump;
1413
1261
  }
1414
1262
 
1415
- function detectReleaseTypeFromCommits(commits, config) {
1416
- return determineSemverChange(commits, config);
1263
+ function determineSemverChange(commits, types) {
1264
+ let [hasMajor, hasMinor, hasPatch] = [false, false, false];
1265
+ for (const commit of commits) {
1266
+ const commitType = types[commit.type];
1267
+ if (!commitType) {
1268
+ continue;
1269
+ }
1270
+ const semverType = commitType.semver;
1271
+ if (semverType === "major" || commit.isBreaking) {
1272
+ hasMajor = true;
1273
+ } else if (semverType === "minor") {
1274
+ hasMinor = true;
1275
+ } else if (semverType === "patch") {
1276
+ hasPatch = true;
1277
+ }
1278
+ }
1279
+ return hasMajor ? "major" : hasMinor ? "minor" : hasPatch ? "patch" : void 0;
1280
+ }
1281
+ function detectReleaseTypeFromCommits(commits, types) {
1282
+ return determineSemverChange(commits, types);
1417
1283
  }
1418
1284
  function validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType) {
1419
1285
  if (configuredType !== "prerelease" || !targetPreid || !isPrerelease(currentVersion)) {
1420
1286
  return;
1421
1287
  }
1422
1288
  const testVersion = semver.inc(currentVersion, "prerelease", targetPreid);
1423
- if (testVersion && !semver.gt(testVersion, currentVersion)) {
1289
+ const isNotUpgrade = testVersion && !semver.gt(testVersion, currentVersion);
1290
+ if (isNotUpgrade) {
1424
1291
  throw new Error(`Unable to graduate from ${currentVersion} to ${testVersion}, it's not a valid prerelease`);
1425
1292
  }
1426
1293
  }
1427
- function handleStableVersionWithReleaseType(commits, config, force) {
1294
+ function handleStableVersionWithReleaseType(commits, types, force) {
1428
1295
  if (!commits?.length && !force) {
1429
1296
  logger.debug('No commits found for stable version with "release" type, skipping bump');
1430
- return null;
1297
+ return void 0;
1431
1298
  }
1432
- const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, config) : null;
1299
+ const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
1433
1300
  if (!detectedType && !force) {
1434
1301
  logger.debug("No significant commits found, skipping bump");
1435
- return null;
1302
+ return void 0;
1436
1303
  }
1437
1304
  logger.debug(`Auto-detected release type from commits: ${detectedType}`);
1438
1305
  return detectedType;
1439
1306
  }
1440
- function handleStableVersionWithPrereleaseType(commits, config, force) {
1307
+ function handleStableVersionWithPrereleaseType(commits, types, force) {
1441
1308
  if (!commits?.length && !force) {
1442
1309
  logger.debug('No commits found for stable version with "prerelease" type, skipping bump');
1443
- return null;
1310
+ return void 0;
1444
1311
  }
1445
- const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, config) : null;
1312
+ const detectedType = commits?.length ? detectReleaseTypeFromCommits(commits, types) : void 0;
1446
1313
  if (!detectedType) {
1447
1314
  logger.debug("No significant commits found, using prepatch as default");
1448
1315
  return "prepatch";
@@ -1455,83 +1322,73 @@ function handlePrereleaseVersionToStable(currentVersion) {
1455
1322
  logger.debug(`Graduating from prerelease ${currentVersion} to stable release`);
1456
1323
  return "release";
1457
1324
  }
1458
- function handlePrereleaseVersionWithPrereleaseType(currentVersion, targetPreid, commits, config, force) {
1325
+ function handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force }) {
1459
1326
  const currentPreid = getPreid(currentVersion);
1460
- const hasChangedPreid = targetPreid && currentPreid && currentPreid !== targetPreid;
1327
+ const hasChangedPreid = preid && currentPreid && currentPreid !== preid;
1461
1328
  if (hasChangedPreid) {
1462
- const testVersion = semver.inc(currentVersion, "prerelease", targetPreid);
1329
+ const testVersion = semver.inc(currentVersion, "prerelease", preid);
1463
1330
  if (!testVersion) {
1464
- throw new Error(`Unable to change preid from ${currentPreid} to ${targetPreid} for version ${currentVersion}`);
1331
+ throw new Error(`Unable to change preid from ${currentPreid} to ${preid} for version ${currentVersion}`);
1465
1332
  }
1466
1333
  const isUpgrade = semver.gt(testVersion, currentVersion);
1467
1334
  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})`);
1335
+ throw new Error(`Unable to change preid from ${currentVersion} to ${testVersion}, it's not a valid upgrade (cannot downgrade from ${currentPreid} to ${preid})`);
1469
1336
  }
1470
1337
  return "prerelease";
1471
1338
  }
1472
1339
  if (!commits?.length && !force) {
1473
1340
  logger.debug("No commits found for prerelease version, skipping bump");
1474
- return null;
1341
+ return void 0;
1475
1342
  }
1476
1343
  logger.debug(`Incrementing prerelease version: ${currentVersion}`);
1477
1344
  return "prerelease";
1478
1345
  }
1479
- function handleExplicitReleaseType(configuredType, currentVersion) {
1346
+ function handleExplicitReleaseType({
1347
+ releaseType,
1348
+ currentVersion
1349
+ }) {
1480
1350
  const isCurrentPrerelease = isPrerelease(currentVersion);
1481
- const isGraduatingToStable = isCurrentPrerelease && isStableReleaseType(configuredType);
1351
+ const isGraduatingToStable = isCurrentPrerelease && isStableReleaseType(releaseType);
1482
1352
  if (isGraduatingToStable) {
1483
- logger.debug(`Graduating from prerelease ${currentVersion} to stable with type: ${configuredType}`);
1353
+ logger.debug(`Graduating from prerelease ${currentVersion} to stable with type: ${releaseType}`);
1484
1354
  } else {
1485
- logger.debug(`Using explicit release type: ${configuredType}`);
1355
+ logger.debug(`Using explicit release type: ${releaseType}`);
1486
1356
  }
1487
- return configuredType;
1357
+ return releaseType;
1488
1358
  }
1489
1359
  function determineReleaseType({
1490
1360
  currentVersion,
1491
- from,
1492
- to,
1493
1361
  commits,
1494
- config,
1362
+ releaseType,
1363
+ preid,
1364
+ types,
1495
1365
  force
1496
1366
  }) {
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) {
1367
+ if (releaseType === "release" && preid) {
1505
1368
  throw new Error('You cannot use a "release" type with a "preid", to use a preid you must use a "prerelease" type');
1506
1369
  }
1507
- validatePrereleaseDowngrade(currentVersion, targetPreid, configuredType);
1370
+ validatePrereleaseDowngrade(currentVersion, preid, releaseType);
1508
1371
  if (force) {
1509
- logger.debug(`Force flag enabled, using configured type: ${configuredType}`);
1510
- return configuredType;
1372
+ logger.debug(`Force flag enabled, using configured type: ${releaseType}`);
1373
+ return releaseType;
1511
1374
  }
1512
1375
  const isCurrentPrerelease = isPrerelease(currentVersion);
1513
1376
  if (!isCurrentPrerelease) {
1514
- if (configuredType === "release") {
1515
- return handleStableVersionWithReleaseType(commits, configWithRange, force);
1377
+ if (releaseType === "release") {
1378
+ return handleStableVersionWithReleaseType(commits, types, force);
1516
1379
  }
1517
- if (configuredType === "prerelease") {
1518
- return handleStableVersionWithPrereleaseType(commits, configWithRange, force);
1380
+ if (releaseType === "prerelease") {
1381
+ return handleStableVersionWithPrereleaseType(commits, types, force);
1519
1382
  }
1520
- return handleExplicitReleaseType(configuredType, currentVersion);
1383
+ return handleExplicitReleaseType({ releaseType, currentVersion });
1521
1384
  }
1522
- if (configuredType === "release") {
1385
+ if (releaseType === "release") {
1523
1386
  return handlePrereleaseVersionToStable(currentVersion);
1524
1387
  }
1525
- if (configuredType === "prerelease") {
1526
- return handlePrereleaseVersionWithPrereleaseType(
1527
- currentVersion,
1528
- targetPreid,
1529
- commits,
1530
- configWithRange,
1531
- force
1532
- );
1388
+ if (releaseType === "prerelease") {
1389
+ return handlePrereleaseVersionWithPrereleaseType({ currentVersion, preid, commits, force });
1533
1390
  }
1534
- return handleExplicitReleaseType(configuredType, currentVersion);
1391
+ return handleExplicitReleaseType({ releaseType, currentVersion });
1535
1392
  }
1536
1393
  function writeVersion(pkgPath, version, dryRun = false) {
1537
1394
  const packageJsonPath = join(pkgPath, "package.json");
@@ -1552,33 +1409,32 @@ function writeVersion(pkgPath, version, dryRun = false) {
1552
1409
  throw new Error(`Unable to write version to ${packageJsonPath}: ${error}`);
1553
1410
  }
1554
1411
  }
1555
- function bumpPackageVersion({
1412
+ function getPackageNewVersion({
1556
1413
  currentVersion,
1557
1414
  releaseType,
1558
1415
  preid,
1559
1416
  suffix
1560
1417
  }) {
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}"
1418
+ let newVersion = semver.inc(currentVersion, releaseType, preid);
1419
+ if (!newVersion) {
1420
+ throw new Error(`Unable to bump version "${currentVersion}" with release type "${releaseType}"
1565
1421
 
1566
1422
  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
1423
  }
1424
+ if (isPrereleaseReleaseType(releaseType) && suffix) {
1425
+ newVersion = newVersion.replace(/\.(\d+)$/, `.${suffix}`);
1426
+ }
1427
+ const isValidVersion = semver.gt(newVersion, currentVersion);
1428
+ if (!isValidVersion) {
1429
+ throw new Error(`Unable to bump version "${currentVersion}" to "${newVersion}", new version is not greater than current version`);
1430
+ }
1431
+ if (isGraduating(currentVersion, releaseType)) {
1432
+ logger.info(`Graduating from prerelease ${currentVersion} to stable ${newVersion}`);
1433
+ }
1434
+ if (isChangedPreid(currentVersion, preid)) {
1435
+ logger.info(`Graduating from ${getPreid(currentVersion)} to ${preid}`);
1436
+ }
1437
+ return newVersion;
1582
1438
  }
1583
1439
  function updateLernaVersion({
1584
1440
  rootDir,
@@ -1612,7 +1468,7 @@ function updateLernaVersion({
1612
1468
  `, "utf8");
1613
1469
  logger.success(`Updated lerna.json: ${oldVersion} \u2192 ${version}`);
1614
1470
  } catch (error) {
1615
- logger.warn(`Unable to update lerna.json: ${error}`);
1471
+ logger.fail(`Unable to update lerna.json: ${error}`);
1616
1472
  }
1617
1473
  }
1618
1474
  function extractVersionFromPackageTag(tag) {
@@ -1658,13 +1514,16 @@ function isChangedPreid(currentVersion, targetPreid) {
1658
1514
  }
1659
1515
  return currentPreid !== targetPreid;
1660
1516
  }
1661
- function bumpPackageIndependently({
1517
+ function getBumpedPackageIndependently({
1662
1518
  pkg,
1663
1519
  dryRun
1664
1520
  }) {
1665
1521
  logger.debug(`Analyzing ${pkg.name}`);
1666
1522
  const currentVersion = pkg.version || "0.0.0";
1667
- const newVersion = pkg.version;
1523
+ const newVersion = pkg.newVersion;
1524
+ if (!newVersion) {
1525
+ return { bumped: false };
1526
+ }
1668
1527
  logger.debug(`Bumping ${pkg.name} from ${currentVersion} to ${newVersion}`);
1669
1528
  writeVersion(pkg.path, newVersion, dryRun);
1670
1529
  return { bumped: true, newVersion, oldVersion: currentVersion };
@@ -1690,9 +1549,9 @@ function displayUnifiedModePackages({
1690
1549
  newVersion,
1691
1550
  force
1692
1551
  }) {
1693
- logger.log(`${packages.length} package(s)${force ? " (force)" : ""}:`);
1552
+ logger.log(`${packages.length} package(s):`);
1694
1553
  packages.forEach((pkg) => {
1695
- logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion}`);
1554
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} ${force ? "(force)" : ""}`);
1696
1555
  });
1697
1556
  logger.log("");
1698
1557
  }
@@ -1702,14 +1561,15 @@ function displaySelectiveModePackages({
1702
1561
  force
1703
1562
  }) {
1704
1563
  if (force) {
1705
- logger.log(`${packages.length} package(s) (force):`);
1564
+ logger.log(`${packages.length} package(s):`);
1706
1565
  packages.forEach((pkg) => {
1707
- logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion}`);
1566
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion} (force)`);
1708
1567
  });
1709
1568
  logger.log("");
1710
1569
  } else {
1711
1570
  const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
1712
1571
  const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
1572
+ const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
1713
1573
  if (packagesWithCommits.length > 0) {
1714
1574
  logger.log(`${packagesWithCommits.length} package(s) with commits:`);
1715
1575
  packagesWithCommits.forEach((pkg) => {
@@ -1724,33 +1584,47 @@ function displaySelectiveModePackages({
1724
1584
  });
1725
1585
  logger.log("");
1726
1586
  }
1587
+ if (packagesAsGraduation.length > 0) {
1588
+ logger.log(`${packagesAsGraduation.length} graduation package(s):`);
1589
+ packagesAsGraduation.forEach((pkg) => {
1590
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${newVersion}`);
1591
+ });
1592
+ logger.log("");
1593
+ }
1727
1594
  }
1728
1595
  }
1729
1596
  function displayIndependentModePackages({
1730
1597
  packages,
1731
- force,
1732
- dryRun
1598
+ force
1733
1599
  }) {
1734
1600
  if (force) {
1735
- logger.log(`${dryRun ? "[dry-run] " : ""}${packages.length} package(s) (force):`);
1601
+ logger.log(`${packages.length} package(s):`);
1736
1602
  packages.forEach((pkg) => {
1737
- logger.log(` \u2022 ${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.version}`);
1603
+ logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion} (force)`);
1738
1604
  });
1739
1605
  logger.log("");
1740
1606
  } else {
1741
1607
  const packagesWithCommits = packages.filter((p) => "reason" in p && p.reason === "commits");
1742
1608
  const packagesAsDependents = packages.filter((p) => "reason" in p && p.reason === "dependency");
1609
+ const packagesAsGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation");
1743
1610
  if (packagesWithCommits.length > 0) {
1744
- logger.log(`${dryRun ? "[dry-run] " : ""}${packagesWithCommits.length} package(s) with commits:`);
1611
+ logger.log(`${packagesWithCommits.length} package(s) with commits:`);
1745
1612
  packagesWithCommits.forEach((pkg) => {
1746
- logger.log(` \u2022 ${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.version}`);
1613
+ pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion}`);
1747
1614
  });
1748
1615
  logger.log("");
1749
1616
  }
1750
1617
  if (packagesAsDependents.length > 0) {
1751
- logger.log(`${dryRun ? "[dry-run] " : ""}${packagesAsDependents.length} dependent package(s):`);
1618
+ logger.log(`${packagesAsDependents.length} dependent package(s):`);
1752
1619
  packagesAsDependents.forEach((pkg) => {
1753
- logger.log(` \u2022 ${pkg.name}: ${pkg.currentVersion} \u2192 ${pkg.version}`);
1620
+ pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion}`);
1621
+ });
1622
+ logger.log("");
1623
+ }
1624
+ if (packagesAsGraduation.length > 0) {
1625
+ logger.log(`${packagesAsGraduation.length} graduation package(s):`);
1626
+ packagesAsGraduation.forEach((pkg) => {
1627
+ pkg.newVersion && logger.log(` \u2022 ${pkg.name}: ${pkg.version} \u2192 ${pkg.newVersion}`);
1754
1628
  });
1755
1629
  logger.log("");
1756
1630
  }
@@ -1765,6 +1639,10 @@ async function confirmBump({
1765
1639
  newVersion,
1766
1640
  dryRun
1767
1641
  }) {
1642
+ if (packages.length === 0) {
1643
+ logger.debug("No packages to bump");
1644
+ return;
1645
+ }
1768
1646
  const lernaJsonExists = hasLernaJson(config.cwd);
1769
1647
  logger.log("");
1770
1648
  logger.info(`${dryRun ? "[dry-run] " : ""}The following packages will be updated:
@@ -1776,220 +1654,100 @@ async function confirmBump({
1776
1654
  lernaJsonExists,
1777
1655
  dryRun
1778
1656
  });
1779
- if (versionMode === "unified" && newVersion) {
1657
+ if (versionMode === "unified") {
1658
+ if (!newVersion) {
1659
+ logger.error("Cannot confirm bump in unified mode without a new version");
1660
+ process.exit(1);
1661
+ }
1780
1662
  displayUnifiedModePackages({ packages, newVersion, force });
1781
- } else if (versionMode === "selective" && newVersion) {
1663
+ } else if (versionMode === "selective") {
1664
+ if (!newVersion) {
1665
+ logger.error("Cannot confirm bump in selective mode without a new version");
1666
+ process.exit(1);
1667
+ }
1782
1668
  displaySelectiveModePackages({ packages, newVersion, force });
1783
1669
  } else if (versionMode === "independent") {
1784
- displayIndependentModePackages({ packages, force, dryRun });
1670
+ displayIndependentModePackages({ packages, force });
1785
1671
  }
1786
1672
  try {
1787
1673
  const confirmed = await confirm({
1788
1674
  message: `${dryRun ? "[dry-run] " : ""}Do you want to proceed with these version updates?`,
1789
1675
  default: true
1790
1676
  });
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 [];
1677
+ if (!confirmed) {
1678
+ logger.log("");
1679
+ logger.fail("Bump refused");
1680
+ process.exit(0);
1681
+ }
1682
+ } catch (error) {
1683
+ const userHasExited = error instanceof Error && error.name === "ExitPromptError";
1684
+ if (userHasExited) {
1685
+ logger.log("");
1686
+ logger.fail("Bump cancelled");
1687
+ process.exit(0);
1688
+ }
1689
+ logger.fail("Error while confirming bump");
1690
+ process.exit(1);
1928
1691
  }
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;
1692
+ logger.log("");
1943
1693
  }
1944
- function bumpIndependentPackages({
1694
+ function getBumpedIndependentPackages({
1945
1695
  packages,
1946
1696
  dryRun
1947
1697
  }) {
1948
1698
  const bumpedPackages = [];
1949
1699
  for (const pkgToBump of packages) {
1950
- logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.currentVersion} to ${pkgToBump.version} (reason: ${pkgToBump.reason})`);
1951
- const result = bumpPackageIndependently({
1700
+ logger.debug(`Bumping ${pkgToBump.name} from ${pkgToBump.version} to ${pkgToBump.newVersion} (reason: ${pkgToBump.reason})`);
1701
+ const result = getBumpedPackageIndependently({
1952
1702
  pkg: pkgToBump,
1953
1703
  dryRun
1954
1704
  });
1955
1705
  if (result.bumped) {
1956
1706
  bumpedPackages.push({
1957
1707
  ...pkgToBump,
1958
- version: result.newVersion
1708
+ version: result.oldVersion
1959
1709
  });
1960
1710
  }
1961
1711
  }
1962
1712
  return bumpedPackages;
1963
1713
  }
1964
1714
 
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
- }
1715
+ function getIndependentTag({ version, name }) {
1716
+ return `${name}@${version}`;
1717
+ }
1718
+ async function getLastStableTag({ logLevel, cwd }) {
1719
+ const { stdout } = await execPromise(
1720
+ `git tag --sort=-creatordate | grep -E '^[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+$' | head -n 1`,
1721
+ {
1722
+ logLevel,
1723
+ noStderr: true,
1724
+ noStdout: true,
1725
+ noSuccess: true,
1726
+ cwd
1727
+ }
1728
+ );
1729
+ const lastTag = stdout.trim();
1730
+ logger.debug("Last stable tag:", lastTag || "No stable tags found");
1731
+ return lastTag;
1732
+ }
1733
+ async function getLastTag({ logLevel, cwd }) {
1982
1734
  const { stdout } = await execPromise(`git tag --sort=-creatordate | head -n 1`, {
1983
- logLevel: options?.logLevel,
1735
+ logLevel,
1984
1736
  noStderr: true,
1985
1737
  noStdout: true,
1986
1738
  noSuccess: true,
1987
- cwd: options?.cwd
1739
+ cwd
1988
1740
  });
1989
- lastTag = stdout.trim();
1741
+ const lastTag = stdout.trim();
1990
1742
  logger.debug("Last tag:", lastTag || "No tags found");
1991
1743
  return lastTag;
1992
1744
  }
1745
+ function getLastRepoTag(options) {
1746
+ if (options?.onlyStable) {
1747
+ return getLastStableTag({ logLevel: options?.logLevel, cwd: options?.cwd });
1748
+ }
1749
+ return getLastTag({ logLevel: options?.logLevel, cwd: options?.cwd });
1750
+ }
1993
1751
  async function getLastPackageTag({
1994
1752
  packageName,
1995
1753
  onlyStable,
@@ -2020,88 +1778,111 @@ async function getLastPackageTag({
2020
1778
  return null;
2021
1779
  }
2022
1780
  }
2023
- async function resolveTagsIndependent({
1781
+ async function resolveFromTagIndependent({
2024
1782
  cwd,
2025
- pkg,
1783
+ packageName,
2026
1784
  graduating,
2027
- newVersion,
2028
- step,
2029
1785
  logLevel
2030
1786
  }) {
2031
- let to;
2032
- if (step === "bump") {
2033
- to = getCurrentGitRef(cwd);
2034
- } else {
2035
- to = newVersion ? `${pkg.name}@${newVersion}` : getCurrentGitRef(cwd);
2036
- }
2037
1787
  const lastPackageTag = await getLastPackageTag({
2038
- packageName: pkg.name,
1788
+ packageName,
2039
1789
  onlyStable: graduating,
2040
1790
  logLevel
2041
1791
  });
2042
1792
  if (!lastPackageTag) {
2043
- const from = await getLastRepoTag({ logLevel }) || getFirstCommit(cwd);
2044
- return { from, to };
1793
+ return getFirstCommit(cwd);
2045
1794
  }
2046
- return { from: lastPackageTag, to };
1795
+ return lastPackageTag;
2047
1796
  }
2048
- async function resolveTagsUnified({
1797
+ async function resolveFromTagUnified({
2049
1798
  config,
2050
- newVersion,
2051
1799
  graduating,
2052
- step,
2053
1800
  logLevel
2054
1801
  }) {
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
1802
  const from = await getLastRepoTag({ onlyStable: graduating, logLevel }) || getFirstCommit(config.cwd);
2062
- return { from, to };
1803
+ return from;
2063
1804
  }
2064
- async function resolveTags({
1805
+ async function resolveFromTag({
2065
1806
  config,
2066
1807
  versionMode,
2067
1808
  step,
2068
- pkg,
2069
- currentVersion,
2070
- newVersion,
1809
+ packageName,
1810
+ graduating,
2071
1811
  logLevel
2072
1812
  }) {
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
- }
1813
+ let from;
2084
1814
  if (versionMode === "independent") {
2085
- const tags2 = await resolveTagsIndependent({
1815
+ if (!packageName) {
1816
+ throw new Error("Package name is required for independent version mode");
1817
+ }
1818
+ from = await resolveFromTagIndependent({
2086
1819
  cwd: config.cwd,
2087
- pkg,
1820
+ packageName,
1821
+ graduating,
1822
+ logLevel
1823
+ });
1824
+ } else {
1825
+ from = await resolveFromTagUnified({
1826
+ config,
2088
1827
  graduating,
2089
- newVersion,
2090
- step,
2091
1828
  logLevel
2092
1829
  });
2093
- logger.debug(`[${versionMode}](${step}) Using tags: ${tags2.from} \u2192 ${tags2.to}`);
2094
- return tags2;
2095
1830
  }
2096
- const tags = await resolveTagsUnified({
1831
+ logger.debug(`[${versionMode}](${step}) Using from tag: ${from}`);
1832
+ return config.from || from;
1833
+ }
1834
+ function resolveToTag({
1835
+ config,
1836
+ versionMode,
1837
+ newVersion,
1838
+ step,
1839
+ packageName
1840
+ }) {
1841
+ const isUntaggedStep = step === "bump" || step === "changelog";
1842
+ let to;
1843
+ if (isUntaggedStep) {
1844
+ to = getCurrentGitRef(config.cwd);
1845
+ } else if (versionMode === "independent") {
1846
+ if (!packageName) {
1847
+ throw new Error("Package name is required for independent version mode");
1848
+ }
1849
+ if (!newVersion) {
1850
+ throw new Error("New version is required for independent version mode");
1851
+ }
1852
+ to = getIndependentTag({ version: newVersion, name: packageName });
1853
+ } else {
1854
+ to = newVersion ? config.templates.tagBody.replace("{{newVersion}}", newVersion) : getCurrentGitRef(config.cwd);
1855
+ }
1856
+ logger.debug(`[${versionMode}](${step}) Using to tag: ${to}`);
1857
+ return config.to || to;
1858
+ }
1859
+ async function resolveTags({
1860
+ config,
1861
+ step,
1862
+ pkg,
1863
+ newVersion
1864
+ }) {
1865
+ const versionMode = config.monorepo?.versionMode || "standalone";
1866
+ const logLevel = config.logLevel;
1867
+ logger.debug(`[${versionMode}](${step}) Resolving tags`);
1868
+ const releaseType = config.bump.type;
1869
+ const from = await resolveFromTag({
2097
1870
  config,
2098
- newVersion,
2099
- graduating,
1871
+ versionMode,
2100
1872
  step,
1873
+ packageName: pkg.name,
1874
+ graduating: isGraduating(pkg.version, releaseType),
2101
1875
  logLevel
2102
1876
  });
2103
- logger.debug(`[${versionMode}](${step}) Using tags: ${tags.from} \u2192 ${tags.to}`);
2104
- return tags;
1877
+ const to = resolveToTag({
1878
+ config,
1879
+ versionMode,
1880
+ newVersion,
1881
+ step,
1882
+ packageName: pkg.name
1883
+ });
1884
+ logger.debug(`[${versionMode}](${step}) Using tags: ${from} \u2192 ${to}`);
1885
+ return { from, to };
2105
1886
  }
2106
1887
 
2107
1888
  let sessionOtp;
@@ -2120,7 +1901,8 @@ function detectPackageManager(cwd = process.cwd()) {
2120
1901
  }
2121
1902
  }
2122
1903
  } catch (e) {
2123
- logger.warn(`Failed to parse package.json: ${e.message}`);
1904
+ const errorString = e instanceof Error ? e.message : String(e);
1905
+ logger.debug(`Failed to parse package.json: ${errorString}`);
2124
1906
  }
2125
1907
  }
2126
1908
  const lockFiles = {
@@ -2146,7 +1928,7 @@ function detectPackageManager(cwd = process.cwd()) {
2146
1928
  logger.debug("No package manager detected, defaulting to npm");
2147
1929
  return "npm";
2148
1930
  } catch (error) {
2149
- logger.warn(`Error detecting package manager: ${error}, defaulting to npm`);
1931
+ logger.fail(`Error detecting package manager: ${error}, defaulting to npm`);
2150
1932
  return "npm";
2151
1933
  }
2152
1934
  }
@@ -2180,23 +1962,13 @@ async function getPackagesToPublishInIndependentMode(sortedPackages, config) {
2180
1962
  for (const pkg of sortedPackages) {
2181
1963
  const { from, to } = await resolveTags({
2182
1964
  config,
2183
- versionMode: "independent",
2184
1965
  step: "publish",
2185
1966
  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
1967
+ newVersion: pkg.version
2196
1968
  });
2197
- if (commits.length > 0) {
1969
+ if (pkg.commits.length > 0) {
2198
1970
  packagesToPublish.push(pkg);
2199
- logger.debug(`${pkg.name}: ${commits.length} commit(s) since ${from} \u2192 ${to}`);
1971
+ logger.debug(`${pkg.name}: ${pkg.commits.length} commit(s) since ${from} \u2192 ${to}`);
2200
1972
  }
2201
1973
  }
2202
1974
  return packagesToPublish;
@@ -2301,7 +2073,7 @@ async function publishPackage({
2301
2073
  dryRun
2302
2074
  }) {
2303
2075
  const tag = determinePublishTag(pkg.version, config.publish.tag);
2304
- const packageNameAndVersion = `${pkg.name}@${pkg.version}`;
2076
+ const packageNameAndVersion = getIndependentTag(pkg);
2305
2077
  const baseCommand = packageManager === "yarn" && isYarnBerry() ? "yarn npm" : packageManager;
2306
2078
  logger.debug(`Building publish command for ${pkg.name}`);
2307
2079
  let dynamicOtp;
@@ -2342,198 +2114,412 @@ async function publishPackage({
2342
2114
  }
2343
2115
  }
2344
2116
 
2117
+ function readPackageJson(packagePath) {
2118
+ const packageJsonPath = join(packagePath, "package.json");
2119
+ if (!existsSync(packageJsonPath))
2120
+ throw new Error(`package.json not found at ${packageJsonPath}`);
2121
+ if (!statSync(packagePath).isDirectory())
2122
+ throw new Error(`Not a directory: ${packagePath}`);
2123
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
2124
+ if (!packageJson.name || !packageJson.version) {
2125
+ throw new Error(`Invalid package.json at ${packagePath}`);
2126
+ }
2127
+ return {
2128
+ name: packageJson.name,
2129
+ version: packageJson.version,
2130
+ private: packageJson.private || false,
2131
+ path: packagePath
2132
+ };
2133
+ }
2134
+ async function getRootPackage({
2135
+ config,
2136
+ force,
2137
+ from,
2138
+ to,
2139
+ suffix,
2140
+ changelog
2141
+ }) {
2142
+ try {
2143
+ const packageJson = readPackageJson(config.cwd);
2144
+ const commits = await getPackageCommits({
2145
+ pkg: packageJson,
2146
+ from,
2147
+ to,
2148
+ config,
2149
+ changelog
2150
+ });
2151
+ const releaseType = determineReleaseType({
2152
+ currentVersion: packageJson.version,
2153
+ commits,
2154
+ releaseType: config.bump.type,
2155
+ preid: config.bump.preid,
2156
+ types: config.types,
2157
+ force
2158
+ });
2159
+ if (!releaseType) {
2160
+ logger.fail("No commits require a version bump");
2161
+ process.exit(0);
2162
+ }
2163
+ const newVersion = getPackageNewVersion({
2164
+ currentVersion: packageJson.version,
2165
+ releaseType,
2166
+ preid: config.bump.preid,
2167
+ suffix
2168
+ });
2169
+ return {
2170
+ ...packageJson,
2171
+ path: config.cwd,
2172
+ fromTag: from,
2173
+ commits,
2174
+ newVersion
2175
+ };
2176
+ } catch (error) {
2177
+ const errorMessage = error instanceof Error ? error.message : String(error);
2178
+ throw new Error(errorMessage);
2179
+ }
2180
+ }
2181
+ function readPackages({
2182
+ cwd,
2183
+ patterns,
2184
+ ignorePackageNames
2185
+ }) {
2186
+ const packages = [];
2187
+ const foundPaths = /* @__PURE__ */ new Set();
2188
+ if (!patterns)
2189
+ patterns = ["."];
2190
+ logger.debug(`Getting packages from patterns: ${patterns.join(", ")}`);
2191
+ for (const pattern of patterns) {
2192
+ try {
2193
+ const matches = fastGlob.sync(pattern, {
2194
+ cwd,
2195
+ onlyDirectories: true,
2196
+ absolute: true,
2197
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
2198
+ });
2199
+ for (const matchPath of matches) {
2200
+ if (foundPaths.has(matchPath))
2201
+ continue;
2202
+ const packageBase = readPackageJson(matchPath);
2203
+ if (!packageBase || packageBase.private || ignorePackageNames?.includes(packageBase.name))
2204
+ continue;
2205
+ foundPaths.add(matchPath);
2206
+ packages.push({
2207
+ ...packageBase,
2208
+ path: matchPath
2209
+ });
2210
+ }
2211
+ } catch (error) {
2212
+ logger.error(error);
2213
+ }
2214
+ }
2215
+ return packages;
2216
+ }
2217
+ function getPackageReleaseType({
2218
+ pkg,
2219
+ config,
2220
+ force
2221
+ }) {
2222
+ const releaseType = config.bump.type;
2223
+ if (force) {
2224
+ return determineReleaseType({
2225
+ currentVersion: pkg.version,
2226
+ commits: pkg.commits,
2227
+ releaseType,
2228
+ preid: config.bump.preid,
2229
+ types: config.types,
2230
+ force
2231
+ });
2232
+ }
2233
+ if (pkg.reason === "dependency") {
2234
+ if (isStableReleaseType(releaseType))
2235
+ return "patch";
2236
+ if (isPrerelease(pkg.version))
2237
+ return "prerelease";
2238
+ return "prepatch";
2239
+ }
2240
+ return determineReleaseType({
2241
+ currentVersion: pkg.version,
2242
+ commits: pkg.commits,
2243
+ releaseType,
2244
+ preid: config.bump.preid,
2245
+ types: config.types,
2246
+ force
2247
+ });
2248
+ }
2249
+ async function getPackages({
2250
+ patterns,
2251
+ config,
2252
+ suffix,
2253
+ force
2254
+ }) {
2255
+ const readedPackages = readPackages({
2256
+ cwd: config.cwd,
2257
+ patterns,
2258
+ ignorePackageNames: config.monorepo?.ignorePackageNames
2259
+ });
2260
+ const packages = /* @__PURE__ */ new Map();
2261
+ const foundPaths = /* @__PURE__ */ new Set();
2262
+ if (!patterns)
2263
+ patterns = ["."];
2264
+ logger.debug(`Getting packages from patterns: ${patterns.join(", ")}`);
2265
+ const patternsSet = new Set(patterns);
2266
+ for (const pattern of patternsSet) {
2267
+ const matches = fastGlob.sync(pattern, {
2268
+ cwd: config.cwd,
2269
+ onlyDirectories: true,
2270
+ absolute: true,
2271
+ ignore: ["**/node_modules/**", "**/dist/**", "**/.git/**"]
2272
+ });
2273
+ for (const matchPath of matches) {
2274
+ if (foundPaths.has(matchPath))
2275
+ continue;
2276
+ const packageBase = readPackageJson(matchPath);
2277
+ if (packageBase.private) {
2278
+ logger.debug(`${packageBase.name} is private and will be ignored`);
2279
+ continue;
2280
+ }
2281
+ if (config.monorepo?.ignorePackageNames?.includes(packageBase.name)) {
2282
+ logger.debug(`${packageBase.name} ignored by config (monorepo.ignorePackageNames)`);
2283
+ continue;
2284
+ }
2285
+ if (!packageBase.version) {
2286
+ logger.warn(`${packageBase.name} has no version and will be ignored`);
2287
+ continue;
2288
+ }
2289
+ const { from, to } = await resolveTags({
2290
+ config,
2291
+ step: "bump",
2292
+ pkg: packageBase,
2293
+ newVersion: void 0
2294
+ });
2295
+ const commits = await getPackageCommits({
2296
+ pkg: packageBase,
2297
+ from,
2298
+ to,
2299
+ config,
2300
+ changelog: false
2301
+ });
2302
+ foundPaths.add(matchPath);
2303
+ const dependencies = getPackageDependencies({
2304
+ packagePath: matchPath,
2305
+ allPackageNames: new Set(readedPackages.map((p) => p.name)),
2306
+ dependencyTypes: config.bump?.dependencyTypes
2307
+ });
2308
+ packages.set(packageBase.name, {
2309
+ ...packageBase,
2310
+ path: matchPath,
2311
+ fromTag: from,
2312
+ dependencies,
2313
+ commits,
2314
+ reason: commits.length > 0 ? "commits" : void 0,
2315
+ dependencyChain: void 0,
2316
+ newVersion: void 0
2317
+ });
2318
+ }
2319
+ }
2320
+ const packagesArray = Array.from(packages.values());
2321
+ const packagesWithCommits = packagesArray.filter((p) => p.commits.length > 0);
2322
+ const expandedPackages = expandPackagesToBumpWithDependents({
2323
+ allPackages: packagesArray,
2324
+ packagesWithCommits
2325
+ });
2326
+ for (const pkg of expandedPackages) {
2327
+ packages.set(pkg.name, pkg);
2328
+ }
2329
+ for (const pkg of Array.from(packages.values())) {
2330
+ const releaseType = getPackageReleaseType({
2331
+ pkg,
2332
+ config,
2333
+ force
2334
+ });
2335
+ const newVersion = releaseType ? getPackageNewVersion({
2336
+ currentVersion: pkg.version,
2337
+ releaseType,
2338
+ preid: config.bump.preid,
2339
+ suffix
2340
+ }) : void 0;
2341
+ const graduating = releaseType && isGraduating(pkg.version, releaseType) || isChangedPreid(pkg.version, config.bump.preid);
2342
+ packages.set(pkg.name, {
2343
+ ...pkg,
2344
+ newVersion,
2345
+ reason: pkg.reason || releaseType && graduating && "graduation" || void 0
2346
+ });
2347
+ }
2348
+ const packagesToBump = Array.from(packages.values()).filter((p) => p.reason || force);
2349
+ if (packagesToBump.length === 0) {
2350
+ logger.debug("No packages to bump");
2351
+ return [];
2352
+ }
2353
+ return packagesToBump;
2354
+ }
2355
+ function isAllowedCommit({
2356
+ commit,
2357
+ type,
2358
+ changelog
2359
+ }) {
2360
+ if (commit.type === "chore" && ["deps", "release"].includes(commit.scope) && !commit.isBreaking) {
2361
+ return false;
2362
+ }
2363
+ if (typeof type === "object") {
2364
+ return !!type.semver || changelog && !!type.title;
2365
+ }
2366
+ if (typeof type === "boolean") {
2367
+ return type;
2368
+ }
2369
+ return false;
2370
+ }
2371
+ async function getPackageCommits({
2372
+ pkg,
2373
+ from,
2374
+ to,
2375
+ config,
2376
+ changelog
2377
+ }) {
2378
+ logger.debug(`Analyzing commits for ${pkg.name} since ${from} to ${to}`);
2379
+ const changelogConfig = {
2380
+ ...config,
2381
+ from,
2382
+ to
2383
+ };
2384
+ const rawCommits = await getGitDiff(from, to, changelogConfig.cwd);
2385
+ const allCommits = parseCommits(rawCommits, changelogConfig);
2386
+ const hasBreakingChanges = allCommits.some((commit) => commit.isBreaking);
2387
+ logger.debug(`Has breaking changes: ${hasBreakingChanges}`);
2388
+ const rootPackage = readPackageJson(changelogConfig.cwd);
2389
+ const commits = allCommits.filter((commit) => {
2390
+ const type = changelogConfig?.types[commit.type];
2391
+ if (!isAllowedCommit({ commit, type, changelog })) {
2392
+ return false;
2393
+ }
2394
+ if (pkg.path === changelogConfig.cwd || pkg.name === rootPackage.name) {
2395
+ return true;
2396
+ }
2397
+ const packageRelativePath = relative(changelogConfig.cwd, pkg.path);
2398
+ const scopeMatches = commit.scope === pkg.name;
2399
+ const bodyContainsPath = commit.body.includes(packageRelativePath);
2400
+ return scopeMatches || bodyContainsPath;
2401
+ });
2402
+ logger.debug(`Found ${commits.length} commit(s) for ${pkg.name} from ${from} to ${to}`);
2403
+ if (commits.length > 0) {
2404
+ logger.debug(`${pkg.name}: ${commits.length} commit(s) found`);
2405
+ } else {
2406
+ logger.debug(`${pkg.name}: No commits found`);
2407
+ }
2408
+ return commits;
2409
+ }
2410
+ function hasLernaJson(rootDir) {
2411
+ const lernaJsonPath = join(rootDir, "lerna.json");
2412
+ return existsSync(lernaJsonPath);
2413
+ }
2414
+
2345
2415
  async function bumpUnifiedMode({
2346
2416
  config,
2347
- packages,
2348
2417
  dryRun,
2349
2418
  force,
2350
2419
  suffix
2351
2420
  }) {
2352
2421
  logger.debug("Starting bump in unified mode");
2353
- const rootPackage = getRootPackage(config.cwd);
2354
- const currentVersion = rootPackage.version;
2422
+ const rootPackageBase = readPackageJson(config.cwd);
2355
2423
  const { from, to } = await resolveTags({
2356
2424
  config,
2357
- versionMode: "unified",
2358
2425
  step: "bump",
2359
2426
  newVersion: void 0,
2360
- pkg: void 0,
2361
- currentVersion,
2362
- logLevel: config.logLevel
2427
+ pkg: rootPackageBase
2363
2428
  });
2364
- logger.debug(`Bump from ${from} to ${to}`);
2365
- const commits = await getPackageCommits({
2366
- pkg: rootPackage,
2429
+ const rootPackage = await getRootPackage({
2430
+ config,
2431
+ force,
2367
2432
  from,
2368
2433
  to,
2369
- config,
2434
+ suffix,
2370
2435
  changelog: false
2371
2436
  });
2372
- logger.debug(`Found ${commits.length} commits since ${from}`);
2373
- const releaseType = determineReleaseType({
2374
- from,
2375
- to,
2376
- currentVersion,
2377
- commits,
2437
+ const currentVersion = rootPackage.version;
2438
+ const newVersion = rootPackage.newVersion;
2439
+ if (!newVersion) {
2440
+ throw new Error(`Could not determine a new version for ${rootPackage.name} (root)`);
2441
+ }
2442
+ logger.debug(`Bump from ${from} to ${to}`);
2443
+ logger.debug(`${currentVersion} \u2192 ${newVersion} (${config.monorepo?.versionMode || "standalone"} mode)`);
2444
+ const packages = await getPackages({
2378
2445
  config,
2446
+ patterns: config.monorepo?.packages,
2447
+ suffix,
2379
2448
  force
2380
2449
  });
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
2450
  if (!config.bump.yes) {
2399
2451
  await confirmBump({
2400
2452
  versionMode: "unified",
2401
- config,
2402
2453
  packages,
2454
+ config,
2403
2455
  force,
2404
2456
  currentVersion,
2405
2457
  newVersion,
2406
2458
  dryRun
2407
2459
  });
2408
2460
  } else {
2409
- logger.info(`${allPackages.length === 1 ? allPackages[0].name : allPackages.length} package(s) bumped from ${currentVersion} to ${newVersion} (unified mode)`);
2461
+ logger.info(`${packages.length === 1 ? packages[0].name : packages.length} package(s) bumped from ${currentVersion} to ${newVersion} (${config.monorepo?.versionMode || "standalone"} mode)`);
2410
2462
  }
2411
- for (const pkg of allPackages) {
2463
+ for (const pkg of [rootPackage, ...packages]) {
2412
2464
  writeVersion(pkg.path, newVersion, dryRun);
2413
2465
  }
2414
- if (newVersion) {
2415
- updateLernaVersion({
2416
- rootDir: config.cwd,
2417
- versionMode: config.monorepo?.versionMode,
2418
- version: newVersion,
2419
- dryRun
2420
- });
2421
- }
2466
+ updateLernaVersion({
2467
+ rootDir: config.cwd,
2468
+ versionMode: config.monorepo?.versionMode,
2469
+ version: newVersion,
2470
+ dryRun
2471
+ });
2422
2472
  if (!dryRun) {
2423
- logger.info(`All ${allPackages.length} package(s) bumped to ${newVersion}`);
2473
+ logger.info(`All ${packages.length} package(s) bumped to ${newVersion}`);
2424
2474
  }
2425
2475
  return {
2426
2476
  bumped: true,
2427
2477
  oldVersion: currentVersion,
2428
2478
  fromTag: from,
2429
2479
  newVersion,
2480
+ rootPackage,
2430
2481
  bumpedPackages: packages.map((pkg) => ({
2431
2482
  ...pkg,
2432
- currentVersion: pkg.version,
2433
- version: newVersion,
2434
- fromTag: from
2483
+ oldVersion: pkg.version,
2484
+ newVersion,
2485
+ fromTag: from,
2486
+ reason: "commits"
2435
2487
  }))
2436
2488
  };
2437
2489
  }
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
2490
  async function bumpSelectiveMode({
2492
2491
  config,
2493
- packages,
2494
2492
  dryRun,
2495
2493
  force,
2496
2494
  suffix
2497
2495
  }) {
2498
2496
  logger.debug("Starting bump in selective mode");
2499
- const rootPackage = getRootPackage(config.cwd);
2500
- const currentVersion = rootPackage.version;
2497
+ const rootPackageBase = readPackageJson(config.cwd);
2501
2498
  const { from, to } = await resolveTags({
2502
2499
  config,
2503
- versionMode: "selective",
2504
2500
  step: "bump",
2505
- currentVersion,
2506
- newVersion: void 0,
2507
- pkg: void 0,
2508
- logLevel: config.logLevel
2501
+ pkg: rootPackageBase,
2502
+ newVersion: void 0
2509
2503
  });
2510
- logger.debug(`Bump from ${from} to ${to}`);
2511
- const commits = await getPackageCommits({
2512
- pkg: rootPackage,
2504
+ const rootPackage = await getRootPackage({
2505
+ config,
2506
+ force,
2513
2507
  from,
2514
2508
  to,
2515
- config,
2509
+ suffix,
2516
2510
  changelog: false
2517
2511
  });
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
- });
2512
+ const currentVersion = rootPackage.version;
2513
+ const newVersion = rootPackage.newVersion;
2514
+ logger.debug(`Bump from ${currentVersion} to ${newVersion}`);
2529
2515
  logger.debug("Determining packages to bump...");
2530
- const packagesToBump = force ? packages : await getPackageToBump({
2531
- packages,
2532
- from,
2533
- to,
2534
- config
2516
+ const packages = await getPackages({
2517
+ config,
2518
+ patterns: config.monorepo?.packages,
2519
+ suffix,
2520
+ force
2535
2521
  });
2536
- if (packagesToBump.length === 0) {
2522
+ if (packages.length === 0) {
2537
2523
  logger.debug("No packages have commits, skipping bump");
2538
2524
  return { bumped: false };
2539
2525
  }
@@ -2541,7 +2527,7 @@ async function bumpSelectiveMode({
2541
2527
  await confirmBump({
2542
2528
  versionMode: "selective",
2543
2529
  config,
2544
- packages: packagesToBump,
2530
+ packages,
2545
2531
  force,
2546
2532
  currentVersion,
2547
2533
  newVersion,
@@ -2549,18 +2535,18 @@ async function bumpSelectiveMode({
2549
2535
  });
2550
2536
  } else {
2551
2537
  if (force) {
2552
- logger.info(`${packagesToBump.length} package(s) bumped to ${newVersion} (force)`);
2538
+ logger.info(`${packages.length} package(s) bumped to ${newVersion} (force)`);
2553
2539
  } 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;
2540
+ const bumpedByCommits = packages.filter((p) => "reason" in p && p.reason === "commits").length;
2541
+ const bumpedByDependency = packages.filter((p) => "reason" in p && p.reason === "dependency").length;
2542
+ const bumpedByGraduation = packages.filter((p) => "reason" in p && p.reason === "graduation").length;
2556
2543
  logger.info(
2557
- `${currentVersion} \u2192 ${newVersion} (selective mode: ${bumpedByCommits} with commits, ${bumpedByDependency} as dependents)`
2544
+ `${currentVersion} \u2192 ${newVersion} (selective mode: ${bumpedByCommits} with commits, ${bumpedByDependency} as dependents, ${bumpedByGraduation} from graduation)`
2558
2545
  );
2559
2546
  }
2560
2547
  }
2561
- logger.debug(`Writing version to ${packagesToBump.length} package(s)`);
2562
- writeVersion(rootPackage.path, newVersion, dryRun);
2563
- for (const pkg of packagesToBump) {
2548
+ logger.debug(`Writing version to ${packages.length} package(s)`);
2549
+ for (const pkg of [rootPackage, ...packages]) {
2564
2550
  writeVersion(pkg.path, newVersion, dryRun);
2565
2551
  }
2566
2552
  updateLernaVersion({
@@ -2571,19 +2557,76 @@ async function bumpSelectiveMode({
2571
2557
  });
2572
2558
  if (!dryRun) {
2573
2559
  logger.info(
2574
- `${packagesToBump.length} package(s) bumped to ${newVersion} (${packages.length - packagesToBump.length} skipped)`
2560
+ `${packages.length} package(s) bumped to ${newVersion}`
2575
2561
  );
2576
2562
  }
2577
2563
  return {
2578
2564
  bumped: true,
2579
2565
  oldVersion: currentVersion,
2566
+ rootPackage,
2580
2567
  fromTag: from,
2581
2568
  newVersion,
2582
- bumpedPackages: packagesToBump.map((pkg) => ({
2569
+ bumpedPackages: packages.map((pkg) => ({
2583
2570
  ...pkg,
2584
- currentVersion: pkg.version,
2585
- version: newVersion,
2586
- fromTag: from
2571
+ oldVersion: pkg.version,
2572
+ fromTag: from,
2573
+ newVersion
2574
+ }))
2575
+ };
2576
+ }
2577
+ async function bumpIndependentMode({
2578
+ config,
2579
+ dryRun,
2580
+ suffix,
2581
+ force
2582
+ }) {
2583
+ logger.debug("Starting bump in independent mode");
2584
+ const packagesToBump = await getPackages({
2585
+ config,
2586
+ patterns: config.monorepo?.packages,
2587
+ suffix,
2588
+ force
2589
+ });
2590
+ if (packagesToBump.length === 0) {
2591
+ logger.debug("No packages have commits");
2592
+ return { bumped: false };
2593
+ }
2594
+ if (!config.bump.yes) {
2595
+ await confirmBump({
2596
+ versionMode: "independent",
2597
+ config,
2598
+ packages: packagesToBump,
2599
+ force,
2600
+ dryRun
2601
+ });
2602
+ } else {
2603
+ const bumpedByCommits2 = packagesToBump.filter((p) => p.reason === "commits").length;
2604
+ const bumpedByDependency2 = packagesToBump.filter((p) => p.reason === "dependency").length;
2605
+ const bumpedByGraduation = packagesToBump.filter((p) => p.reason === "graduation").length;
2606
+ logger.info(
2607
+ `${bumpedByCommits2 + bumpedByDependency2 + bumpedByGraduation} package(s) will be bumped independently (${bumpedByCommits2} from commits, ${bumpedByDependency2} from dependencies, ${bumpedByGraduation} from graduation)`
2608
+ );
2609
+ }
2610
+ const bumpedPackages = getBumpedIndependentPackages({
2611
+ packages: packagesToBump,
2612
+ dryRun
2613
+ });
2614
+ const bumpedByCommits = bumpedPackages.filter(
2615
+ (p) => packagesToBump.find((pkg) => pkg.name === p.name)?.reason === "commits"
2616
+ ).length;
2617
+ const bumpedByDependency = bumpedPackages.length - bumpedByCommits;
2618
+ if (bumpedPackages.length === 0) {
2619
+ logger.debug("No packages were bumped");
2620
+ } else {
2621
+ logger.info(
2622
+ `${dryRun ? "[dry-run] " : ""}${bumpedPackages.length} package(s) bumped independently (${bumpedByCommits} from commits, ${bumpedByDependency} from dependencies)`
2623
+ );
2624
+ }
2625
+ return {
2626
+ bumped: bumpedPackages.length > 0,
2627
+ bumpedPackages: bumpedPackages.map((pkg) => ({
2628
+ ...pkg,
2629
+ oldVersion: pkg.version
2587
2630
  }))
2588
2631
  };
2589
2632
  }
@@ -2613,7 +2656,7 @@ async function bump(options = {}) {
2613
2656
  }
2614
2657
  await fetchGitTags(config.cwd);
2615
2658
  logger.info(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
2616
- const packages = getPackages({
2659
+ const packages = readPackages({
2617
2660
  cwd: config.cwd,
2618
2661
  patterns: config.monorepo?.packages,
2619
2662
  ignorePackageNames: config.monorepo?.ignorePackageNames
@@ -2622,7 +2665,6 @@ async function bump(options = {}) {
2622
2665
  let result;
2623
2666
  const payload = {
2624
2667
  config,
2625
- packages,
2626
2668
  dryRun,
2627
2669
  force,
2628
2670
  suffix: options.suffix
@@ -2651,17 +2693,20 @@ async function bump(options = {}) {
2651
2693
  }
2652
2694
  }
2653
2695
 
2654
- function getPackagesToGenerateChangelogFor({
2696
+ async function getPackagesToGenerateChangelogFor({
2655
2697
  config,
2656
- bumpedPackages
2698
+ bumpResult,
2699
+ suffix,
2700
+ force
2657
2701
  }) {
2658
- if (bumpedPackages && bumpedPackages.length > 0) {
2659
- return bumpedPackages;
2702
+ if (bumpResult?.bumpedPackages && bumpResult.bumpedPackages.length > 0) {
2703
+ return bumpResult.bumpedPackages;
2660
2704
  }
2661
- return getPackages({
2662
- cwd: config.cwd,
2663
- ignorePackageNames: config.monorepo?.ignorePackageNames,
2664
- patterns: config.monorepo?.packages
2705
+ return await getPackages({
2706
+ config,
2707
+ patterns: config.monorepo?.packages,
2708
+ suffix,
2709
+ force
2665
2710
  });
2666
2711
  }
2667
2712
  async function generateIndependentRootChangelog({
@@ -2674,39 +2719,16 @@ async function generateIndependentRootChangelog({
2674
2719
  return;
2675
2720
  }
2676
2721
  logger.debug("Generating aggregated root changelog for independent mode");
2677
- const rootPackage = getRootPackage(config.cwd);
2678
2722
  const packageChangelogs = [];
2679
2723
  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
2724
  const changelog2 = await generateChangelog({
2699
- config,
2700
2725
  pkg,
2701
- commits,
2702
- from: lastTag,
2703
- dryRun
2726
+ config,
2727
+ dryRun,
2728
+ newVersion: isBumpedPackage(pkg) && pkg.newVersion || pkg.version
2704
2729
  });
2705
2730
  if (changelog2) {
2706
- const cleanedChangelog = changelog2.split("\n").slice(2).join("\n");
2707
- packageChangelogs.push(`## ${pkg.name}@${pkg.version}
2708
-
2709
- ${cleanedChangelog}`);
2731
+ packageChangelogs.push(changelog2);
2710
2732
  }
2711
2733
  }
2712
2734
  if (packageChangelogs.length === 0) {
@@ -2718,7 +2740,9 @@ ${cleanedChangelog}`);
2718
2740
 
2719
2741
  ${packageChangelogs.join("\n\n")}`;
2720
2742
  logger.verbose(`Aggregated root changelog: ${aggregatedChangelog}`);
2743
+ const rootPackage = readPackageJson(config.cwd);
2721
2744
  writeChangelogToFile({
2745
+ cwd: config.cwd,
2722
2746
  pkg: rootPackage,
2723
2747
  changelog: aggregatedChangelog,
2724
2748
  dryRun
@@ -2727,41 +2751,43 @@ ${packageChangelogs.join("\n\n")}`;
2727
2751
  }
2728
2752
  async function generateSimpleRootChangelog({
2729
2753
  config,
2730
- dryRun
2754
+ dryRun,
2755
+ force,
2756
+ suffix,
2757
+ bumpResult
2731
2758
  }) {
2732
2759
  if (!config.changelog?.rootChangelog) {
2733
2760
  logger.debug("Skipping root changelog generation");
2734
2761
  return;
2735
2762
  }
2736
2763
  logger.debug("Generating simple root changelog");
2737
- const rootPackage = getRootPackage(config.cwd);
2764
+ const rootPackageRead = readPackageJson(config.cwd);
2738
2765
  const { from, to } = await resolveTags({
2739
2766
  config,
2740
- versionMode: config.monorepo?.versionMode || "unified",
2741
2767
  step: "changelog",
2742
- currentVersion: rootPackage.version,
2743
2768
  newVersion: void 0,
2744
- pkg: void 0,
2745
- logLevel: config.logLevel
2769
+ pkg: rootPackageRead
2746
2770
  });
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,
2771
+ const fromTag = bumpResult?.fromTag || from;
2772
+ const rootPackage = bumpResult?.rootPackage || await getRootPackage({
2753
2773
  config,
2754
- changelog: true
2774
+ force,
2775
+ suffix,
2776
+ changelog: true,
2777
+ from: fromTag,
2778
+ to
2755
2779
  });
2780
+ logger.debug(`Generating ${rootPackage.name} changelog (${fromTag}...${to})`);
2781
+ const newVersion = bumpResult?.newVersion || rootPackage.version;
2756
2782
  const rootChangelog = await generateChangelog({
2757
2783
  pkg: rootPackage,
2758
- commits: rootCommits,
2759
2784
  config,
2760
- from,
2761
- dryRun
2785
+ dryRun,
2786
+ newVersion
2762
2787
  });
2763
2788
  if (rootChangelog) {
2764
2789
  writeChangelogToFile({
2790
+ cwd: config.cwd,
2765
2791
  changelog: rootChangelog,
2766
2792
  pkg: rootPackage,
2767
2793
  dryRun
@@ -2791,56 +2817,57 @@ async function changelog(options = {}) {
2791
2817
  try {
2792
2818
  await executeHook("before:changelog", config, dryRun);
2793
2819
  logger.start("Start generating changelogs");
2794
- const packages = getPackagesToGenerateChangelogFor({
2795
- config,
2796
- bumpedPackages: options.bumpedPackages
2797
- });
2798
2820
  if (config.changelog?.rootChangelog && config.monorepo) {
2799
2821
  if (config.monorepo.versionMode === "independent") {
2822
+ const packages2 = await getPackagesToGenerateChangelogFor({
2823
+ config,
2824
+ bumpResult: options.bumpResult,
2825
+ suffix: options.suffix,
2826
+ force: options.force ?? false
2827
+ });
2800
2828
  await generateIndependentRootChangelog({
2801
- packages,
2829
+ packages: packages2,
2802
2830
  config,
2803
2831
  dryRun
2804
2832
  });
2805
2833
  } else {
2806
2834
  await generateSimpleRootChangelog({
2807
2835
  config,
2808
- dryRun
2836
+ dryRun,
2837
+ bumpResult: options.bumpResult,
2838
+ suffix: options.suffix,
2839
+ force: options.force ?? false
2809
2840
  });
2810
2841
  }
2811
2842
  } else {
2812
2843
  logger.debug("Skipping root changelog generation");
2813
2844
  }
2814
2845
  logger.debug("Generating package changelogs...");
2846
+ const packages = options.bumpResult?.bumpedPackages ? options.bumpResult.bumpedPackages : await getPackages({
2847
+ config,
2848
+ patterns: config.monorepo?.packages,
2849
+ suffix: options.suffix,
2850
+ force: options.force ?? false
2851
+ });
2815
2852
  logger.debug(`Processing ${packages.length} package(s)`);
2816
2853
  let generatedCount = 0;
2817
2854
  for await (const pkg of packages) {
2818
2855
  const { from, to } = await resolveTags({
2819
2856
  config,
2820
- versionMode: config.monorepo?.versionMode || "unified",
2821
2857
  step: "changelog",
2822
- currentVersion: pkg.version,
2823
- newVersion: void 0,
2824
2858
  pkg,
2825
- logLevel: config.logLevel
2859
+ newVersion: void 0
2826
2860
  });
2827
2861
  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
2862
  const changelog2 = await generateChangelog({
2836
2863
  pkg,
2837
- commits,
2838
2864
  config,
2839
- from,
2840
- dryRun
2865
+ dryRun,
2866
+ newVersion: options.bumpResult?.bumpedPackages?.find((p) => p.name === pkg.name)?.newVersion
2841
2867
  });
2842
2868
  if (changelog2) {
2843
2869
  writeChangelogToFile({
2870
+ cwd: config.cwd,
2844
2871
  pkg,
2845
2872
  changelog: changelog2,
2846
2873
  dryRun
@@ -2889,7 +2916,7 @@ async function providerRelease(options = {}) {
2889
2916
  configName: options.configName,
2890
2917
  baseConfig: options.config,
2891
2918
  overrides: {
2892
- from: options.from || (options.bumpResult?.bumped === true ? options.bumpResult.fromTag : void 0),
2919
+ from: options.from,
2893
2920
  to: options.to,
2894
2921
  tokens: {
2895
2922
  github: options.token,
@@ -2903,12 +2930,12 @@ async function providerRelease(options = {}) {
2903
2930
  logger.debug(`Dry run: ${dryRun}`);
2904
2931
  logger.info(`Version mode: ${config.monorepo?.versionMode || "standalone"}`);
2905
2932
  try {
2906
- await executeHook("before:provider-release", config, dryRun);
2907
2933
  const detectedProvider = options.provider || detectGitProvider();
2908
2934
  providerReleaseSafetyCheck({ config, provider: detectedProvider });
2935
+ await executeHook("before:provider-release", config, dryRun);
2909
2936
  logger.start("Start provider release");
2910
2937
  if (!detectedProvider) {
2911
- logger.warn("Unable to detect Git provider. Skipping release publication.");
2938
+ logger.fail("Unable to detect Git provider. Skipping release publication.");
2912
2939
  throw new Error("Unable to detect Git provider");
2913
2940
  } else {
2914
2941
  logger.info(
@@ -2917,19 +2944,21 @@ async function providerRelease(options = {}) {
2917
2944
  }
2918
2945
  let postedReleases = [];
2919
2946
  const payload = {
2920
- from: config.from,
2947
+ from: config.from || options.bumpResult?.fromTag,
2921
2948
  to: config.to,
2922
2949
  dryRun,
2923
2950
  config,
2924
2951
  logLevel: config.logLevel,
2925
- bumpResult: options.bumpResult
2952
+ bumpResult: options.bumpResult,
2953
+ force: options.force ?? false,
2954
+ suffix: options.suffix
2926
2955
  };
2927
2956
  if (detectedProvider === "github") {
2928
2957
  postedReleases = await github(payload);
2929
2958
  } else if (detectedProvider === "gitlab") {
2930
2959
  postedReleases = await gitlab(payload);
2931
2960
  } else {
2932
- logger.warn(`Unsupported Git provider: ${detectedProvider}`);
2961
+ logger.error(`Unsupported Git provider: ${detectedProvider}`);
2933
2962
  }
2934
2963
  await executeHook("success:provider-release", config, dryRun);
2935
2964
  return {
@@ -2973,17 +3002,17 @@ async function publish(options = {}) {
2973
3002
  }
2974
3003
  try {
2975
3004
  await executeHook("before:publish", config, dryRun);
3005
+ const rootPackage = readPackageJson(config.cwd);
2976
3006
  logger.start("Start publishing packages");
2977
- const packages = options.bumpedPackages || getPackages({
2978
- cwd: config.cwd,
3007
+ const packages = options.bumpedPackages || await getPackages({
3008
+ config,
2979
3009
  patterns: config.publish.packages ?? config.monorepo?.packages,
2980
- ignorePackageNames: config.monorepo?.ignorePackageNames
3010
+ suffix: options.suffix,
3011
+ force: options.force ?? false
2981
3012
  });
2982
- const rootPackage = getRootPackage(config.cwd);
2983
3013
  logger.debug(`Found ${packages.length} package(s)`);
2984
3014
  logger.debug("Building dependency graph and sorting...");
2985
- const packagesWithDeps = getPackagesWithDependencies(packages, config.bump.dependencyTypes);
2986
- const sortedPackages = topologicalSort(packagesWithDeps);
3015
+ const sortedPackages = topologicalSort(packages);
2987
3016
  let publishedPackages = packages || [];
2988
3017
  if (publishedPackages.length === 0 && config.monorepo?.versionMode === "independent") {
2989
3018
  logger.debug("Determining packages to publish in independent mode...");
@@ -3000,7 +3029,7 @@ async function publish(options = {}) {
3000
3029
  logger.info(`Publishing ${publishedPackages.length} package(s) (unified mode)`);
3001
3030
  }
3002
3031
  if (publishedPackages.length === 0) {
3003
- logger.warn("No packages need to be published");
3032
+ logger.fail("No packages need to be published");
3004
3033
  return;
3005
3034
  }
3006
3035
  await executeBuildCmd({
@@ -3009,7 +3038,7 @@ async function publish(options = {}) {
3009
3038
  });
3010
3039
  for (const pkg of sortedPackages) {
3011
3040
  if (publishedPackages.some((p) => p.name === pkg.name)) {
3012
- logger.debug(`Publishing ${pkg.name}@${pkg.version}...`);
3041
+ logger.debug(`Publishing ${getIndependentTag(pkg)}...`);
3013
3042
  await publishPackage({
3014
3043
  pkg,
3015
3044
  config,
@@ -3104,7 +3133,8 @@ async function release(options = {}) {
3104
3133
  config,
3105
3134
  force,
3106
3135
  clean: config.release.clean,
3107
- configName: options.configName
3136
+ configName: options.configName,
3137
+ suffix: options.suffix
3108
3138
  });
3109
3139
  if (!bumpResult.bumped) {
3110
3140
  logger.debug("No packages bumped");
@@ -3118,10 +3148,12 @@ async function release(options = {}) {
3118
3148
  dryRun,
3119
3149
  formatCmd: config.changelog.formatCmd,
3120
3150
  rootChangelog: config.changelog.rootChangelog,
3121
- bumpedPackages: bumpResult.bumpedPackages,
3151
+ bumpResult,
3122
3152
  config,
3123
3153
  logLevel: config.logLevel,
3124
- configName: options.configName
3154
+ configName: options.configName,
3155
+ force,
3156
+ suffix: options.suffix
3125
3157
  });
3126
3158
  } else {
3127
3159
  logger.info("Skipping changelog generation (--no-changelog)");
@@ -3164,7 +3196,9 @@ async function release(options = {}) {
3164
3196
  bumpedPackages: bumpResult.bumpedPackages,
3165
3197
  dryRun,
3166
3198
  config,
3167
- configName: options.configName
3199
+ configName: options.configName,
3200
+ suffix: options.suffix,
3201
+ force
3168
3202
  });
3169
3203
  } else {
3170
3204
  logger.info("Skipping publish (--no-publish)");
@@ -3184,7 +3218,9 @@ async function release(options = {}) {
3184
3218
  config,
3185
3219
  logLevel: config.logLevel,
3186
3220
  bumpResult,
3187
- configName: options.configName
3221
+ configName: options.configName,
3222
+ force,
3223
+ suffix: options.suffix
3188
3224
  });
3189
3225
  provider = response.detectedProvider;
3190
3226
  postedReleases = response.postedReleases;
@@ -3195,7 +3231,7 @@ async function release(options = {}) {
3195
3231
  logger.info("Skipping release (--no-provider-release)");
3196
3232
  }
3197
3233
  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;
3234
+ const versionDisplay = config.monorepo?.versionMode === "independent" ? `${bumpResult.bumpedPackages.length} packages bumped independently` : bumpResult.newVersion || readPackageJson(config.cwd).version;
3199
3235
  logger.box(`Release workflow completed!
3200
3236
 
3201
3237
  Version: ${versionDisplay}
@@ -3212,4 +3248,4 @@ Git provider: ${provider}`);
3212
3248
  }
3213
3249
  }
3214
3250
 
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 };
3251
+ 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 };