@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 +8 -2
- package/dist/index.mjs +246 -71
- package/package.json +3 -1
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
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
|
|
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(
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
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
|
|
1069
|
-
|
|
1070
|
-
|
|
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.
|
|
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:
|
|
1085
|
-
hasDirectChanges:
|
|
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
|
|
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: {
|
|
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(
|
|
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.
|
|
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",
|