@ucdjs/release-scripts 0.1.0-beta.60 → 0.1.0-beta.62
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/README.md +100 -1
- package/dist/{eta-DAZlmVBQ.mjs → eta-g9ausaEx.mjs} +1 -3
- package/dist/index.mjs +109 -58
- package/package.json +11 -8
package/README.md
CHANGED
|
@@ -1,3 +1,102 @@
|
|
|
1
1
|
# @ucdjs/release-scripts
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![npm downloads][npm-downloads-src]][npm-downloads-href]
|
|
5
|
+
|
|
6
|
+
Monorepo release automation for pnpm workspaces. Handles version calculation, dependency graph resolution, changelog generation, and GitHub integration.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @ucdjs/release-scripts
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { createReleaseScripts } from "@ucdjs/release-scripts";
|
|
18
|
+
|
|
19
|
+
const release = await createReleaseScripts({
|
|
20
|
+
repo: "owner/repo",
|
|
21
|
+
githubToken: process.env.GITHUB_TOKEN,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Prepare a release (calculate versions, update package.json files, create PR)
|
|
25
|
+
const result = await release.prepare();
|
|
26
|
+
|
|
27
|
+
// Verify a release branch matches expected state
|
|
28
|
+
await release.verify();
|
|
29
|
+
|
|
30
|
+
// Publish packages to npm
|
|
31
|
+
await release.publish();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Configuration
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
const release = await createReleaseScripts({
|
|
38
|
+
repo: "owner/repo",
|
|
39
|
+
githubToken: "...",
|
|
40
|
+
workspaceRoot: process.cwd(),
|
|
41
|
+
dryRun: false,
|
|
42
|
+
safeguards: true,
|
|
43
|
+
globalCommitMode: "dependencies",
|
|
44
|
+
packages: {
|
|
45
|
+
include: ["@scope/pkg-a", "@scope/pkg-b"],
|
|
46
|
+
exclude: ["@scope/internal"],
|
|
47
|
+
excludePrivate: true,
|
|
48
|
+
},
|
|
49
|
+
branch: {
|
|
50
|
+
release: "release/next",
|
|
51
|
+
default: "main",
|
|
52
|
+
},
|
|
53
|
+
npm: {
|
|
54
|
+
provenance: true,
|
|
55
|
+
access: "public",
|
|
56
|
+
},
|
|
57
|
+
changelog: {
|
|
58
|
+
enabled: true,
|
|
59
|
+
emojis: true,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Package Discovery
|
|
65
|
+
|
|
66
|
+
```typescript
|
|
67
|
+
// List all workspace packages
|
|
68
|
+
const packages = await release.packages.list();
|
|
69
|
+
|
|
70
|
+
// Get a specific package
|
|
71
|
+
const pkg = await release.packages.get("@scope/pkg-a");
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Workflows
|
|
75
|
+
|
|
76
|
+
#### `prepare()`
|
|
77
|
+
|
|
78
|
+
Calculates version bumps from conventional commits, updates `package.json` files, generates changelogs, and creates/updates a release pull request.
|
|
79
|
+
|
|
80
|
+
#### `verify()`
|
|
81
|
+
|
|
82
|
+
Validates that a release branch matches expected release artifacts. Compares expected vs actual versions and dependency ranges, then sets a GitHub commit status.
|
|
83
|
+
|
|
84
|
+
#### `publish()`
|
|
85
|
+
|
|
86
|
+
Publishes packages to npm in topological order with provenance support, creates git tags, and pushes them to the remote.
|
|
87
|
+
|
|
88
|
+
## CLI Flags
|
|
89
|
+
|
|
90
|
+
When used in a script, the following flags are supported:
|
|
91
|
+
|
|
92
|
+
- `--dry` / `-d` - Dry-run mode, no changes are made
|
|
93
|
+
- `--verbose` / `-v` - Enable verbose logging
|
|
94
|
+
|
|
95
|
+
## 📄 License
|
|
96
|
+
|
|
97
|
+
Published under [MIT License](./LICENSE).
|
|
98
|
+
|
|
99
|
+
[npm-version-src]: https://img.shields.io/npm/v/@ucdjs/release-scripts?style=flat&colorA=18181B&colorB=4169E1
|
|
100
|
+
[npm-version-href]: https://npmjs.com/package/@ucdjs/release-scripts
|
|
101
|
+
[npm-downloads-src]: https://img.shields.io/npm/dm/@ucdjs/release-scripts?style=flat&colorA=18181B&colorB=4169E1
|
|
102
|
+
[npm-downloads-href]: https://npmjs.com/package/@ucdjs/release-scripts
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import * as fs from "node:fs";
|
|
3
|
-
|
|
4
3
|
//#region node_modules/.pnpm/eta@4.5.1/node_modules/eta/dist/index.mjs
|
|
5
4
|
var EtaError = class extends Error {
|
|
6
5
|
constructor(message) {
|
|
@@ -476,6 +475,5 @@ var Eta = class extends Eta$1 {
|
|
|
476
475
|
readFile = readFile;
|
|
477
476
|
resolvePath = resolvePath;
|
|
478
477
|
};
|
|
479
|
-
|
|
480
478
|
//#endregion
|
|
481
|
-
export { Eta as t };
|
|
479
|
+
export { Eta as t };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as Eta } from "./eta-
|
|
1
|
+
import { t as Eta } from "./eta-g9ausaEx.mjs";
|
|
2
2
|
import process from "node:process";
|
|
3
3
|
import readline from "node:readline";
|
|
4
4
|
import { parseArgs } from "node:util";
|
|
@@ -10,7 +10,6 @@ import { getCommits, groupByType } from "commit-parser";
|
|
|
10
10
|
import { dedent } from "@luxass/utils";
|
|
11
11
|
import semver, { gt } from "semver";
|
|
12
12
|
import prompts from "prompts";
|
|
13
|
-
|
|
14
13
|
//#region src/shared/utils.ts
|
|
15
14
|
const ucdjsReleaseOverridesPath = ".github/ucdjs-release.overrides.json";
|
|
16
15
|
function parseCLIFlags() {
|
|
@@ -114,7 +113,6 @@ async function dryRun(bin, args, opts) {
|
|
|
114
113
|
async function runIfNotDry(bin, args, opts) {
|
|
115
114
|
return getIsDryRun() ? dryRun(bin, args, opts) : run(bin, args, opts);
|
|
116
115
|
}
|
|
117
|
-
|
|
118
116
|
//#endregion
|
|
119
117
|
//#region src/shared/errors.ts
|
|
120
118
|
function isRecord(value) {
|
|
@@ -219,16 +217,16 @@ function printReleaseError(error) {
|
|
|
219
217
|
function exitWithError(message, hint, cause) {
|
|
220
218
|
throw new ReleaseError(message, hint, cause);
|
|
221
219
|
}
|
|
222
|
-
|
|
223
220
|
//#endregion
|
|
224
221
|
//#region src/operations/changelog-format.ts
|
|
222
|
+
const HASH_PREFIX_RE = /^#/;
|
|
225
223
|
function formatCommitLine({ commit, owner, repo, authors }) {
|
|
226
224
|
const commitUrl = `https://github.com/${owner}/${repo}/commit/${commit.hash}`;
|
|
227
225
|
let line = `${commit.description}`;
|
|
228
226
|
const references = commit.references ?? [];
|
|
229
227
|
for (const ref of references) {
|
|
230
228
|
if (!ref.value) continue;
|
|
231
|
-
const number = Number.parseInt(ref.value.replace(
|
|
229
|
+
const number = Number.parseInt(ref.value.replace(HASH_PREFIX_RE, ""), 10);
|
|
232
230
|
if (Number.isNaN(number)) continue;
|
|
233
231
|
if (ref.type === "issue") {
|
|
234
232
|
line += ` ([Issue ${ref.value}](https://github.com/${owner}/${repo}/issues/${number}))`;
|
|
@@ -263,7 +261,6 @@ function buildTemplateGroups(options) {
|
|
|
263
261
|
};
|
|
264
262
|
});
|
|
265
263
|
}
|
|
266
|
-
|
|
267
264
|
//#endregion
|
|
268
265
|
//#region src/core/github.ts
|
|
269
266
|
function toGitHubError(operation, error) {
|
|
@@ -459,9 +456,10 @@ var GitHubClient = class {
|
|
|
459
456
|
function createGitHubClient(options) {
|
|
460
457
|
return new GitHubClient(options);
|
|
461
458
|
}
|
|
459
|
+
const NON_WHITESPACE_RE = /\S/;
|
|
462
460
|
function dedentString(str) {
|
|
463
461
|
const lines = str.split("\n");
|
|
464
|
-
const minIndent = lines.filter((line) => line.trim().length > 0).reduce((min, line) => Math.min(min, line.search(
|
|
462
|
+
const minIndent = lines.filter((line) => line.trim().length > 0).reduce((min, line) => Math.min(min, line.search(NON_WHITESPACE_RE)), Infinity);
|
|
465
463
|
return lines.map((line) => minIndent === Infinity ? line : line.slice(minIndent)).join("\n").trim();
|
|
466
464
|
}
|
|
467
465
|
function generatePullRequestBody(updates, body) {
|
|
@@ -475,7 +473,6 @@ function generatePullRequestBody(updates, body) {
|
|
|
475
473
|
hasDirectChanges: u.hasDirectChanges
|
|
476
474
|
})) });
|
|
477
475
|
}
|
|
478
|
-
|
|
479
476
|
//#endregion
|
|
480
477
|
//#region src/options.ts
|
|
481
478
|
const DEFAULT_PR_BODY_TEMPLATE = dedent`
|
|
@@ -586,7 +583,6 @@ function normalizeReleaseScriptsOptions(options) {
|
|
|
586
583
|
}
|
|
587
584
|
};
|
|
588
585
|
}
|
|
589
|
-
|
|
590
586
|
//#endregion
|
|
591
587
|
//#region src/types.ts
|
|
592
588
|
function ok(value) {
|
|
@@ -601,9 +597,10 @@ function err(error) {
|
|
|
601
597
|
error
|
|
602
598
|
};
|
|
603
599
|
}
|
|
604
|
-
|
|
605
600
|
//#endregion
|
|
606
601
|
//#region src/core/git.ts
|
|
602
|
+
const CHECKOUT_BRANCH_RE = /Switched to (?:a new )?branch '(.+)'/;
|
|
603
|
+
const COMMIT_HASH_RE = /^[0-9a-f]{40}$/i;
|
|
607
604
|
function toGitError(operation, error) {
|
|
608
605
|
const formatted = formatUnknownError(error);
|
|
609
606
|
return {
|
|
@@ -686,6 +683,30 @@ async function isWorkingDirectoryClean(workspaceRoot) {
|
|
|
686
683
|
* @param {string} workspaceRoot - The root directory of the workspace
|
|
687
684
|
* @returns {Promise<boolean>} Promise resolving to true if branch exists, false otherwise
|
|
688
685
|
*/
|
|
686
|
+
/**
|
|
687
|
+
* Check if a remote branch exists on origin
|
|
688
|
+
* @param {string} branch - The branch name to check
|
|
689
|
+
* @param {string} workspaceRoot - The root directory of the workspace
|
|
690
|
+
* @returns {Promise<Result<boolean, GitError>>} Promise resolving to true if remote branch exists
|
|
691
|
+
*/
|
|
692
|
+
async function doesRemoteBranchExist(branch, workspaceRoot) {
|
|
693
|
+
try {
|
|
694
|
+
await run("git", [
|
|
695
|
+
"ls-remote",
|
|
696
|
+
"--exit-code",
|
|
697
|
+
"--heads",
|
|
698
|
+
"origin",
|
|
699
|
+
branch
|
|
700
|
+
], { nodeOptions: {
|
|
701
|
+
cwd: workspaceRoot,
|
|
702
|
+
stdio: "pipe"
|
|
703
|
+
} });
|
|
704
|
+
return ok(true);
|
|
705
|
+
} catch (error) {
|
|
706
|
+
logger.verbose(`Remote branch "origin/${branch}" does not exist: ${formatUnknownError(error).message}`);
|
|
707
|
+
return ok(false);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
689
710
|
async function doesBranchExist(branch, workspaceRoot) {
|
|
690
711
|
try {
|
|
691
712
|
await run("git", [
|
|
@@ -751,7 +772,7 @@ async function checkoutBranch(branch, workspaceRoot) {
|
|
|
751
772
|
cwd: workspaceRoot,
|
|
752
773
|
stdio: "pipe"
|
|
753
774
|
} })).stderr.trim();
|
|
754
|
-
const match = output.match(
|
|
775
|
+
const match = output.match(CHECKOUT_BRANCH_RE);
|
|
755
776
|
if (match && match[1] === branch) {
|
|
756
777
|
logger.info(`Successfully switched to branch: ${farver.green(branch)}`);
|
|
757
778
|
return ok(true);
|
|
@@ -985,10 +1006,9 @@ async function getGroupedFilesByCommitSha(workspaceRoot, from, to) {
|
|
|
985
1006
|
} });
|
|
986
1007
|
const lines = stdout.trim().split("\n").filter((line) => line.trim() !== "");
|
|
987
1008
|
let currentSha = null;
|
|
988
|
-
const HASH_REGEX = /^[0-9a-f]{40}$/i;
|
|
989
1009
|
for (const line of lines) {
|
|
990
1010
|
const trimmedLine = line.trim();
|
|
991
|
-
if (
|
|
1011
|
+
if (COMMIT_HASH_RE.test(trimmedLine)) {
|
|
992
1012
|
currentSha = trimmedLine;
|
|
993
1013
|
commitsMap.set(currentSha, []);
|
|
994
1014
|
continue;
|
|
@@ -1055,9 +1075,9 @@ async function createAndPushPackageTag(packageName, version, workspaceRoot) {
|
|
|
1055
1075
|
if (!createResult.ok) return createResult;
|
|
1056
1076
|
return pushTag(`${packageName}@${version}`, workspaceRoot);
|
|
1057
1077
|
}
|
|
1058
|
-
|
|
1059
1078
|
//#endregion
|
|
1060
1079
|
//#region src/core/changelog.ts
|
|
1080
|
+
const CHANGELOG_VERSION_RE = /##\s+(?:<small>)?\[?([^\](\s<]+)/;
|
|
1061
1081
|
const excludeAuthors = [
|
|
1062
1082
|
/\[bot\]/i,
|
|
1063
1083
|
/dependabot/i,
|
|
@@ -1125,7 +1145,7 @@ async function updateChangelog(options) {
|
|
|
1125
1145
|
const insertAt = parsed.headerLineEnd + 1;
|
|
1126
1146
|
const before = lines.slice(0, insertAt);
|
|
1127
1147
|
const after = lines.slice(insertAt);
|
|
1128
|
-
if (before.length > 0 && before
|
|
1148
|
+
if (before.length > 0 && before.at(-1) !== "") before.push("");
|
|
1129
1149
|
updatedContent = [
|
|
1130
1150
|
...before,
|
|
1131
1151
|
newEntry,
|
|
@@ -1154,7 +1174,7 @@ async function resolveCommitAuthors(commits, githubClient) {
|
|
|
1154
1174
|
});
|
|
1155
1175
|
commitAuthors.set(commit.hash, authorsForCommit);
|
|
1156
1176
|
}
|
|
1157
|
-
const authors =
|
|
1177
|
+
const authors = [...authorMap.values()];
|
|
1158
1178
|
await Promise.all(authors.map((info) => githubClient.resolveAuthorInfo(info)));
|
|
1159
1179
|
return commitAuthors;
|
|
1160
1180
|
}
|
|
@@ -1174,7 +1194,7 @@ function parseChangelog(content) {
|
|
|
1174
1194
|
for (let i = headerLineEnd + 1; i < lines.length; i++) {
|
|
1175
1195
|
const line = lines[i].trim();
|
|
1176
1196
|
if (line.startsWith("## ")) {
|
|
1177
|
-
const versionMatch = line.match(
|
|
1197
|
+
const versionMatch = line.match(CHANGELOG_VERSION_RE);
|
|
1178
1198
|
if (versionMatch) {
|
|
1179
1199
|
const version = versionMatch[1];
|
|
1180
1200
|
const lineStart = i;
|
|
@@ -1199,7 +1219,6 @@ function parseChangelog(content) {
|
|
|
1199
1219
|
headerLineEnd
|
|
1200
1220
|
};
|
|
1201
1221
|
}
|
|
1202
|
-
|
|
1203
1222
|
//#endregion
|
|
1204
1223
|
//#region src/operations/semver.ts
|
|
1205
1224
|
function isValidSemver(version) {
|
|
@@ -1207,6 +1226,23 @@ function isValidSemver(version) {
|
|
|
1207
1226
|
}
|
|
1208
1227
|
function getNextVersion(currentVersion, bump) {
|
|
1209
1228
|
if (bump === "none") return currentVersion;
|
|
1229
|
+
if (!isValidSemver(currentVersion)) throw new Error(`Cannot bump version for invalid semver: ${currentVersion}`);
|
|
1230
|
+
if (semver.prerelease(currentVersion)) {
|
|
1231
|
+
const identifier = getPrereleaseIdentifier(currentVersion);
|
|
1232
|
+
const next = identifier ? semver.inc(currentVersion, "prerelease", identifier) : semver.inc(currentVersion, "prerelease");
|
|
1233
|
+
if (!next) throw new Error(`Failed to bump prerelease version ${currentVersion}`);
|
|
1234
|
+
return next;
|
|
1235
|
+
}
|
|
1236
|
+
const next = semver.inc(currentVersion, bump);
|
|
1237
|
+
if (!next) throw new Error(`Failed to bump version ${currentVersion} with bump ${bump}`);
|
|
1238
|
+
return next;
|
|
1239
|
+
}
|
|
1240
|
+
/**
|
|
1241
|
+
* Like getNextVersion but always produces a stable version, even when
|
|
1242
|
+
* currentVersion is a prerelease. Use this when the caller explicitly wants
|
|
1243
|
+
* to promote to a stable release (e.g. patch/minor/major prompt choices).
|
|
1244
|
+
*/
|
|
1245
|
+
function getNextStableVersion(currentVersion, bump) {
|
|
1210
1246
|
if (!isValidSemver(currentVersion)) throw new Error(`Cannot bump version for invalid semver: ${currentVersion}`);
|
|
1211
1247
|
const next = semver.inc(currentVersion, bump);
|
|
1212
1248
|
if (!next) throw new Error(`Failed to bump version ${currentVersion} with bump ${bump}`);
|
|
@@ -1235,7 +1271,6 @@ function getNextPrereleaseVersion(currentVersion, mode, identifier) {
|
|
|
1235
1271
|
if (!next) throw new Error(`Failed to compute prerelease version for ${currentVersion}`);
|
|
1236
1272
|
return next;
|
|
1237
1273
|
}
|
|
1238
|
-
|
|
1239
1274
|
//#endregion
|
|
1240
1275
|
//#region src/core/prompts.ts
|
|
1241
1276
|
async function selectPackagePrompt(packages) {
|
|
@@ -1288,15 +1323,15 @@ async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggested
|
|
|
1288
1323
|
}] : [],
|
|
1289
1324
|
{
|
|
1290
1325
|
value: "patch",
|
|
1291
|
-
title: `patch ${farver.bold(
|
|
1326
|
+
title: `patch ${farver.bold(getNextStableVersion(pkg.version, "patch"))}`
|
|
1292
1327
|
},
|
|
1293
1328
|
{
|
|
1294
1329
|
value: "minor",
|
|
1295
|
-
title: `minor ${farver.bold(
|
|
1330
|
+
title: `minor ${farver.bold(getNextStableVersion(pkg.version, "minor"))}`
|
|
1296
1331
|
},
|
|
1297
1332
|
{
|
|
1298
1333
|
value: "major",
|
|
1299
|
-
title: `major ${farver.bold(
|
|
1334
|
+
title: `major ${farver.bold(getNextStableVersion(pkg.version, "major"))}`
|
|
1300
1335
|
},
|
|
1301
1336
|
{
|
|
1302
1337
|
value: "prerelease",
|
|
@@ -1398,7 +1433,8 @@ async function selectVersionPrompt(workspaceRoot, pkg, currentVersion, suggested
|
|
|
1398
1433
|
}
|
|
1399
1434
|
const prereleaseVersion = prereleaseVersionByChoice[answers.version];
|
|
1400
1435
|
if (prereleaseVersion) return prereleaseVersion;
|
|
1401
|
-
|
|
1436
|
+
const stableBump = answers.version;
|
|
1437
|
+
return getNextStableVersion(pkg.version, stableBump);
|
|
1402
1438
|
}
|
|
1403
1439
|
async function confirmOverridePrompt(pkg, overrideVersion) {
|
|
1404
1440
|
const response = await prompts({
|
|
@@ -1417,7 +1453,6 @@ async function confirmOverridePrompt(pkg, overrideVersion) {
|
|
|
1417
1453
|
if (!response.choice) return null;
|
|
1418
1454
|
return response.choice;
|
|
1419
1455
|
}
|
|
1420
|
-
|
|
1421
1456
|
//#endregion
|
|
1422
1457
|
//#region src/core/workspace.ts
|
|
1423
1458
|
function toWorkspaceError(operation, error) {
|
|
@@ -1501,7 +1536,7 @@ async function findWorkspacePackages(workspaceRoot, options) {
|
|
|
1501
1536
|
};
|
|
1502
1537
|
});
|
|
1503
1538
|
const packages = await Promise.all(promises);
|
|
1504
|
-
if (excludedPackages.size > 0) logger.info(`Excluded packages: ${farver.green(
|
|
1539
|
+
if (excludedPackages.size > 0) logger.info(`Excluded packages: ${farver.green([...excludedPackages].join(", "))}`);
|
|
1505
1540
|
return packages.filter((pkg) => pkg !== null);
|
|
1506
1541
|
} catch (err) {
|
|
1507
1542
|
logger.error("Error discovering workspace packages:", err);
|
|
@@ -1517,7 +1552,6 @@ function shouldIncludePackage(pkg, options) {
|
|
|
1517
1552
|
if (options.exclude?.includes(pkg.name)) return false;
|
|
1518
1553
|
return true;
|
|
1519
1554
|
}
|
|
1520
|
-
|
|
1521
1555
|
//#endregion
|
|
1522
1556
|
//#region src/operations/branch.ts
|
|
1523
1557
|
async function prepareReleaseBranch(options) {
|
|
@@ -1538,9 +1572,13 @@ async function prepareReleaseBranch(options) {
|
|
|
1538
1572
|
const checkedOut = await checkoutBranch(releaseBranch, workspaceRoot);
|
|
1539
1573
|
if (!checkedOut.ok) return checkedOut;
|
|
1540
1574
|
if (branchExists.value) {
|
|
1541
|
-
const
|
|
1542
|
-
if (!
|
|
1543
|
-
if (
|
|
1575
|
+
const remoteExists = await doesRemoteBranchExist(releaseBranch, workspaceRoot);
|
|
1576
|
+
if (!remoteExists.ok) return remoteExists;
|
|
1577
|
+
if (remoteExists.value) {
|
|
1578
|
+
const pulled = await pullLatestChanges(releaseBranch, workspaceRoot);
|
|
1579
|
+
if (!pulled.ok) return pulled;
|
|
1580
|
+
if (!pulled.value) logger.warn("Failed to pull latest changes, continuing anyway.");
|
|
1581
|
+
} else logger.info(`Remote branch "origin/${releaseBranch}" does not exist yet, skipping pull.`);
|
|
1544
1582
|
}
|
|
1545
1583
|
const rebased = await rebaseBranch(defaultBranch, workspaceRoot);
|
|
1546
1584
|
if (!rebased.ok) return rebased;
|
|
@@ -1557,7 +1595,6 @@ async function syncReleaseChanges(options) {
|
|
|
1557
1595
|
if (!pushed.ok) return pushed;
|
|
1558
1596
|
return ok(true);
|
|
1559
1597
|
}
|
|
1560
|
-
|
|
1561
1598
|
//#endregion
|
|
1562
1599
|
//#region src/versioning/commits.ts
|
|
1563
1600
|
/**
|
|
@@ -1643,7 +1680,7 @@ function findCommitRange(packageCommits) {
|
|
|
1643
1680
|
for (const commits of packageCommits.values()) {
|
|
1644
1681
|
if (commits.length === 0) continue;
|
|
1645
1682
|
const firstCommit = commits[0].shortHash;
|
|
1646
|
-
const lastCommit = commits
|
|
1683
|
+
const lastCommit = commits.at(-1).shortHash;
|
|
1647
1684
|
if (!newestCommit) newestCommit = firstCommit;
|
|
1648
1685
|
oldestCommit = lastCommit;
|
|
1649
1686
|
}
|
|
@@ -1712,7 +1749,6 @@ async function getGlobalCommitsPerPackage(workspaceRoot, packageCommits, allPack
|
|
|
1712
1749
|
}
|
|
1713
1750
|
return result;
|
|
1714
1751
|
}
|
|
1715
|
-
|
|
1716
1752
|
//#endregion
|
|
1717
1753
|
//#region src/operations/version.ts
|
|
1718
1754
|
function determineHighestBump(commits) {
|
|
@@ -1744,7 +1780,6 @@ function determineBumpType(commit) {
|
|
|
1744
1780
|
if (commit.type === "fix" || commit.type === "perf") return "patch";
|
|
1745
1781
|
return "none";
|
|
1746
1782
|
}
|
|
1747
|
-
|
|
1748
1783
|
//#endregion
|
|
1749
1784
|
//#region src/versioning/package.ts
|
|
1750
1785
|
/**
|
|
@@ -1874,7 +1909,6 @@ function createDependentUpdates(graph, workspacePackages, directUpdates, exclude
|
|
|
1874
1909
|
}
|
|
1875
1910
|
return allUpdates;
|
|
1876
1911
|
}
|
|
1877
|
-
|
|
1878
1912
|
//#endregion
|
|
1879
1913
|
//#region src/versioning/version.ts
|
|
1880
1914
|
const messageColorMap = {
|
|
@@ -2148,7 +2182,6 @@ function getDependencyUpdates(pkg, allUpdates) {
|
|
|
2148
2182
|
if (updates.size === 0) logger.verbose(` - No dependency updates needed`);
|
|
2149
2183
|
return updates;
|
|
2150
2184
|
}
|
|
2151
|
-
|
|
2152
2185
|
//#endregion
|
|
2153
2186
|
//#region src/operations/calculate.ts
|
|
2154
2187
|
async function calculateUpdates(options) {
|
|
@@ -2181,7 +2214,6 @@ function ensureHasPackages(packages) {
|
|
|
2181
2214
|
});
|
|
2182
2215
|
return ok(packages);
|
|
2183
2216
|
}
|
|
2184
|
-
|
|
2185
2217
|
//#endregion
|
|
2186
2218
|
//#region src/operations/pr.ts
|
|
2187
2219
|
async function syncPullRequest(options) {
|
|
@@ -2218,7 +2250,6 @@ async function syncPullRequest(options) {
|
|
|
2218
2250
|
created: !doesExist
|
|
2219
2251
|
});
|
|
2220
2252
|
}
|
|
2221
|
-
|
|
2222
2253
|
//#endregion
|
|
2223
2254
|
//#region src/workflows/prepare.ts
|
|
2224
2255
|
async function prepareWorkflow(options) {
|
|
@@ -2258,6 +2289,23 @@ async function prepareWorkflow(options) {
|
|
|
2258
2289
|
logger.info("No existing version overrides file found. Continuing...");
|
|
2259
2290
|
logger.verbose(`Reading overrides file failed: ${formatUnknownError(error).message}`);
|
|
2260
2291
|
}
|
|
2292
|
+
if (Object.keys(existingOverrides).length > 0) {
|
|
2293
|
+
const packageNames = new Set(workspacePackages.map((p) => p.name));
|
|
2294
|
+
const staleEntries = [];
|
|
2295
|
+
for (const [pkgName, override] of Object.entries(existingOverrides)) {
|
|
2296
|
+
if (!packageNames.has(pkgName)) {
|
|
2297
|
+
staleEntries.push(pkgName);
|
|
2298
|
+
delete existingOverrides[pkgName];
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
const pkg = workspacePackages.find((p) => p.name === pkgName);
|
|
2302
|
+
if (pkg && semver.valid(override.version) && semver.gte(pkg.version, override.version)) {
|
|
2303
|
+
staleEntries.push(pkgName);
|
|
2304
|
+
delete existingOverrides[pkgName];
|
|
2305
|
+
}
|
|
2306
|
+
}
|
|
2307
|
+
if (staleEntries.length > 0) logger.info(`Removed ${staleEntries.length} stale override(s): ${staleEntries.join(", ")}`);
|
|
2308
|
+
}
|
|
2261
2309
|
const updatesResult = await calculateUpdates({
|
|
2262
2310
|
workspacePackages,
|
|
2263
2311
|
workspaceRoot: options.workspaceRoot,
|
|
@@ -2391,7 +2439,6 @@ async function prepareWorkflow(options) {
|
|
|
2391
2439
|
created: prResult.value.created
|
|
2392
2440
|
};
|
|
2393
2441
|
}
|
|
2394
|
-
|
|
2395
2442
|
//#endregion
|
|
2396
2443
|
//#region src/core/npm.ts
|
|
2397
2444
|
function toNPMError(operation, error, code) {
|
|
@@ -2504,7 +2551,7 @@ async function publishPackage(packageName, version, workspaceRoot, options) {
|
|
|
2504
2551
|
} catch (error) {
|
|
2505
2552
|
const code = classifyPublishErrorCode(error);
|
|
2506
2553
|
if (code === "EPUBLISHCONFLICT" && attempt < maxAttempts) {
|
|
2507
|
-
const delay = backoffMs[attempt - 1] ?? backoffMs
|
|
2554
|
+
const delay = backoffMs[attempt - 1] ?? backoffMs.at(-1);
|
|
2508
2555
|
logger.warn(`Publish conflict for ${packageName}@${version} (attempt ${attempt}/${maxAttempts}). Retrying in ${Math.ceil(delay / 1e3)}s...`);
|
|
2509
2556
|
await wait(delay);
|
|
2510
2557
|
continue;
|
|
@@ -2513,7 +2560,6 @@ async function publishPackage(packageName, version, workspaceRoot, options) {
|
|
|
2513
2560
|
}
|
|
2514
2561
|
return err(toNPMError("publishPackage", /* @__PURE__ */ new Error(`Failed to publish ${packageName}@${version} after ${maxAttempts} attempts`), "EPUBLISHCONFLICT"));
|
|
2515
2562
|
}
|
|
2516
|
-
|
|
2517
2563
|
//#endregion
|
|
2518
2564
|
//#region src/workflows/publish.ts
|
|
2519
2565
|
async function getReleaseBodyFromChangelog(workspaceRoot, packageName, packagePath, version) {
|
|
@@ -2609,24 +2655,32 @@ async function publishWorkflow(options) {
|
|
|
2609
2655
|
status.failed.push(packageName);
|
|
2610
2656
|
exitWithError(`Publishing failed for ${packageName}.`, "Check your network connection and NPM registry access", existsResult.error);
|
|
2611
2657
|
}
|
|
2612
|
-
|
|
2613
|
-
|
|
2658
|
+
const npmExists = existsResult.value;
|
|
2659
|
+
let changelogEntryExists = false;
|
|
2660
|
+
const changelogPath = join(pkg.path, "CHANGELOG.md");
|
|
2661
|
+
try {
|
|
2662
|
+
changelogEntryExists = parseChangelog(await readFile(changelogPath, "utf-8")).versions.some((v) => v.version === version);
|
|
2663
|
+
} catch {}
|
|
2664
|
+
if (npmExists && changelogEntryExists) {
|
|
2665
|
+
logger.info(`Version ${farver.cyan(version)} already exists on NPM and in changelog, skipping`);
|
|
2614
2666
|
status.skipped.push(packageName);
|
|
2615
2667
|
continue;
|
|
2616
2668
|
}
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2669
|
+
if (!npmExists) {
|
|
2670
|
+
logger.step(`Publishing ${farver.cyan(`${packageName}@${version}`)} to NPM...`);
|
|
2671
|
+
const publishResult = await publishPackage(packageName, version, options.workspaceRoot, options);
|
|
2672
|
+
if (!publishResult.ok) {
|
|
2673
|
+
logger.error(`Failed to publish: ${publishResult.error.message}`);
|
|
2674
|
+
status.failed.push(packageName);
|
|
2675
|
+
let hint;
|
|
2676
|
+
if (publishResult.error.code === "E403") hint = "Authentication failed. Ensure your NPM token or OIDC configuration is correct";
|
|
2677
|
+
else if (publishResult.error.code === "EPUBLISHCONFLICT") hint = "Version conflict. The version may have been published recently";
|
|
2678
|
+
else if (publishResult.error.code === "EOTP") hint = "2FA/OTP required. Provide the otp option or use OIDC authentication";
|
|
2679
|
+
exitWithError(`Publishing failed for ${packageName}`, hint, publishResult.error);
|
|
2680
|
+
}
|
|
2681
|
+
logger.success(`Published ${farver.cyan(`${packageName}@${version}`)}`);
|
|
2682
|
+
status.published.push(packageName);
|
|
2627
2683
|
}
|
|
2628
|
-
logger.success(`Published ${farver.cyan(`${packageName}@${version}`)}`);
|
|
2629
|
-
status.published.push(packageName);
|
|
2630
2684
|
logger.step(`Creating git tag ${farver.cyan(`${packageName}@${version}`)}...`);
|
|
2631
2685
|
const tagResult = await createAndPushPackageTag(packageName, version, options.workspaceRoot);
|
|
2632
2686
|
const tagName = `${packageName}@${version}`;
|
|
@@ -2678,7 +2732,6 @@ async function publishWorkflow(options) {
|
|
|
2678
2732
|
}
|
|
2679
2733
|
logger.success("All packages published successfully!");
|
|
2680
2734
|
}
|
|
2681
|
-
|
|
2682
2735
|
//#endregion
|
|
2683
2736
|
//#region src/workflows/verify.ts
|
|
2684
2737
|
async function verifyWorkflow(options) {
|
|
@@ -2773,7 +2826,6 @@ async function verifyWorkflow(options) {
|
|
|
2773
2826
|
logger.success("Verification successful. Commit status set to 'success'.");
|
|
2774
2827
|
}
|
|
2775
2828
|
}
|
|
2776
|
-
|
|
2777
2829
|
//#endregion
|
|
2778
2830
|
//#region src/index.ts
|
|
2779
2831
|
function withErrorBoundary(fn) {
|
|
@@ -2831,6 +2883,5 @@ async function createReleaseScripts(options) {
|
|
|
2831
2883
|
}
|
|
2832
2884
|
};
|
|
2833
2885
|
}
|
|
2834
|
-
|
|
2835
2886
|
//#endregion
|
|
2836
|
-
export { createReleaseScripts };
|
|
2887
|
+
export { createReleaseScripts };
|
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.62",
|
|
4
4
|
"description": "@ucdjs release scripts",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,23 +26,26 @@
|
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@luxass/utils": "2.7.3",
|
|
29
|
-
"commit-parser": "1.3.
|
|
29
|
+
"commit-parser": "1.3.1",
|
|
30
30
|
"farver": "1.0.0-beta.1",
|
|
31
31
|
"prompts": "2.4.2",
|
|
32
32
|
"semver": "7.7.4",
|
|
33
|
-
"tinyexec": "1.0.
|
|
33
|
+
"tinyexec": "1.0.4"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@luxass/eslint-config": "7.
|
|
36
|
+
"@luxass/eslint-config": "7.3.0",
|
|
37
37
|
"@types/node": "22.18.12",
|
|
38
38
|
"@types/prompts": "2.4.9",
|
|
39
39
|
"@types/semver": "7.7.1",
|
|
40
|
-
"eslint": "10.0.
|
|
40
|
+
"eslint": "10.0.3",
|
|
41
41
|
"eta": "4.5.1",
|
|
42
|
-
"tsdown": "0.
|
|
42
|
+
"tsdown": "0.21.2",
|
|
43
43
|
"typescript": "5.9.3",
|
|
44
|
-
"vitest": "4.0
|
|
45
|
-
"vitest-testdirs": "4.4.
|
|
44
|
+
"vitest": "4.1.0",
|
|
45
|
+
"vitest-testdirs": "4.4.3"
|
|
46
|
+
},
|
|
47
|
+
"inlinedDependencies": {
|
|
48
|
+
"eta": "4.5.1"
|
|
46
49
|
},
|
|
47
50
|
"scripts": {
|
|
48
51
|
"build": "tsdown",
|