@ucdjs/release-scripts 0.1.0-beta.15 → 0.1.0-beta.17
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/{eta-Boh7yPZi.mjs → eta-j5TFRbI4.mjs} +3 -3
- package/dist/index.d.mts +30 -2
- package/dist/index.mjs +482 -149
- package/package.json +6 -4
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
1
|
import * as path from "node:path";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
3
|
|
|
4
4
|
//#region node_modules/.pnpm/eta@4.0.1/node_modules/eta/dist/index.js
|
|
5
5
|
var EtaError = class extends Error {
|
|
@@ -90,8 +90,8 @@ function resolvePath(templatePath, options) {
|
|
|
90
90
|
} else throw new EtaFileResolutionError(`Template '${templatePath}' is not in the views directory`);
|
|
91
91
|
}
|
|
92
92
|
function dirIsChild(parent, dir) {
|
|
93
|
-
const relative = path.relative(parent, dir);
|
|
94
|
-
return relative && !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
93
|
+
const relative$1 = path.relative(parent, dir);
|
|
94
|
+
return relative$1 && !relative$1.startsWith("..") && !path.isAbsolute(relative$1);
|
|
95
95
|
}
|
|
96
96
|
const absolutePathRegExp = /^\\|^\//;
|
|
97
97
|
/* istanbul ignore next */
|
package/dist/index.d.mts
CHANGED
|
@@ -11,11 +11,25 @@ interface WorkspacePackage {
|
|
|
11
11
|
//#region src/shared/types.d.ts
|
|
12
12
|
type BumpKind = "none" | "patch" | "minor" | "major";
|
|
13
13
|
type GlobalCommitMode = false | "dependencies" | "all";
|
|
14
|
+
interface CommitGroup {
|
|
15
|
+
/**
|
|
16
|
+
* Unique identifier for the group
|
|
17
|
+
*/
|
|
18
|
+
name: string;
|
|
19
|
+
/**
|
|
20
|
+
* Display title (e.g., "Features", "Bug Fixes")
|
|
21
|
+
*/
|
|
22
|
+
title: string;
|
|
23
|
+
/**
|
|
24
|
+
* Conventional commit types to include in this group
|
|
25
|
+
*/
|
|
26
|
+
types: string[];
|
|
27
|
+
}
|
|
14
28
|
interface SharedOptions {
|
|
15
29
|
/**
|
|
16
30
|
* Repository identifier (e.g., "owner/repo")
|
|
17
31
|
*/
|
|
18
|
-
repo: string
|
|
32
|
+
repo: `${string}/${string}`;
|
|
19
33
|
/**
|
|
20
34
|
* Root directory of the workspace (defaults to process.cwd())
|
|
21
35
|
*/
|
|
@@ -44,6 +58,12 @@ interface SharedOptions {
|
|
|
44
58
|
*/
|
|
45
59
|
versions?: boolean;
|
|
46
60
|
};
|
|
61
|
+
/**
|
|
62
|
+
* Commit grouping configuration
|
|
63
|
+
* Used for changelog generation and commit display
|
|
64
|
+
* @default DEFAULT_COMMIT_GROUPS
|
|
65
|
+
*/
|
|
66
|
+
groups?: CommitGroup[];
|
|
47
67
|
}
|
|
48
68
|
interface PackageJson {
|
|
49
69
|
name: string;
|
|
@@ -136,6 +156,10 @@ interface ReleaseOptions extends SharedOptions {
|
|
|
136
156
|
* @default true
|
|
137
157
|
*/
|
|
138
158
|
enabled?: boolean;
|
|
159
|
+
/**
|
|
160
|
+
* Custom changelog entry template (ETA format)
|
|
161
|
+
*/
|
|
162
|
+
template?: string;
|
|
139
163
|
};
|
|
140
164
|
globalCommitMode?: GlobalCommitMode;
|
|
141
165
|
}
|
|
@@ -155,4 +179,8 @@ interface ReleaseResult {
|
|
|
155
179
|
}
|
|
156
180
|
declare function release(options: ReleaseOptions): Promise<ReleaseResult | null>;
|
|
157
181
|
//#endregion
|
|
158
|
-
|
|
182
|
+
//#region src/verify.d.ts
|
|
183
|
+
interface VerifyOptions extends SharedOptions {}
|
|
184
|
+
declare function verify(_options: VerifyOptions): void;
|
|
185
|
+
//#endregion
|
|
186
|
+
export { type PublishOptions, type ReleaseOptions, type ReleaseResult, type VerifyOptions, publish, release, verify };
|
package/dist/index.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { t as Eta } from "./eta-
|
|
1
|
+
import { t as Eta } from "./eta-j5TFRbI4.mjs";
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
2
4
|
import process from "node:process";
|
|
3
5
|
import farver from "farver";
|
|
4
6
|
import mri from "mri";
|
|
5
7
|
import { exec } from "tinyexec";
|
|
6
8
|
import { dedent } from "@luxass/utils";
|
|
7
|
-
import {
|
|
8
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
9
|
-
import { getCommits } from "commit-parser";
|
|
9
|
+
import { getCommits, groupByType } from "commit-parser";
|
|
10
10
|
import prompts from "prompts";
|
|
11
11
|
|
|
12
12
|
//#region src/publish.ts
|
|
@@ -49,8 +49,8 @@ const logger = {
|
|
|
49
49
|
emptyLine: () => {
|
|
50
50
|
console.log();
|
|
51
51
|
},
|
|
52
|
-
item: (message) => {
|
|
53
|
-
console.log(` ${message}
|
|
52
|
+
item: (message, ...args$1) => {
|
|
53
|
+
console.log(` ${message}`, ...args$1);
|
|
54
54
|
},
|
|
55
55
|
step: (message) => {
|
|
56
56
|
console.log(` ${farver.blue("→")} ${message}`);
|
|
@@ -78,28 +78,6 @@ function exitWithError(message, hint) {
|
|
|
78
78
|
if (hint) console.error(farver.gray(` ${hint}`));
|
|
79
79
|
process.exit(1);
|
|
80
80
|
}
|
|
81
|
-
function normalizeSharedOptions(options) {
|
|
82
|
-
const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, prompts: prompts$1 = {
|
|
83
|
-
packages: true,
|
|
84
|
-
versions: true
|
|
85
|
-
},...rest } = options;
|
|
86
|
-
if (!githubToken.trim()) exitWithError("GitHub token is required", "Set GITHUB_TOKEN environment variable or pass it in options");
|
|
87
|
-
if (!fullRepo || !fullRepo.trim() || !fullRepo.includes("/")) exitWithError("Repository (repo) is required", "Specify the repository in 'owner/repo' format (e.g., 'octocat/hello-world')");
|
|
88
|
-
const [owner, repo] = fullRepo.split("/");
|
|
89
|
-
if (!owner || !repo) exitWithError(`Invalid repo format: "${fullRepo}"`, "Expected format: \"owner/repo\" (e.g., \"octocat/hello-world\")");
|
|
90
|
-
return {
|
|
91
|
-
...rest,
|
|
92
|
-
packages,
|
|
93
|
-
prompts: {
|
|
94
|
-
packages: prompts$1?.packages ?? true,
|
|
95
|
-
versions: prompts$1?.versions ?? true
|
|
96
|
-
},
|
|
97
|
-
workspaceRoot,
|
|
98
|
-
githubToken,
|
|
99
|
-
owner,
|
|
100
|
-
repo
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
81
|
if (isDryRun || isVerbose || isForce) {
|
|
104
82
|
logger.verbose(farver.inverse(farver.yellow(" Running with special flags ")));
|
|
105
83
|
logger.verbose({
|
|
@@ -328,19 +306,314 @@ async function pushBranch(branch, workspaceRoot, options) {
|
|
|
328
306
|
exitWithError(`Failed to push branch: ${branch}`, `Make sure you have permission to push to the remote repository`);
|
|
329
307
|
}
|
|
330
308
|
}
|
|
309
|
+
async function readFileFromGit(workspaceRoot, ref, filePath) {
|
|
310
|
+
try {
|
|
311
|
+
return (await run("git", ["show", `${ref}:${filePath}`], { nodeOptions: {
|
|
312
|
+
cwd: workspaceRoot,
|
|
313
|
+
stdio: "pipe"
|
|
314
|
+
} })).stdout;
|
|
315
|
+
} catch {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
async function getMostRecentPackageTag(workspaceRoot, packageName) {
|
|
320
|
+
try {
|
|
321
|
+
const { stdout } = await run("git", [
|
|
322
|
+
"tag",
|
|
323
|
+
"--list",
|
|
324
|
+
`${packageName}@*`
|
|
325
|
+
], { nodeOptions: {
|
|
326
|
+
cwd: workspaceRoot,
|
|
327
|
+
stdio: "pipe"
|
|
328
|
+
} });
|
|
329
|
+
const tags = stdout.split("\n").map((tag) => tag.trim()).filter(Boolean);
|
|
330
|
+
if (tags.length === 0) return;
|
|
331
|
+
return tags.reverse()[0];
|
|
332
|
+
} catch (err) {
|
|
333
|
+
logger.warn(`Failed to get tags for package ${packageName}: ${err.message}`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Builds a mapping of commit SHAs to the list of files changed in each commit
|
|
339
|
+
* within a given inclusive range.
|
|
340
|
+
*
|
|
341
|
+
* Internally runs:
|
|
342
|
+
* git log --name-only --format=%H <from>^..<to>
|
|
343
|
+
*
|
|
344
|
+
* Notes
|
|
345
|
+
* - This includes the commit identified by `from` (via `from^..to`).
|
|
346
|
+
* - Order of commits in the resulting Map follows `git log` output
|
|
347
|
+
* (reverse chronological, newest first).
|
|
348
|
+
* - On failure (e.g., invalid refs), the function returns null.
|
|
349
|
+
*
|
|
350
|
+
* @param {string} workspaceRoot Absolute path to the git repository root used as cwd.
|
|
351
|
+
* @param {string} from Starting commit/ref (inclusive).
|
|
352
|
+
* @param {string} to Ending commit/ref (inclusive).
|
|
353
|
+
* @returns {Promise<Map<string, string[]> | null>} Promise resolving to a Map where keys are commit SHAs and values are
|
|
354
|
+
* arrays of file paths changed by that commit, or null on error.
|
|
355
|
+
*/
|
|
356
|
+
async function getGroupedFilesByCommitSha(workspaceRoot, from, to) {
|
|
357
|
+
const commitsMap = /* @__PURE__ */ new Map();
|
|
358
|
+
try {
|
|
359
|
+
const { stdout } = await run("git", [
|
|
360
|
+
"log",
|
|
361
|
+
"--name-only",
|
|
362
|
+
"--format=%H",
|
|
363
|
+
`${from}^..${to}`
|
|
364
|
+
], { nodeOptions: {
|
|
365
|
+
cwd: workspaceRoot,
|
|
366
|
+
stdio: "pipe"
|
|
367
|
+
} });
|
|
368
|
+
const lines = stdout.trim().split("\n").filter((line) => line.trim() !== "");
|
|
369
|
+
let currentSha = null;
|
|
370
|
+
const HASH_REGEX = /^[0-9a-f]{40}$/i;
|
|
371
|
+
for (const line of lines) {
|
|
372
|
+
const trimmedLine = line.trim();
|
|
373
|
+
if (HASH_REGEX.test(trimmedLine)) {
|
|
374
|
+
currentSha = trimmedLine;
|
|
375
|
+
commitsMap.set(currentSha, []);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
if (currentSha === null) continue;
|
|
379
|
+
commitsMap.get(currentSha).push(trimmedLine);
|
|
380
|
+
}
|
|
381
|
+
return commitsMap;
|
|
382
|
+
} catch {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
//#endregion
|
|
388
|
+
//#region src/core/changelog.ts
|
|
389
|
+
const DEFAULT_CHANGELOG_TEMPLATE = dedent`
|
|
390
|
+
<% if (it.previousVersion) { -%>
|
|
391
|
+
## [<%= it.version %>](<%= it.compareUrl %>) (<%= it.date %>)
|
|
392
|
+
<% } else { -%>
|
|
393
|
+
## <%= it.version %> (<%= it.date %>)
|
|
394
|
+
<% } %>
|
|
395
|
+
|
|
396
|
+
<% it.groups.forEach((group) => { %>
|
|
397
|
+
<% if (group.commits.length > 0) { %>
|
|
398
|
+
|
|
399
|
+
### <%= group.title %>
|
|
400
|
+
<% group.commits.forEach((commit) => { %>
|
|
401
|
+
|
|
402
|
+
* <%= commit.line %>
|
|
403
|
+
<% }); %>
|
|
404
|
+
|
|
405
|
+
<% } %>
|
|
406
|
+
<% }); %>
|
|
407
|
+
`;
|
|
408
|
+
async function generateChangelogEntry(options) {
|
|
409
|
+
const { packageName, version, previousVersion, date, commits, owner, repo, groups, template, githubClient } = options;
|
|
410
|
+
const compareUrl = previousVersion ? `https://github.com/${owner}/${repo}/compare/${packageName}@${previousVersion}...${packageName}@${version}` : void 0;
|
|
411
|
+
const grouped = groupByType(commits, {
|
|
412
|
+
includeNonConventional: false,
|
|
413
|
+
mergeKeys: Object.fromEntries(groups.map((g) => [g.name, g.types]))
|
|
414
|
+
});
|
|
415
|
+
const commitAuthors = await resolveCommitAuthors(commits, githubClient);
|
|
416
|
+
const templateData = {
|
|
417
|
+
packageName,
|
|
418
|
+
version,
|
|
419
|
+
previousVersion,
|
|
420
|
+
date,
|
|
421
|
+
compareUrl,
|
|
422
|
+
owner,
|
|
423
|
+
repo,
|
|
424
|
+
groups: groups.map((group) => {
|
|
425
|
+
const commitsInGroup = grouped.get(group.name) ?? [];
|
|
426
|
+
if (commitsInGroup.length > 0) logger.verbose(`Found ${commitsInGroup.length} commits for group "${group.name}".`);
|
|
427
|
+
const formattedCommits = commitsInGroup.map((commit) => ({ line: formatCommitLine({
|
|
428
|
+
commit,
|
|
429
|
+
owner,
|
|
430
|
+
repo,
|
|
431
|
+
authors: commitAuthors.get(commit.hash) ?? []
|
|
432
|
+
}) }));
|
|
433
|
+
return {
|
|
434
|
+
name: group.name,
|
|
435
|
+
title: group.title,
|
|
436
|
+
commits: formattedCommits
|
|
437
|
+
};
|
|
438
|
+
})
|
|
439
|
+
};
|
|
440
|
+
const eta = new Eta();
|
|
441
|
+
const templateToUse = template || DEFAULT_CHANGELOG_TEMPLATE;
|
|
442
|
+
return eta.renderString(templateToUse, templateData).trim();
|
|
443
|
+
}
|
|
444
|
+
async function updateChangelog(options) {
|
|
445
|
+
const { version, previousVersion, commits, date, normalizedOptions, workspacePackage, githubClient } = options;
|
|
446
|
+
const changelogPath = join(workspacePackage.path, "CHANGELOG.md");
|
|
447
|
+
const changelogRelativePath = relative(normalizedOptions.workspaceRoot, join(workspacePackage.path, "CHANGELOG.md"));
|
|
448
|
+
const existingContent = await readFileFromGit(normalizedOptions.workspaceRoot, normalizedOptions.branch.default, changelogRelativePath);
|
|
449
|
+
logger.verbose("Existing content found: ", Boolean(existingContent));
|
|
450
|
+
const newEntry = await generateChangelogEntry({
|
|
451
|
+
packageName: workspacePackage.name,
|
|
452
|
+
version,
|
|
453
|
+
previousVersion,
|
|
454
|
+
date,
|
|
455
|
+
commits,
|
|
456
|
+
owner: normalizedOptions.owner,
|
|
457
|
+
repo: normalizedOptions.repo,
|
|
458
|
+
groups: normalizedOptions.groups,
|
|
459
|
+
template: normalizedOptions.changelog?.template,
|
|
460
|
+
githubClient
|
|
461
|
+
});
|
|
462
|
+
let updatedContent;
|
|
463
|
+
if (!existingContent) {
|
|
464
|
+
updatedContent = `# ${workspacePackage.name}\n\n${newEntry}\n`;
|
|
465
|
+
await writeFile(changelogPath, updatedContent, "utf-8");
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const parsed = parseChangelog(existingContent);
|
|
469
|
+
const lines = existingContent.split("\n");
|
|
470
|
+
const existingVersionIndex = parsed.versions.findIndex((v) => v.version === version);
|
|
471
|
+
if (existingVersionIndex !== -1) {
|
|
472
|
+
const existingVersion = parsed.versions[existingVersionIndex];
|
|
473
|
+
const before = lines.slice(0, existingVersion.lineStart);
|
|
474
|
+
const after = lines.slice(existingVersion.lineEnd + 1);
|
|
475
|
+
updatedContent = [
|
|
476
|
+
...before,
|
|
477
|
+
newEntry,
|
|
478
|
+
...after
|
|
479
|
+
].join("\n");
|
|
480
|
+
} else {
|
|
481
|
+
const insertAt = parsed.headerLineEnd + 1;
|
|
482
|
+
const before = lines.slice(0, insertAt);
|
|
483
|
+
const after = lines.slice(insertAt);
|
|
484
|
+
if (before.length > 0 && before[before.length - 1] !== "") before.push("");
|
|
485
|
+
updatedContent = [
|
|
486
|
+
...before,
|
|
487
|
+
newEntry,
|
|
488
|
+
"",
|
|
489
|
+
...after
|
|
490
|
+
].join("\n");
|
|
491
|
+
}
|
|
492
|
+
await writeFile(changelogPath, updatedContent, "utf-8");
|
|
493
|
+
}
|
|
494
|
+
async function resolveCommitAuthors(commits, githubClient) {
|
|
495
|
+
const authorsByEmail = /* @__PURE__ */ new Map();
|
|
496
|
+
const commitAuthors = /* @__PURE__ */ new Map();
|
|
497
|
+
for (const commit of commits) {
|
|
498
|
+
const authorsForCommit = [];
|
|
499
|
+
commit.authors.forEach((author, idx) => {
|
|
500
|
+
if (!author.email || !author.name) return;
|
|
501
|
+
if (!authorsByEmail.has(author.email)) authorsByEmail.set(author.email, {
|
|
502
|
+
commits: [],
|
|
503
|
+
name: author.name,
|
|
504
|
+
email: author.email
|
|
505
|
+
});
|
|
506
|
+
const info = authorsByEmail.get(author.email);
|
|
507
|
+
if (idx === 0) info.commits.push(commit.shortHash);
|
|
508
|
+
authorsForCommit.push(info);
|
|
509
|
+
});
|
|
510
|
+
commitAuthors.set(commit.hash, authorsForCommit);
|
|
511
|
+
}
|
|
512
|
+
await Promise.all(Array.from(authorsByEmail.values()).map((info) => githubClient.resolveAuthorInfo(info)));
|
|
513
|
+
return commitAuthors;
|
|
514
|
+
}
|
|
515
|
+
function formatCommitLine({ commit, owner, repo, authors }) {
|
|
516
|
+
const commitUrl = `https://github.com/${owner}/${repo}/commit/${commit.hash}`;
|
|
517
|
+
let line = `${commit.description}`;
|
|
518
|
+
const references = commit.references ?? [];
|
|
519
|
+
if (references.length > 0) logger.verbose("Located references in commit", references.length);
|
|
520
|
+
for (const ref of references) {
|
|
521
|
+
if (!ref.value) continue;
|
|
522
|
+
const number = Number.parseInt(ref.value.replace(/^#/, ""), 10);
|
|
523
|
+
if (Number.isNaN(number)) continue;
|
|
524
|
+
if (ref.type === "issue") {
|
|
525
|
+
line += ` ([Issue ${ref.value}](https://github.com/${owner}/${repo}/issues/${number}))`;
|
|
526
|
+
continue;
|
|
527
|
+
}
|
|
528
|
+
line += ` ([PR ${ref.value}](https://github.com/${owner}/${repo}/pull/${number}))`;
|
|
529
|
+
}
|
|
530
|
+
line += ` ([${commit.shortHash}](${commitUrl}))`;
|
|
531
|
+
if (authors.length > 0) {
|
|
532
|
+
const authorList = authors.map((author) => {
|
|
533
|
+
if (author.login) return `[@${author.login}](https://github.com/${author.login})`;
|
|
534
|
+
return author.name;
|
|
535
|
+
}).join(", ");
|
|
536
|
+
line += ` (by ${authorList})`;
|
|
537
|
+
}
|
|
538
|
+
return line;
|
|
539
|
+
}
|
|
540
|
+
function parseChangelog(content) {
|
|
541
|
+
const lines = content.split("\n");
|
|
542
|
+
let packageName = null;
|
|
543
|
+
let headerLineEnd = -1;
|
|
544
|
+
const versions = [];
|
|
545
|
+
for (let i = 0; i < lines.length; i++) {
|
|
546
|
+
const line = lines[i].trim();
|
|
547
|
+
if (line.startsWith("# ")) {
|
|
548
|
+
packageName = line.slice(2).trim();
|
|
549
|
+
headerLineEnd = i;
|
|
550
|
+
break;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
for (let i = headerLineEnd + 1; i < lines.length; i++) {
|
|
554
|
+
const line = lines[i].trim();
|
|
555
|
+
if (line.startsWith("## ")) {
|
|
556
|
+
const versionMatch = line.match(/##\s+(?:<small>)?\[?([^\](\s<]+)/);
|
|
557
|
+
if (versionMatch) {
|
|
558
|
+
const version = versionMatch[1];
|
|
559
|
+
const lineStart = i;
|
|
560
|
+
let lineEnd = lines.length - 1;
|
|
561
|
+
for (let j = i + 1; j < lines.length; j++) if (lines[j].trim().startsWith("## ")) {
|
|
562
|
+
lineEnd = j - 1;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
const versionContent = lines.slice(lineStart, lineEnd + 1).join("\n");
|
|
566
|
+
versions.push({
|
|
567
|
+
version,
|
|
568
|
+
lineStart,
|
|
569
|
+
lineEnd,
|
|
570
|
+
content: versionContent
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
packageName,
|
|
577
|
+
versions,
|
|
578
|
+
headerLineEnd
|
|
579
|
+
};
|
|
580
|
+
}
|
|
331
581
|
|
|
332
582
|
//#endregion
|
|
333
583
|
//#region src/core/github.ts
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
584
|
+
var GitHubClient = class {
|
|
585
|
+
owner;
|
|
586
|
+
repo;
|
|
587
|
+
githubToken;
|
|
588
|
+
apiBase = "https://api.github.com";
|
|
589
|
+
constructor({ owner, repo, githubToken }) {
|
|
590
|
+
this.owner = owner;
|
|
591
|
+
this.repo = repo;
|
|
592
|
+
this.githubToken = githubToken;
|
|
593
|
+
}
|
|
594
|
+
async request(path, init = {}) {
|
|
595
|
+
const url = path.startsWith("http") ? path : `${this.apiBase}${path}`;
|
|
596
|
+
const res = await fetch(url, {
|
|
597
|
+
...init,
|
|
598
|
+
headers: {
|
|
599
|
+
...init.headers,
|
|
600
|
+
Accept: "application/vnd.github.v3+json",
|
|
601
|
+
Authorization: `token ${this.githubToken}`
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
if (!res.ok) {
|
|
605
|
+
const errorText = await res.text();
|
|
606
|
+
throw new Error(`GitHub API request failed with status ${res.status}: ${errorText || "No response body"}`);
|
|
607
|
+
}
|
|
608
|
+
if (res.status === 204) return;
|
|
609
|
+
return res.json();
|
|
610
|
+
}
|
|
611
|
+
async getExistingPullRequest(branch) {
|
|
612
|
+
const head = branch.includes(":") ? branch : `${this.owner}:${branch}`;
|
|
613
|
+
const endpoint = `/repos/${this.owner}/${this.repo}/pulls?state=open&head=${encodeURIComponent(head)}`;
|
|
614
|
+
logger.verbose(`Requesting pull request for branch: ${branch} (url: ${this.apiBase}${endpoint})`);
|
|
615
|
+
const pulls = await this.request(endpoint);
|
|
616
|
+
if (!Array.isArray(pulls) || pulls.length === 0) return null;
|
|
344
617
|
const firstPullRequest = pulls[0];
|
|
345
618
|
if (typeof firstPullRequest !== "object" || firstPullRequest === null || !("number" in firstPullRequest) || typeof firstPullRequest.number !== "number" || !("title" in firstPullRequest) || typeof firstPullRequest.title !== "string" || !("body" in firstPullRequest) || typeof firstPullRequest.body !== "string" || !("draft" in firstPullRequest) || typeof firstPullRequest.draft !== "boolean" || !("html_url" in firstPullRequest) || typeof firstPullRequest.html_url !== "string") throw new TypeError("Pull request data validation failed");
|
|
346
619
|
const pullRequest = {
|
|
@@ -348,20 +621,15 @@ async function getExistingPullRequest({ owner, repo, branch, githubToken }) {
|
|
|
348
621
|
title: firstPullRequest.title,
|
|
349
622
|
body: firstPullRequest.body,
|
|
350
623
|
draft: firstPullRequest.draft,
|
|
351
|
-
html_url: firstPullRequest.html_url
|
|
624
|
+
html_url: firstPullRequest.html_url,
|
|
625
|
+
head: "head" in firstPullRequest && typeof firstPullRequest.head === "object" && firstPullRequest.head !== null && "sha" in firstPullRequest.head && typeof firstPullRequest.head.sha === "string" ? { sha: firstPullRequest.head.sha } : void 0
|
|
352
626
|
};
|
|
353
627
|
logger.info(`Found existing pull request: ${farver.yellow(`#${pullRequest.number}`)}`);
|
|
354
628
|
return pullRequest;
|
|
355
|
-
} catch (err) {
|
|
356
|
-
logger.error("Error fetching pull request:", err);
|
|
357
|
-
return null;
|
|
358
629
|
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
const isUpdate = pullNumber != null;
|
|
363
|
-
const url = isUpdate ? `https://api.github.com/repos/${owner}/${repo}/pulls/${pullNumber}` : `https://api.github.com/repos/${owner}/${repo}/pulls`;
|
|
364
|
-
const method = isUpdate ? "PATCH" : "POST";
|
|
630
|
+
async upsertPullRequest({ title, body, head, base, pullNumber }) {
|
|
631
|
+
const isUpdate = typeof pullNumber === "number";
|
|
632
|
+
const endpoint = isUpdate ? `/repos/${this.owner}/${this.repo}/pulls/${pullNumber}` : `/repos/${this.owner}/${this.repo}/pulls`;
|
|
365
633
|
const requestBody = isUpdate ? {
|
|
366
634
|
title,
|
|
367
635
|
body
|
|
@@ -372,17 +640,11 @@ async function upsertPullRequest({ owner, repo, title, body, head, base, pullNum
|
|
|
372
640
|
base,
|
|
373
641
|
draft: true
|
|
374
642
|
};
|
|
375
|
-
logger.verbose(`${isUpdate ? "Updating" : "Creating"} pull request (url: ${
|
|
376
|
-
const
|
|
377
|
-
method,
|
|
378
|
-
headers: {
|
|
379
|
-
Accept: "application/vnd.github.v3+json",
|
|
380
|
-
Authorization: `token ${githubToken}`
|
|
381
|
-
},
|
|
643
|
+
logger.verbose(`${isUpdate ? "Updating" : "Creating"} pull request (url: ${this.apiBase}${endpoint})`);
|
|
644
|
+
const pr = await this.request(endpoint, {
|
|
645
|
+
method: isUpdate ? "PATCH" : "POST",
|
|
382
646
|
body: JSON.stringify(requestBody)
|
|
383
647
|
});
|
|
384
|
-
if (!res.ok) throw new Error(`GitHub API request failed with status ${res.status}`);
|
|
385
|
-
const pr = await res.json();
|
|
386
648
|
if (typeof pr !== "object" || pr === null || !("number" in pr) || typeof pr.number !== "number" || !("title" in pr) || typeof pr.title !== "string" || !("body" in pr) || typeof pr.body !== "string" || !("draft" in pr) || typeof pr.draft !== "boolean" || !("html_url" in pr) || typeof pr.html_url !== "string") throw new TypeError("Pull request data validation failed");
|
|
387
649
|
const action = isUpdate ? "Updated" : "Created";
|
|
388
650
|
logger.info(`${action} pull request: ${farver.yellow(`#${pr.number}`)}`);
|
|
@@ -393,12 +655,45 @@ async function upsertPullRequest({ owner, repo, title, body, head, base, pullNum
|
|
|
393
655
|
draft: pr.draft,
|
|
394
656
|
html_url: pr.html_url
|
|
395
657
|
};
|
|
396
|
-
} catch (err) {
|
|
397
|
-
logger.error(`Error upserting pull request:`, err);
|
|
398
|
-
throw err;
|
|
399
658
|
}
|
|
659
|
+
async setCommitStatus({ sha, state, targetUrl, description, context }) {
|
|
660
|
+
const endpoint = `/repos/${this.owner}/${this.repo}/statuses/${sha}`;
|
|
661
|
+
logger.verbose(`Setting commit status on ${sha} to ${state} (url: ${this.apiBase}${endpoint})`);
|
|
662
|
+
await this.request(endpoint, {
|
|
663
|
+
method: "POST",
|
|
664
|
+
body: JSON.stringify({
|
|
665
|
+
state,
|
|
666
|
+
target_url: targetUrl,
|
|
667
|
+
description: description || "",
|
|
668
|
+
context
|
|
669
|
+
})
|
|
670
|
+
});
|
|
671
|
+
logger.info(`Commit status set to ${farver.cyan(state)} for ${farver.gray(sha.substring(0, 7))}`);
|
|
672
|
+
}
|
|
673
|
+
async resolveAuthorInfo(info) {
|
|
674
|
+
if (info.login) return info;
|
|
675
|
+
try {
|
|
676
|
+
const q = encodeURIComponent(`${info.email} type:user in:email`);
|
|
677
|
+
const data = await this.request(`/search/users?q=${q}`);
|
|
678
|
+
if (!data.items || data.items.length === 0) return info;
|
|
679
|
+
info.login = data.items[0].login;
|
|
680
|
+
} catch (err) {
|
|
681
|
+
logger.warn(`Failed to resolve author info for email ${info.email}: ${err.message}`);
|
|
682
|
+
}
|
|
683
|
+
if (info.login) return info;
|
|
684
|
+
if (info.commits.length > 0) try {
|
|
685
|
+
const data = await this.request(`/repos/${this.owner}/${this.repo}/commits/${info.commits[0]}`);
|
|
686
|
+
if (data.author && data.author.login) info.login = data.author.login;
|
|
687
|
+
} catch (err) {
|
|
688
|
+
logger.warn(`Failed to resolve author info from commits for email ${info.email}: ${err.message}`);
|
|
689
|
+
}
|
|
690
|
+
return info;
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
function createGitHubClient(options) {
|
|
694
|
+
return new GitHubClient(options);
|
|
400
695
|
}
|
|
401
|
-
const
|
|
696
|
+
const DEFAULT_PR_BODY_TEMPLATE = dedent`
|
|
402
697
|
This PR was automatically generated by the release script.
|
|
403
698
|
|
|
404
699
|
The following packages have been prepared for release:
|
|
@@ -421,7 +716,7 @@ function dedentString(str) {
|
|
|
421
716
|
}
|
|
422
717
|
function generatePullRequestBody(updates, body) {
|
|
423
718
|
const eta = new Eta();
|
|
424
|
-
const bodyTemplate = body ? dedentString(body) :
|
|
719
|
+
const bodyTemplate = body ? dedentString(body) : DEFAULT_PR_BODY_TEMPLATE;
|
|
425
720
|
return eta.renderString(bodyTemplate, { packages: updates.map((u) => ({
|
|
426
721
|
name: u.package.name,
|
|
427
722
|
currentVersion: u.currentVersion,
|
|
@@ -433,24 +728,6 @@ function generatePullRequestBody(updates, body) {
|
|
|
433
728
|
|
|
434
729
|
//#endregion
|
|
435
730
|
//#region src/versioning/commits.ts
|
|
436
|
-
async function getMostRecentPackageTag(workspaceRoot, packageName) {
|
|
437
|
-
try {
|
|
438
|
-
const { stdout } = await run("git", [
|
|
439
|
-
"tag",
|
|
440
|
-
"--list",
|
|
441
|
-
`${packageName}@*`
|
|
442
|
-
], { nodeOptions: {
|
|
443
|
-
cwd: workspaceRoot,
|
|
444
|
-
stdio: "pipe"
|
|
445
|
-
} });
|
|
446
|
-
const tags = stdout.split("\n").map((tag) => tag.trim()).filter(Boolean);
|
|
447
|
-
if (tags.length === 0) return;
|
|
448
|
-
return tags.reverse()[0];
|
|
449
|
-
} catch (err) {
|
|
450
|
-
logger.warn(`Failed to get tags for package ${packageName}: ${err.message}`);
|
|
451
|
-
return;
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
731
|
function determineHighestBump(commits) {
|
|
455
732
|
if (commits.length === 0) return "none";
|
|
456
733
|
let highestBump = "none";
|
|
@@ -490,33 +767,6 @@ async function getWorkspacePackageGroupedCommits(workspaceRoot, packages) {
|
|
|
490
767
|
for (const { pkgName, commits } of results) changedPackages.set(pkgName, commits);
|
|
491
768
|
return changedPackages;
|
|
492
769
|
}
|
|
493
|
-
async function getCommitFileList(workspaceRoot, from, to) {
|
|
494
|
-
const commits = /* @__PURE__ */ new Map();
|
|
495
|
-
try {
|
|
496
|
-
const { stdout } = await run("git", [
|
|
497
|
-
"log",
|
|
498
|
-
"--name-only",
|
|
499
|
-
"--format=%H",
|
|
500
|
-
`${from}^..${to}`
|
|
501
|
-
], { nodeOptions: {
|
|
502
|
-
cwd: workspaceRoot,
|
|
503
|
-
stdio: "pipe"
|
|
504
|
-
} });
|
|
505
|
-
const lines = stdout.trim().split("\n").filter((line) => line.trim() !== "");
|
|
506
|
-
let currentSha = null;
|
|
507
|
-
const HASH_REGEX = /^[0-9a-f]{40}$/i;
|
|
508
|
-
for (const line of lines) {
|
|
509
|
-
const trimmedLine = line.trim();
|
|
510
|
-
if (HASH_REGEX.test(trimmedLine)) {
|
|
511
|
-
currentSha = trimmedLine;
|
|
512
|
-
commits.set(currentSha, []);
|
|
513
|
-
} else if (currentSha !== null) commits.get(currentSha)?.push(trimmedLine);
|
|
514
|
-
}
|
|
515
|
-
return commits;
|
|
516
|
-
} catch {
|
|
517
|
-
return null;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
770
|
/**
|
|
521
771
|
* Check if a file path touches any package folder.
|
|
522
772
|
* @param file - The file path to check
|
|
@@ -599,7 +849,7 @@ async function getGlobalCommitsPerPackage(workspaceRoot, packageCommits, allPack
|
|
|
599
849
|
return result;
|
|
600
850
|
}
|
|
601
851
|
logger.verbose("Fetching files for commits range", `${farver.cyan(commitRange.oldest)}..${farver.cyan(commitRange.newest)}`);
|
|
602
|
-
const commitFilesMap = await
|
|
852
|
+
const commitFilesMap = await getGroupedFilesByCommitSha(workspaceRoot, commitRange.oldest, commitRange.newest);
|
|
603
853
|
if (!commitFilesMap) {
|
|
604
854
|
logger.warn("Failed to get commit file list, returning empty global commits");
|
|
605
855
|
return result;
|
|
@@ -814,7 +1064,7 @@ function formatCommitsForDisplay(commits) {
|
|
|
814
1064
|
const commitsToShow = commits.slice(0, maxCommitsToShow);
|
|
815
1065
|
const hasMore = commits.length > maxCommitsToShow;
|
|
816
1066
|
const typeLength = commits.map(({ type }) => type.length).reduce((a, b) => Math.max(a, b), 0);
|
|
817
|
-
const scopeLength = commits.map(({ scope }) => scope
|
|
1067
|
+
const scopeLength = commits.map(({ scope }) => scope?.length).reduce((a, b) => Math.max(a || 0, b || 0), 0) || 0;
|
|
818
1068
|
const formattedCommits = commitsToShow.map((commit) => {
|
|
819
1069
|
let color = messageColorMap[commit.type] || ((c) => c);
|
|
820
1070
|
if (commit.isBreaking) color = (s) => farver.inverse.red(s);
|
|
@@ -1108,6 +1358,87 @@ function shouldIncludePackage(pkg, options) {
|
|
|
1108
1358
|
return true;
|
|
1109
1359
|
}
|
|
1110
1360
|
|
|
1361
|
+
//#endregion
|
|
1362
|
+
//#region src/shared/options.ts
|
|
1363
|
+
const DEFAULT_COMMIT_GROUPS = [
|
|
1364
|
+
{
|
|
1365
|
+
name: "features",
|
|
1366
|
+
title: "Features",
|
|
1367
|
+
types: ["feat"]
|
|
1368
|
+
},
|
|
1369
|
+
{
|
|
1370
|
+
name: "fixes",
|
|
1371
|
+
title: "Bug Fixes",
|
|
1372
|
+
types: ["fix", "perf"]
|
|
1373
|
+
},
|
|
1374
|
+
{
|
|
1375
|
+
name: "refactor",
|
|
1376
|
+
title: "Refactoring",
|
|
1377
|
+
types: ["refactor"]
|
|
1378
|
+
},
|
|
1379
|
+
{
|
|
1380
|
+
name: "docs",
|
|
1381
|
+
title: "Documentation",
|
|
1382
|
+
types: ["docs"]
|
|
1383
|
+
}
|
|
1384
|
+
];
|
|
1385
|
+
function normalizeSharedOptions(options) {
|
|
1386
|
+
const { workspaceRoot = process.cwd(), githubToken = "", repo: fullRepo, packages = true, prompts: prompts$1 = {
|
|
1387
|
+
packages: true,
|
|
1388
|
+
versions: true
|
|
1389
|
+
}, groups = DEFAULT_COMMIT_GROUPS } = options;
|
|
1390
|
+
if (!githubToken.trim()) exitWithError("GitHub token is required", "Set GITHUB_TOKEN environment variable or pass it in options");
|
|
1391
|
+
if (!fullRepo || !fullRepo.trim() || !fullRepo.includes("/")) exitWithError("Repository (repo) is required", "Specify the repository in 'owner/repo' format (e.g., 'octocat/hello-world')");
|
|
1392
|
+
const [owner, repo] = fullRepo.split("/");
|
|
1393
|
+
if (!owner || !repo) exitWithError(`Invalid repo format: "${fullRepo}"`, "Expected format: \"owner/repo\" (e.g., \"octocat/hello-world\")");
|
|
1394
|
+
return {
|
|
1395
|
+
packages: typeof packages === "object" && !Array.isArray(packages) ? {
|
|
1396
|
+
exclude: packages.exclude ?? [],
|
|
1397
|
+
include: packages.include ?? [],
|
|
1398
|
+
excludePrivate: packages.excludePrivate ?? false
|
|
1399
|
+
} : packages,
|
|
1400
|
+
prompts: {
|
|
1401
|
+
packages: prompts$1?.packages ?? true,
|
|
1402
|
+
versions: prompts$1?.versions ?? true
|
|
1403
|
+
},
|
|
1404
|
+
workspaceRoot,
|
|
1405
|
+
githubToken,
|
|
1406
|
+
owner,
|
|
1407
|
+
repo,
|
|
1408
|
+
groups
|
|
1409
|
+
};
|
|
1410
|
+
}
|
|
1411
|
+
async function normalizeReleaseOptions(options) {
|
|
1412
|
+
const normalized = normalizeSharedOptions(options);
|
|
1413
|
+
let defaultBranch = options.branch?.default?.trim();
|
|
1414
|
+
const releaseBranch = options.branch?.release?.trim() ?? "release/next";
|
|
1415
|
+
if (defaultBranch == null || defaultBranch === "") {
|
|
1416
|
+
defaultBranch = await getDefaultBranch(normalized.workspaceRoot);
|
|
1417
|
+
if (!defaultBranch) exitWithError("Could not determine default branch", "Please specify the default branch in options");
|
|
1418
|
+
}
|
|
1419
|
+
if (defaultBranch === releaseBranch) exitWithError(`Default branch and release branch cannot be the same: "${defaultBranch}"`, "Specify different branches for default and release");
|
|
1420
|
+
const availableBranches = await getAvailableBranches(normalized.workspaceRoot);
|
|
1421
|
+
if (!availableBranches.includes(defaultBranch)) exitWithError(`Default branch "${defaultBranch}" does not exist in the repository`, `Available branches: ${availableBranches.join(", ")}`);
|
|
1422
|
+
logger.verbose(`Using default branch: ${farver.green(defaultBranch)}`);
|
|
1423
|
+
return {
|
|
1424
|
+
...normalized,
|
|
1425
|
+
branch: {
|
|
1426
|
+
release: releaseBranch,
|
|
1427
|
+
default: defaultBranch
|
|
1428
|
+
},
|
|
1429
|
+
safeguards: options.safeguards ?? true,
|
|
1430
|
+
globalCommitMode: options.globalCommitMode ?? "dependencies",
|
|
1431
|
+
pullRequest: {
|
|
1432
|
+
title: options.pullRequest?.title ?? "chore: release new version",
|
|
1433
|
+
body: options.pullRequest?.body ?? DEFAULT_PR_BODY_TEMPLATE
|
|
1434
|
+
},
|
|
1435
|
+
changelog: {
|
|
1436
|
+
enabled: options.changelog?.enabled ?? true,
|
|
1437
|
+
template: options.changelog?.template ?? DEFAULT_CHANGELOG_TEMPLATE
|
|
1438
|
+
}
|
|
1439
|
+
};
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1111
1442
|
//#endregion
|
|
1112
1443
|
//#region src/release.ts
|
|
1113
1444
|
async function release(options) {
|
|
@@ -1138,11 +1469,14 @@ async function release(options) {
|
|
|
1138
1469
|
logger.section("🔄 Version Updates");
|
|
1139
1470
|
logger.item(`Updating ${allUpdates.length} packages (including dependents)`);
|
|
1140
1471
|
for (const update of allUpdates) logger.item(`${update.package.name}: ${update.currentVersion} → ${update.newVersion}`);
|
|
1141
|
-
const
|
|
1142
|
-
workspaceRoot,
|
|
1472
|
+
const githubClient = createGitHubClient({
|
|
1143
1473
|
owner: normalizedOptions.owner,
|
|
1144
1474
|
repo: normalizedOptions.repo,
|
|
1145
|
-
githubToken: normalizedOptions.githubToken
|
|
1475
|
+
githubToken: normalizedOptions.githubToken
|
|
1476
|
+
});
|
|
1477
|
+
const prOps = await orchestrateReleasePullRequest({
|
|
1478
|
+
workspaceRoot,
|
|
1479
|
+
githubClient,
|
|
1146
1480
|
releaseBranch: normalizedOptions.branch.release,
|
|
1147
1481
|
defaultBranch: normalizedOptions.branch.default,
|
|
1148
1482
|
pullRequestTitle: options.pullRequest?.title,
|
|
@@ -1150,6 +1484,33 @@ async function release(options) {
|
|
|
1150
1484
|
});
|
|
1151
1485
|
await prOps.prepareBranch();
|
|
1152
1486
|
await applyUpdates();
|
|
1487
|
+
if (normalizedOptions.changelog.enabled) {
|
|
1488
|
+
logger.step("Updating changelogs");
|
|
1489
|
+
const changelogPromises = allUpdates.map((update) => {
|
|
1490
|
+
const pkgCommits = groupedPackageCommits.get(update.package.name) || [];
|
|
1491
|
+
const globalCommits = globalCommitsPerPackage.get(update.package.name) || [];
|
|
1492
|
+
const allCommits = [...pkgCommits, ...globalCommits];
|
|
1493
|
+
if (allCommits.length === 0) {
|
|
1494
|
+
logger.verbose(`No commits for ${update.package.name}, skipping changelog`);
|
|
1495
|
+
return Promise.resolve();
|
|
1496
|
+
}
|
|
1497
|
+
logger.verbose(`Updating changelog for ${farver.cyan(update.package.name)}`);
|
|
1498
|
+
return updateChangelog({
|
|
1499
|
+
normalizedOptions: {
|
|
1500
|
+
...normalizedOptions,
|
|
1501
|
+
workspaceRoot
|
|
1502
|
+
},
|
|
1503
|
+
githubClient,
|
|
1504
|
+
workspacePackage: update.package,
|
|
1505
|
+
version: update.newVersion,
|
|
1506
|
+
previousVersion: update.currentVersion !== "0.0.0" ? update.currentVersion : void 0,
|
|
1507
|
+
commits: allCommits,
|
|
1508
|
+
date: (/* @__PURE__ */ new Date()).toISOString().split("T")[0]
|
|
1509
|
+
});
|
|
1510
|
+
}).filter((p) => p != null);
|
|
1511
|
+
const updates = await Promise.all(changelogPromises);
|
|
1512
|
+
logger.success(`Updated ${updates.length} changelog(s)`);
|
|
1513
|
+
}
|
|
1153
1514
|
if (!await prOps.syncChanges(true)) if (prOps.doesReleasePRExist && prOps.existingPullRequest) {
|
|
1154
1515
|
logger.item("No updates needed, PR is already up to date");
|
|
1155
1516
|
const { pullRequest: pullRequest$1, created: created$1 } = await prOps.syncPullRequest(allUpdates);
|
|
@@ -1175,39 +1536,10 @@ async function release(options) {
|
|
|
1175
1536
|
created
|
|
1176
1537
|
};
|
|
1177
1538
|
}
|
|
1178
|
-
async function
|
|
1179
|
-
const normalized = normalizeSharedOptions(options);
|
|
1180
|
-
let defaultBranch = options.branch?.default?.trim();
|
|
1181
|
-
const releaseBranch = options.branch?.release?.trim() ?? "release/next";
|
|
1182
|
-
if (defaultBranch == null || defaultBranch === "") {
|
|
1183
|
-
defaultBranch = await getDefaultBranch(normalized.workspaceRoot);
|
|
1184
|
-
if (!defaultBranch) exitWithError("Could not determine default branch", "Please specify the default branch in options");
|
|
1185
|
-
}
|
|
1186
|
-
if (defaultBranch === releaseBranch) exitWithError(`Default branch and release branch cannot be the same: "${defaultBranch}"`, "Specify different branches for default and release");
|
|
1187
|
-
const availableBranches = await getAvailableBranches(normalized.workspaceRoot);
|
|
1188
|
-
if (!availableBranches.includes(defaultBranch)) exitWithError(`Default branch "${defaultBranch}" does not exist in the repository`, `Available branches: ${availableBranches.join(", ")}`);
|
|
1189
|
-
logger.verbose(`Using default branch: ${farver.green(defaultBranch)}`);
|
|
1190
|
-
return {
|
|
1191
|
-
...normalized,
|
|
1192
|
-
branch: {
|
|
1193
|
-
release: releaseBranch,
|
|
1194
|
-
default: defaultBranch
|
|
1195
|
-
},
|
|
1196
|
-
safeguards: options.safeguards ?? true,
|
|
1197
|
-
globalCommitMode: options.globalCommitMode ?? "dependencies",
|
|
1198
|
-
pullRequest: options.pullRequest,
|
|
1199
|
-
changelog: { enabled: options.changelog?.enabled ?? true }
|
|
1200
|
-
};
|
|
1201
|
-
}
|
|
1202
|
-
async function orchestrateReleasePullRequest({ workspaceRoot, owner, repo, githubToken, releaseBranch, defaultBranch, pullRequestTitle, pullRequestBody }) {
|
|
1539
|
+
async function orchestrateReleasePullRequest({ workspaceRoot, githubClient, releaseBranch, defaultBranch, pullRequestTitle, pullRequestBody }) {
|
|
1203
1540
|
const currentBranch = await getCurrentBranch(workspaceRoot);
|
|
1204
1541
|
if (currentBranch !== defaultBranch) exitWithError(`Current branch is '${currentBranch}'. Please switch to the default branch '${defaultBranch}' before proceeding.`, `git checkout ${defaultBranch}`);
|
|
1205
|
-
const existingPullRequest = await getExistingPullRequest(
|
|
1206
|
-
owner,
|
|
1207
|
-
repo,
|
|
1208
|
-
branch: releaseBranch,
|
|
1209
|
-
githubToken
|
|
1210
|
-
});
|
|
1542
|
+
const existingPullRequest = await githubClient.getExistingPullRequest(releaseBranch);
|
|
1211
1543
|
const doesReleasePRExist = !!existingPullRequest;
|
|
1212
1544
|
if (doesReleasePRExist) logger.item("Found existing release pull request");
|
|
1213
1545
|
else logger.item("Will create new pull request");
|
|
@@ -1240,15 +1572,12 @@ async function orchestrateReleasePullRequest({ workspaceRoot, owner, repo, githu
|
|
|
1240
1572
|
async syncPullRequest(updates) {
|
|
1241
1573
|
const prTitle = existingPullRequest?.title || pullRequestTitle || "chore: update package versions";
|
|
1242
1574
|
const prBody = generatePullRequestBody(updates, pullRequestBody);
|
|
1243
|
-
const pullRequest = await upsertPullRequest({
|
|
1244
|
-
owner,
|
|
1245
|
-
repo,
|
|
1575
|
+
const pullRequest = await githubClient.upsertPullRequest({
|
|
1246
1576
|
pullNumber: existingPullRequest?.number,
|
|
1247
1577
|
title: prTitle,
|
|
1248
1578
|
body: prBody,
|
|
1249
1579
|
head: releaseBranch,
|
|
1250
|
-
base: defaultBranch
|
|
1251
|
-
githubToken
|
|
1580
|
+
base: defaultBranch
|
|
1252
1581
|
});
|
|
1253
1582
|
logger.success(`${doesReleasePRExist ? "Updated" : "Created"} pull request: ${pullRequest?.html_url}`);
|
|
1254
1583
|
return {
|
|
@@ -1263,4 +1592,8 @@ async function orchestrateReleasePullRequest({ workspaceRoot, owner, repo, githu
|
|
|
1263
1592
|
}
|
|
1264
1593
|
|
|
1265
1594
|
//#endregion
|
|
1266
|
-
|
|
1595
|
+
//#region src/verify.ts
|
|
1596
|
+
function verify(_options) {}
|
|
1597
|
+
|
|
1598
|
+
//#endregion
|
|
1599
|
+
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.17",
|
|
4
4
|
"description": "@ucdjs release scripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"#versioning/*": "./src/versioning/*.ts",
|
|
14
14
|
"#shared/*": "./src/shared/*.ts",
|
|
15
15
|
"#release": "./src/release.ts",
|
|
16
|
-
"#publish": "./src/publish.ts"
|
|
16
|
+
"#publish": "./src/publish.ts",
|
|
17
|
+
"#verify": "./src/verify.ts"
|
|
17
18
|
},
|
|
18
19
|
"exports": {
|
|
19
20
|
".": "./dist/index.mjs",
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
],
|
|
28
29
|
"dependencies": {
|
|
29
30
|
"@luxass/utils": "2.7.2",
|
|
30
|
-
"commit-parser": "1.
|
|
31
|
+
"commit-parser": "1.3.0",
|
|
31
32
|
"farver": "1.0.0-beta.1",
|
|
32
33
|
"mri": "1.2.0",
|
|
33
34
|
"prompts": "2.4.2",
|
|
@@ -41,7 +42,8 @@
|
|
|
41
42
|
"eta": "4.0.1",
|
|
42
43
|
"tsdown": "0.16.0",
|
|
43
44
|
"typescript": "5.9.3",
|
|
44
|
-
"vitest": "4.0.4"
|
|
45
|
+
"vitest": "4.0.4",
|
|
46
|
+
"vitest-testdirs": "4.3.0"
|
|
45
47
|
},
|
|
46
48
|
"scripts": {
|
|
47
49
|
"build": "tsdown",
|