@ucdjs/release-scripts 0.1.0-beta.16 → 0.1.0-beta.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -180,7 +180,13 @@ interface ReleaseResult {
180
180
  declare function release(options: ReleaseOptions): Promise<ReleaseResult | null>;
181
181
  //#endregion
182
182
  //#region src/verify.d.ts
183
- interface VerifyOptions extends SharedOptions {}
184
- declare function verify(_options: VerifyOptions): void;
183
+ interface VerifyOptions extends SharedOptions {
184
+ branch?: {
185
+ release?: string;
186
+ default?: string;
187
+ };
188
+ safeguards?: boolean;
189
+ }
190
+ declare function verify(options: VerifyOptions): Promise<void>;
185
191
  //#endregion
186
192
  export { type PublishOptions, type ReleaseOptions, type ReleaseResult, type VerifyOptions, publish, release, verify };
package/dist/index.mjs CHANGED
@@ -1,13 +1,15 @@
1
1
  import { t as Eta } from "./eta-j5TFRbI4.mjs";
2
- import { readFile, writeFile } from "node:fs/promises";
2
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
3
3
  import { join, relative } from "node:path";
4
4
  import process from "node:process";
5
+ import readline from "node:readline";
5
6
  import farver from "farver";
6
7
  import mri from "mri";
7
8
  import { exec } from "tinyexec";
8
9
  import { dedent } from "@luxass/utils";
9
10
  import { getCommits, groupByType } from "commit-parser";
10
11
  import prompts from "prompts";
12
+ import { gt } from "semver";
11
13
 
12
14
  //#region src/publish.ts
13
15
  function publish(_options) {}
@@ -57,6 +59,13 @@ const logger = {
57
59
  },
58
60
  success: (message) => {
59
61
  console.log(` ${farver.green("✓")} ${message}`);
62
+ },
63
+ clearScreen: () => {
64
+ const repeatCount = process.stdout.rows - 2;
65
+ const blank = repeatCount > 0 ? "\n".repeat(repeatCount) : "";
66
+ console.log(blank);
67
+ readline.cursorTo(process.stdout, 0, 0);
68
+ readline.clearScreenDown(process.stdout);
60
69
  }
61
70
  };
62
71
  async function run(bin, args$1, opts = {}) {
@@ -386,23 +395,25 @@ async function getGroupedFilesByCommitSha(workspaceRoot, from, to) {
386
395
 
387
396
  //#endregion
388
397
  //#region src/core/changelog.ts
398
+ const globalAuthorCache = /* @__PURE__ */ new Map();
389
399
  const DEFAULT_CHANGELOG_TEMPLATE = dedent`
390
- <% if (it.previousVersion) { %>
400
+ <% if (it.previousVersion) { -%>
391
401
  ## [<%= it.version %>](<%= it.compareUrl %>) (<%= it.date %>)
392
- <% } else { %>
402
+ <% } else { -%>
393
403
  ## <%= it.version %> (<%= it.date %>)
394
404
  <% } %>
395
405
 
396
406
  <% it.groups.forEach((group) => { %>
397
407
  <% if (group.commits.length > 0) { %>
398
- ### <%= group.title %>
399
408
 
409
+ ### <%= group.title %>
400
410
  <% group.commits.forEach((commit) => { %>
411
+
401
412
  * <%= commit.line %>
402
- <% }) %>
413
+ <% }); %>
403
414
 
404
415
  <% } %>
405
- <% }) %>
416
+ <% }); %>
406
417
  `;
407
418
  async function generateChangelogEntry(options) {
408
419
  const { packageName, version, previousVersion, date, commits, owner, repo, groups, template, githubClient } = options;
@@ -411,16 +422,7 @@ async function generateChangelogEntry(options) {
411
422
  includeNonConventional: false,
412
423
  mergeKeys: Object.fromEntries(groups.map((g) => [g.name, g.types]))
413
424
  });
414
- for (const commit of commits) {
415
- if (commit.authors.length === 0) continue;
416
- commit.resolvedAuthors = await Promise.all(commit.authors.map(async (author) => {
417
- const username = await githubClient.resolveAuthorInfo(author.email);
418
- return {
419
- ...author,
420
- username
421
- };
422
- }));
423
- }
425
+ const commitAuthors = await resolveCommitAuthors(commits, githubClient);
424
426
  const templateData = {
425
427
  packageName,
426
428
  version,
@@ -432,27 +434,12 @@ async function generateChangelogEntry(options) {
432
434
  groups: groups.map((group) => {
433
435
  const commitsInGroup = grouped.get(group.name) ?? [];
434
436
  if (commitsInGroup.length > 0) logger.verbose(`Found ${commitsInGroup.length} commits for group "${group.name}".`);
435
- const formattedCommits = commitsInGroup.map((commit) => {
436
- const commitUrl = `https://github.com/${owner}/${repo}/commit/${commit.hash}`;
437
- let line = `${commit.description}`;
438
- if (commit.references.length > 0) logger.verbose("Located references in commit", commit.references.length);
439
- for (const ref of commit.references) {
440
- if (!ref.value) continue;
441
- const number = Number.parseInt(ref.value.replace(/^#/, ""), 10);
442
- if (Number.isNaN(number)) continue;
443
- if (ref.type === "issue") {
444
- line += ` ([Issue ${ref.value}](https://github.com/${owner}/${repo}/issues/${number}))`;
445
- continue;
446
- }
447
- line += ` ([PR ${ref.value}](https://github.com/${owner}/${repo}/pull/${number}))`;
448
- }
449
- line += ` ([${commit.shortHash}](${commitUrl}))`;
450
- if (commit.authors.length > 0) {
451
- console.log(commit.resolvedAuthors);
452
- line += ` (by ${commit.authors.map((a) => a.name).join(", ")})`;
453
- }
454
- return { line };
455
- });
437
+ const formattedCommits = commitsInGroup.map((commit) => ({ line: formatCommitLine({
438
+ commit,
439
+ owner,
440
+ repo,
441
+ authors: commitAuthors.get(commit.hash) ?? []
442
+ }) }));
456
443
  return {
457
444
  name: group.name,
458
445
  title: group.title,
@@ -465,7 +452,7 @@ async function generateChangelogEntry(options) {
465
452
  return eta.renderString(templateToUse, templateData).trim();
466
453
  }
467
454
  async function updateChangelog(options) {
468
- const { version, previousVersion, commits, date, normalizedOptions, workspacePackage } = options;
455
+ const { version, previousVersion, commits, date, normalizedOptions, workspacePackage, githubClient } = options;
469
456
  const changelogPath = join(workspacePackage.path, "CHANGELOG.md");
470
457
  const changelogRelativePath = relative(normalizedOptions.workspaceRoot, join(workspacePackage.path, "CHANGELOG.md"));
471
458
  const existingContent = await readFileFromGit(normalizedOptions.workspaceRoot, normalizedOptions.branch.default, changelogRelativePath);
@@ -480,7 +467,7 @@ async function updateChangelog(options) {
480
467
  repo: normalizedOptions.repo,
481
468
  groups: normalizedOptions.groups,
482
469
  template: normalizedOptions.changelog?.template,
483
- githubClient: options.githubClient
470
+ githubClient
484
471
  });
485
472
  let updatedContent;
486
473
  if (!existingContent) {
@@ -514,6 +501,56 @@ async function updateChangelog(options) {
514
501
  }
515
502
  await writeFile(changelogPath, updatedContent, "utf-8");
516
503
  }
504
+ async function resolveCommitAuthors(commits, githubClient) {
505
+ const authorsToResolve = /* @__PURE__ */ new Set();
506
+ const commitAuthors = /* @__PURE__ */ new Map();
507
+ for (const commit of commits) {
508
+ const authorsForCommit = [];
509
+ commit.authors.forEach((author, idx) => {
510
+ if (!author.email || !author.name) return;
511
+ let info = globalAuthorCache.get(author.email);
512
+ if (!info) {
513
+ info = {
514
+ commits: [],
515
+ name: author.name,
516
+ email: author.email
517
+ };
518
+ globalAuthorCache.set(author.email, info);
519
+ }
520
+ if (idx === 0) info.commits.push(commit.shortHash);
521
+ authorsForCommit.push(info);
522
+ if (!info.login) authorsToResolve.add(info);
523
+ });
524
+ commitAuthors.set(commit.hash, authorsForCommit);
525
+ }
526
+ await Promise.all(Array.from(authorsToResolve).map((info) => githubClient.resolveAuthorInfo(info)));
527
+ return commitAuthors;
528
+ }
529
+ function formatCommitLine({ commit, owner, repo, authors }) {
530
+ const commitUrl = `https://github.com/${owner}/${repo}/commit/${commit.hash}`;
531
+ let line = `${commit.description}`;
532
+ const references = commit.references ?? [];
533
+ if (references.length > 0) logger.verbose("Located references in commit", references.length);
534
+ for (const ref of references) {
535
+ if (!ref.value) continue;
536
+ const number = Number.parseInt(ref.value.replace(/^#/, ""), 10);
537
+ if (Number.isNaN(number)) continue;
538
+ if (ref.type === "issue") {
539
+ line += ` ([Issue ${ref.value}](https://github.com/${owner}/${repo}/issues/${number}))`;
540
+ continue;
541
+ }
542
+ line += ` ([PR ${ref.value}](https://github.com/${owner}/${repo}/pull/${number}))`;
543
+ }
544
+ line += ` ([${commit.shortHash}](${commitUrl}))`;
545
+ if (authors.length > 0) {
546
+ const authorList = authors.map((author) => {
547
+ if (author.login) return `[@${author.login}](https://github.com/${author.login})`;
548
+ return author.name;
549
+ }).join(", ");
550
+ line += ` (by ${authorList})`;
551
+ }
552
+ return line;
553
+ }
517
554
  function parseChangelog(content) {
518
555
  const lines = content.split("\n");
519
556
  let packageName = null;
@@ -647,11 +684,24 @@ var GitHubClient = class {
647
684
  });
648
685
  logger.info(`Commit status set to ${farver.cyan(state)} for ${farver.gray(sha.substring(0, 7))}`);
649
686
  }
650
- async resolveAuthorInfo(email) {
651
- const q = encodeURIComponent(`${email} type:user in:email`);
652
- const data = await this.request(`/search/users?q=${q}`);
653
- if (!data.items || data.items.length === 0) return null;
654
- return data.items[0].login;
687
+ async resolveAuthorInfo(info) {
688
+ if (info.login) return info;
689
+ try {
690
+ const q = encodeURIComponent(`${info.email} type:user in:email`);
691
+ const data = await this.request(`/search/users?q=${q}`);
692
+ if (!data.items || data.items.length === 0) return info;
693
+ info.login = data.items[0].login;
694
+ } catch (err) {
695
+ logger.warn(`Failed to resolve author info for email ${info.email}: ${err.message}`);
696
+ }
697
+ if (info.login) return info;
698
+ if (info.commits.length > 0) try {
699
+ const data = await this.request(`/repos/${this.owner}/${this.repo}/commits/${info.commits[0]}`);
700
+ if (data.author && data.author.login) info.login = data.author.login;
701
+ } catch (err) {
702
+ logger.warn(`Failed to resolve author info from commits for email ${info.email}: ${err.message}`);
703
+ }
704
+ return info;
655
705
  }
656
706
  };
657
707
  function createGitHubClient(options) {
@@ -1051,9 +1101,16 @@ function formatCommitsForDisplay(commits) {
1051
1101
  /**
1052
1102
  * Calculate version updates for packages based on their commits
1053
1103
  */
1054
- async function calculateVersionUpdates({ workspacePackages, packageCommits, workspaceRoot, showPrompt, globalCommitsPerPackage }) {
1104
+ async function calculateVersionUpdates({ workspacePackages, packageCommits, workspaceRoot, showPrompt, globalCommitsPerPackage, overrides: initialOverrides = {} }) {
1055
1105
  const versionUpdates = [];
1056
1106
  const processedPackages = /* @__PURE__ */ new Set();
1107
+ const newOverrides = { ...initialOverrides };
1108
+ const bumpRanks = {
1109
+ major: 3,
1110
+ minor: 2,
1111
+ patch: 1,
1112
+ none: 0
1113
+ };
1057
1114
  logger.verbose(`Starting version inference for ${packageCommits.size} packages with commits`);
1058
1115
  for (const [pkgName, pkgCommits] of packageCommits) {
1059
1116
  const pkg = workspacePackages.find((p) => p.name === pkgName);
@@ -1063,30 +1120,39 @@ async function calculateVersionUpdates({ workspacePackages, packageCommits, work
1063
1120
  }
1064
1121
  processedPackages.add(pkgName);
1065
1122
  const globalCommits = globalCommitsPerPackage.get(pkgName) || [];
1066
- if (globalCommits.length > 0) logger.verbose(` - Global commits for this package: ${globalCommits.length}`);
1067
1123
  const allCommitsForPackage = [...pkgCommits, ...globalCommits];
1068
- const bump = determineHighestBump(allCommitsForPackage);
1069
- if (bump === "none") continue;
1070
- let newVersion = getNextVersion(pkg.version, bump);
1124
+ const determinedBump = determineHighestBump(allCommitsForPackage);
1125
+ const effectiveBump = newOverrides[pkgName] || determinedBump;
1126
+ if (effectiveBump === "none") continue;
1127
+ let newVersion = getNextVersion(pkg.version, effectiveBump);
1071
1128
  if (!isCI && showPrompt) {
1072
- logger.section("📝 Commits affecting this package");
1129
+ logger.clearScreen();
1130
+ logger.section(`📝 Commits for ${farver.cyan(pkg.name)}`);
1073
1131
  formatCommitsForDisplay(allCommitsForPackage).split("\n").forEach((line) => logger.item(line));
1074
1132
  logger.emptyLine();
1075
1133
  const selectedVersion = await selectVersionPrompt(workspaceRoot, pkg, pkg.version, newVersion);
1076
1134
  if (selectedVersion === null) continue;
1135
+ const userBump = _calculateBumpType(pkg.version, selectedVersion);
1136
+ if (bumpRanks[userBump] < bumpRanks[determinedBump]) {
1137
+ newOverrides[pkgName] = userBump;
1138
+ logger.info(`Version override recorded for ${pkgName}: ${determinedBump} → ${userBump}`);
1139
+ } else if (newOverrides[pkgName] && bumpRanks[userBump] >= bumpRanks[determinedBump]) {
1140
+ delete newOverrides[pkgName];
1141
+ logger.info(`Version override removed for ${pkgName}.`);
1142
+ }
1077
1143
  newVersion = selectedVersion;
1078
1144
  }
1079
- logger.verbose(`Version update: ${pkg.version} → ${newVersion}`);
1080
1145
  versionUpdates.push({
1081
1146
  package: pkg,
1082
1147
  currentVersion: pkg.version,
1083
1148
  newVersion,
1084
- bumpType: bump,
1085
- hasDirectChanges: true
1149
+ bumpType: effectiveBump,
1150
+ hasDirectChanges: allCommitsForPackage.length > 0
1086
1151
  });
1087
1152
  }
1088
1153
  if (!isCI && showPrompt) for (const pkg of workspacePackages) {
1089
1154
  if (processedPackages.has(pkg.name)) continue;
1155
+ logger.clearScreen();
1090
1156
  logger.section(`📦 Package: ${pkg.name}`);
1091
1157
  logger.item("No direct commits found");
1092
1158
  const newVersion = await selectVersionPrompt(workspaceRoot, pkg, pkg.version, pkg.version);
@@ -1102,19 +1168,23 @@ async function calculateVersionUpdates({ workspacePackages, packageCommits, work
1102
1168
  });
1103
1169
  }
1104
1170
  }
1105
- return versionUpdates;
1171
+ return {
1172
+ updates: versionUpdates,
1173
+ overrides: newOverrides
1174
+ };
1106
1175
  }
1107
1176
  /**
1108
1177
  * Calculate version updates and prepare dependent updates
1109
1178
  * Returns both the updates and a function to apply them
1110
1179
  */
1111
- async function calculateAndPrepareVersionUpdates({ workspacePackages, packageCommits, workspaceRoot, showPrompt, globalCommitsPerPackage }) {
1112
- const directUpdates = await calculateVersionUpdates({
1180
+ async function calculateAndPrepareVersionUpdates({ workspacePackages, packageCommits, workspaceRoot, showPrompt, globalCommitsPerPackage, overrides }) {
1181
+ const { updates: directUpdates, overrides: newOverrides } = await calculateVersionUpdates({
1113
1182
  workspacePackages,
1114
1183
  packageCommits,
1115
1184
  workspaceRoot,
1116
1185
  showPrompt,
1117
- globalCommitsPerPackage
1186
+ globalCommitsPerPackage,
1187
+ overrides
1118
1188
  });
1119
1189
  const allUpdates = createDependentUpdates(buildPackageDependencyGraph(workspacePackages), workspacePackages, directUpdates);
1120
1190
  const applyUpdates = async () => {
@@ -1125,7 +1195,8 @@ async function calculateAndPrepareVersionUpdates({ workspacePackages, packageCom
1125
1195
  };
1126
1196
  return {
1127
1197
  allUpdates,
1128
- applyUpdates
1198
+ applyUpdates,
1199
+ overrides: newOverrides
1129
1200
  };
1130
1201
  }
1131
1202
  async function updatePackageJson(pkg, newVersion, dependencyUpdates) {
@@ -1396,7 +1467,10 @@ async function normalizeReleaseOptions(options) {
1396
1467
  title: options.pullRequest?.title ?? "chore: release new version",
1397
1468
  body: options.pullRequest?.body ?? DEFAULT_PR_BODY_TEMPLATE
1398
1469
  },
1399
- changelog: { enabled: options.changelog?.enabled ?? true }
1470
+ changelog: {
1471
+ enabled: options.changelog?.enabled ?? true,
1472
+ template: options.changelog?.template ?? DEFAULT_CHANGELOG_TEMPLATE
1473
+ }
1400
1474
  };
1401
1475
  }
1402
1476
 
@@ -1419,17 +1493,6 @@ async function release(options) {
1419
1493
  logger.emptyLine();
1420
1494
  const groupedPackageCommits = await getWorkspacePackageGroupedCommits(workspaceRoot, workspacePackages);
1421
1495
  const globalCommitsPerPackage = await getGlobalCommitsPerPackage(workspaceRoot, groupedPackageCommits, workspacePackages, normalizedOptions.globalCommitMode);
1422
- const { allUpdates, applyUpdates } = await calculateAndPrepareVersionUpdates({
1423
- workspacePackages,
1424
- packageCommits: groupedPackageCommits,
1425
- workspaceRoot,
1426
- showPrompt: options.prompts?.versions !== false,
1427
- globalCommitsPerPackage
1428
- });
1429
- if (allUpdates.filter((u) => u.hasDirectChanges).length === 0) logger.warn("No packages have changes requiring a release");
1430
- logger.section("🔄 Version Updates");
1431
- logger.item(`Updating ${allUpdates.length} packages (including dependents)`);
1432
- for (const update of allUpdates) logger.item(`${update.package.name}: ${update.currentVersion} → ${update.newVersion}`);
1433
1496
  const githubClient = createGitHubClient({
1434
1497
  owner: normalizedOptions.owner,
1435
1498
  repo: normalizedOptions.repo,
@@ -1444,6 +1507,38 @@ async function release(options) {
1444
1507
  pullRequestBody: options.pullRequest?.body
1445
1508
  });
1446
1509
  await prOps.prepareBranch();
1510
+ const overridesPath = join(workspaceRoot, ".github", "ucdjs.release.overrides.json");
1511
+ let existingOverrides = {};
1512
+ try {
1513
+ const overridesContent = await readFile(overridesPath, "utf-8");
1514
+ existingOverrides = JSON.parse(overridesContent);
1515
+ logger.info("Found existing version overrides file.");
1516
+ } catch {
1517
+ logger.info("No existing version overrides file found. Continuing...");
1518
+ }
1519
+ const { allUpdates, applyUpdates, overrides: newOverrides } = await calculateAndPrepareVersionUpdates({
1520
+ workspacePackages,
1521
+ packageCommits: groupedPackageCommits,
1522
+ workspaceRoot,
1523
+ showPrompt: options.prompts?.versions !== false,
1524
+ globalCommitsPerPackage,
1525
+ overrides: existingOverrides
1526
+ });
1527
+ if (Object.keys(newOverrides).length > 0) {
1528
+ logger.info("Writing version overrides file...");
1529
+ try {
1530
+ await mkdir(join(workspaceRoot, ".github"), { recursive: true });
1531
+ await writeFile(overridesPath, JSON.stringify(newOverrides, null, 2), "utf-8");
1532
+ logger.success("Successfully wrote version overrides file.");
1533
+ } catch (e) {
1534
+ logger.error("Failed to write version overrides file:", e);
1535
+ }
1536
+ }
1537
+ if (allUpdates.filter((u) => u.hasDirectChanges).length === 0) logger.warn("No packages have changes requiring a release");
1538
+ logger.section("🔄 Version Updates");
1539
+ logger.item(`Updating ${allUpdates.length} packages (including dependents)`);
1540
+ for (const update of allUpdates) logger.item(`${update.package.name}: ${update.currentVersion} → ${update.newVersion}`);
1541
+ await prOps.prepareBranch();
1447
1542
  await applyUpdates();
1448
1543
  if (normalizedOptions.changelog.enabled) {
1449
1544
  logger.step("Updating changelogs");
@@ -1554,7 +1649,87 @@ async function orchestrateReleasePullRequest({ workspaceRoot, githubClient, rele
1554
1649
 
1555
1650
  //#endregion
1556
1651
  //#region src/verify.ts
1557
- function verify(_options) {}
1652
+ async function verify(options) {
1653
+ const { workspaceRoot,...normalizedOptions } = await normalizeReleaseOptions(options);
1654
+ if (normalizedOptions.safeguards && !await isWorkingDirectoryClean(workspaceRoot)) exitWithError("Working directory is not clean. Please commit or stash your changes before proceeding.");
1655
+ const githubClient = createGitHubClient({
1656
+ owner: normalizedOptions.owner,
1657
+ repo: normalizedOptions.repo,
1658
+ githubToken: normalizedOptions.githubToken
1659
+ });
1660
+ const releaseBranch = normalizedOptions.branch.release;
1661
+ const defaultBranch = normalizedOptions.branch.default;
1662
+ const releasePr = await githubClient.getExistingPullRequest(releaseBranch);
1663
+ if (!releasePr || !releasePr.head) {
1664
+ logger.warn(`No open release pull request found for branch "${releaseBranch}". Nothing to verify.`);
1665
+ return;
1666
+ }
1667
+ logger.info(`Found release PR #${releasePr.number}. Verifying against default branch "${defaultBranch}"...`);
1668
+ const originalBranch = await getCurrentBranch(workspaceRoot);
1669
+ if (originalBranch !== defaultBranch) await checkoutBranch(defaultBranch, workspaceRoot);
1670
+ const overridesPath = join(".github", "ucdjs.release.overrides.json");
1671
+ let existingOverrides = {};
1672
+ try {
1673
+ const overridesContent = await readFileFromGit(workspaceRoot, releasePr.head.sha, overridesPath);
1674
+ if (overridesContent) {
1675
+ existingOverrides = JSON.parse(overridesContent);
1676
+ logger.info("Found existing version overrides file on release branch.");
1677
+ }
1678
+ } catch {
1679
+ logger.info("No version overrides file found on release branch. Continuing...");
1680
+ }
1681
+ const mainPackages = await discoverWorkspacePackages(workspaceRoot, options);
1682
+ const mainCommits = await getWorkspacePackageGroupedCommits(workspaceRoot, mainPackages);
1683
+ const { allUpdates: expectedUpdates } = await calculateAndPrepareVersionUpdates({
1684
+ workspacePackages: mainPackages,
1685
+ packageCommits: mainCommits,
1686
+ workspaceRoot,
1687
+ showPrompt: false,
1688
+ globalCommitsPerPackage: await getGlobalCommitsPerPackage(workspaceRoot, mainCommits, mainPackages, normalizedOptions.globalCommitMode),
1689
+ overrides: existingOverrides
1690
+ });
1691
+ const expectedVersionMap = new Map(expectedUpdates.map((u) => [u.package.name, u.newVersion]));
1692
+ const prVersionMap = /* @__PURE__ */ new Map();
1693
+ for (const pkg of mainPackages) {
1694
+ const pkgJsonPath = join(pkg.path.replace(workspaceRoot, ""), "package.json").substring(1);
1695
+ const pkgJsonContent = await readFileFromGit(workspaceRoot, releasePr.head.sha, pkgJsonPath);
1696
+ if (pkgJsonContent) {
1697
+ const pkgJson = JSON.parse(pkgJsonContent);
1698
+ prVersionMap.set(pkg.name, pkgJson.version);
1699
+ }
1700
+ }
1701
+ if (originalBranch !== defaultBranch) await checkoutBranch(originalBranch, workspaceRoot);
1702
+ let isOutOfSync = false;
1703
+ for (const [pkgName, expectedVersion] of expectedVersionMap.entries()) {
1704
+ const prVersion = prVersionMap.get(pkgName);
1705
+ if (!prVersion) {
1706
+ logger.warn(`Package "${pkgName}" found in default branch but not in release branch. Skipping.`);
1707
+ continue;
1708
+ }
1709
+ if (gt(expectedVersion, prVersion)) {
1710
+ logger.error(`Package "${pkgName}" is out of sync. Expected version >= ${expectedVersion}, but PR has ${prVersion}.`);
1711
+ isOutOfSync = true;
1712
+ } else logger.success(`Package "${pkgName}" is up to date (PR version: ${prVersion}, Expected: ${expectedVersion})`);
1713
+ }
1714
+ const statusContext = "ucdjs/release-verify";
1715
+ if (isOutOfSync) {
1716
+ await githubClient.setCommitStatus({
1717
+ sha: releasePr.head.sha,
1718
+ state: "failure",
1719
+ context: statusContext,
1720
+ description: "Release PR is out of sync with the default branch. Please re-run the release process."
1721
+ });
1722
+ logger.error("Verification failed. Commit status set to 'failure'.");
1723
+ } else {
1724
+ await githubClient.setCommitStatus({
1725
+ sha: releasePr.head.sha,
1726
+ state: "success",
1727
+ context: statusContext,
1728
+ description: "Release PR is up to date."
1729
+ });
1730
+ logger.success("Verification successful. Commit status set to 'success'.");
1731
+ }
1732
+ }
1558
1733
 
1559
1734
  //#endregion
1560
1735
  export { publish, release, verify };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ucdjs/release-scripts",
3
- "version": "0.1.0-beta.16",
3
+ "version": "0.1.0-beta.18",
4
4
  "description": "@ucdjs release scripts",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -32,12 +32,14 @@
32
32
  "farver": "1.0.0-beta.1",
33
33
  "mri": "1.2.0",
34
34
  "prompts": "2.4.2",
35
+ "semver": "7.7.3",
35
36
  "tinyexec": "1.0.2"
36
37
  },
37
38
  "devDependencies": {
38
39
  "@luxass/eslint-config": "6.0.1",
39
40
  "@types/node": "22.18.12",
40
41
  "@types/prompts": "2.4.9",
42
+ "@types/semver": "7.7.1",
41
43
  "eslint": "9.39.1",
42
44
  "eta": "4.0.1",
43
45
  "tsdown": "0.16.0",