@peiyanlu/release 0.0.3 → 0.0.4
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 +139 -12
- package/dist/cli.mjs +340 -235
- package/dist/index.d.mts +35 -43
- package/package.json +8 -8
- package/dist/index.cjs +0 -26
- package/dist/index.d.cts +0 -166
package/dist/cli.mjs
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import path, { join, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
3
|
import { eol, execAsync, isGitRepo, parseGitHubRepo, readJsonFile, runGit, runNpm } from "@peiyanlu/cli-utils";
|
|
4
|
-
import { dim,
|
|
4
|
+
import { dim, green, red, rgb, underline, yellow } from "ansis";
|
|
5
5
|
import { program } from "commander";
|
|
6
|
+
import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
6
7
|
import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
|
|
7
|
-
import {
|
|
8
|
+
import { publint } from "publint";
|
|
9
|
+
import { formatMessage } from "publint/utils";
|
|
10
|
+
import { clean, coerce, eq, gt, inc, lt, parse, prerelease, valid } from "semver";
|
|
8
11
|
import { castArray, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
|
|
9
|
-
import { createReadStream, existsSync, statSync } from "node:fs";
|
|
10
12
|
import { spawnSync } from "node:child_process";
|
|
11
|
-
import { inspect
|
|
13
|
+
import { inspect } from "node:util";
|
|
12
14
|
import { Octokit } from "@octokit/rest";
|
|
13
15
|
import { contentType } from "mime-types";
|
|
14
16
|
import open from "open";
|
|
15
17
|
import { glob } from "tinyglobby";
|
|
16
|
-
import {
|
|
18
|
+
import { ConventionalChangelog } from "conventional-changelog";
|
|
19
|
+
import createPreset from "conventional-changelog-conventionalcommits";
|
|
20
|
+
import { writeFile } from "node:fs/promises";
|
|
21
|
+
import { finished } from "node:stream/promises";
|
|
17
22
|
|
|
18
|
-
//#region node_modules/.pnpm/tsdown@0.18.
|
|
23
|
+
//#region node_modules/.pnpm/tsdown@0.18.4_publint@0.3.17_typescript@5.9.3/node_modules/tsdown/esm-shims.js
|
|
19
24
|
const getFilename = () => fileURLToPath(import.meta.url);
|
|
20
25
|
const getDirname = () => path.dirname(getFilename());
|
|
21
26
|
const __dirname = /* @__PURE__ */ getDirname();
|
|
@@ -26,10 +31,8 @@ const findConfigFile = (cwd = process.cwd()) => {
|
|
|
26
31
|
for (const ext of [
|
|
27
32
|
"ts",
|
|
28
33
|
"mts",
|
|
29
|
-
"cts",
|
|
30
34
|
"js",
|
|
31
|
-
"mjs"
|
|
32
|
-
"cjs"
|
|
35
|
+
"mjs"
|
|
33
36
|
]) {
|
|
34
37
|
const path$1 = resolve(cwd, `release.config.${ext}`);
|
|
35
38
|
if (existsSync(path$1)) return path$1;
|
|
@@ -41,11 +44,16 @@ const loadConfig = async (file) => {
|
|
|
41
44
|
const temp = mod.default ?? mod;
|
|
42
45
|
return typeof temp === "function" ? temp() : temp;
|
|
43
46
|
};
|
|
44
|
-
const resolveConfig = async (cwd
|
|
47
|
+
const resolveConfig = async (cwd) => {
|
|
45
48
|
const configFile = findConfigFile(cwd);
|
|
46
|
-
if (!configFile) return {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
if (!configFile) return {
|
|
50
|
+
configPath: "",
|
|
51
|
+
config: {}
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
configPath: configFile,
|
|
55
|
+
config: await loadConfig(configFile)
|
|
56
|
+
};
|
|
49
57
|
};
|
|
50
58
|
const isFunction = (v) => typeof v === "function";
|
|
51
59
|
const mergeConfig = (defaults, overrides) => {
|
|
@@ -69,8 +77,8 @@ const mergeConfig = (defaults, overrides) => {
|
|
|
69
77
|
const n = (b) => b ? "(dry run)" : "";
|
|
70
78
|
const t = () => green(`${Math.floor(process.uptime())}s`);
|
|
71
79
|
const MSG = {
|
|
72
|
-
INTRO: (
|
|
73
|
-
OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()} ${n(dryRun)}
|
|
80
|
+
INTRO: (dryRun) => `🚀 Starting release ${n(dryRun)}`,
|
|
81
|
+
OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()} ${n(dryRun)}`,
|
|
74
82
|
LOG: {
|
|
75
83
|
SHOW_VERSION: (version) => `🎉 Released ${green(version)}`,
|
|
76
84
|
SHOW_CHANGELOG: "🎉 Changelog collected",
|
|
@@ -80,11 +88,11 @@ const MSG = {
|
|
|
80
88
|
CHECK: {
|
|
81
89
|
GIT: {
|
|
82
90
|
CHECKING: "Checking Git repository",
|
|
83
|
-
CHECKED: (name, url) => `Git remote resolved: ${dim(name)} → ${underline(dim(url))}`
|
|
91
|
+
CHECKED: (name, url) => `Git remote resolved: ${url ? `${dim(name)} → ${underline(dim(url))}` : "not found"}`
|
|
84
92
|
},
|
|
85
93
|
NPM: {
|
|
86
94
|
CHECKING: "Checking npm registry",
|
|
87
|
-
CHECKED: (registry, msg$1) => `NPM registry: ${underline(dim(registry))}
|
|
95
|
+
CHECKED: (registry, msg$1) => `NPM registry: ${underline(dim(registry))} ${underline(dim(msg$1))}`
|
|
88
96
|
}
|
|
89
97
|
},
|
|
90
98
|
TASK: {
|
|
@@ -110,6 +118,7 @@ const MSG = {
|
|
|
110
118
|
}
|
|
111
119
|
},
|
|
112
120
|
PROMPT: {
|
|
121
|
+
SELECT_PACKAGE: "Select package release:",
|
|
113
122
|
SELECT_VERSION: "Select version bump:",
|
|
114
123
|
INPUT_VERSION: "Enter custom version:",
|
|
115
124
|
NPM_PUBLISH: (pkg$1) => `Publish to npm ${underline(dim`(${pkg$1})`)}?`,
|
|
@@ -130,6 +139,15 @@ const MSG = {
|
|
|
130
139
|
NPM_REGISTRY: (registry) => `[npm] Unable to reach npm registry ${underline(dim(registry))}`,
|
|
131
140
|
NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again.`,
|
|
132
141
|
NPM_USER: (user, name) => `[npm] User ${dim(user)} is not a collaborator of ${dim(name)}.`
|
|
142
|
+
},
|
|
143
|
+
ABORT: {
|
|
144
|
+
CANCEL: "Operation cancelled",
|
|
145
|
+
MONOREPO_CI_NO_PACKAGE: "CI mode requires a target package in monorepo. Please specify it via \"--package <pkg>\"",
|
|
146
|
+
MONOREPO_NO_PACKAGES: "Monorepo detected, but no packages found. Please configure \"config.packages\""
|
|
147
|
+
},
|
|
148
|
+
INFO: {
|
|
149
|
+
TOOL: (name, version) => `${name} ${dim(`v${version}`)}`,
|
|
150
|
+
CONFIG: (path$1) => `config file: ${underline(dim(path$1 || "not found"))}`
|
|
133
151
|
}
|
|
134
152
|
};
|
|
135
153
|
|
|
@@ -164,28 +182,28 @@ const uploadAssets = async (ctx, config) => {
|
|
|
164
182
|
}));
|
|
165
183
|
};
|
|
166
184
|
const createWebRelease = async (ctx, config) => {
|
|
167
|
-
const { pkg: {
|
|
185
|
+
const { pkg: { toPreRelease }, github: { owner, repo, changelog, releaseName }, git: { currentTag } } = ctx;
|
|
168
186
|
const { github: { prerelease: prerelease$1 } } = config;
|
|
169
187
|
const github = getGithubUrl(owner, repo);
|
|
170
188
|
const url = new URL(`${github}/releases/new`);
|
|
171
|
-
url.searchParams.set("tag",
|
|
172
|
-
url.searchParams.set("title", releaseName
|
|
189
|
+
url.searchParams.set("tag", currentTag);
|
|
190
|
+
url.searchParams.set("title", releaseName);
|
|
173
191
|
url.searchParams.set("body", truncateBody(changelog));
|
|
174
192
|
url.searchParams.set("prelease", String(toPreRelease || prerelease$1));
|
|
175
193
|
const isWindows = process.platform === "win32";
|
|
176
194
|
await open(url.toString(), { wait: isWindows });
|
|
177
195
|
};
|
|
178
196
|
const createCiRelease = async (ctx, config) => {
|
|
179
|
-
const { pkg: {
|
|
197
|
+
const { pkg: { toPreRelease }, github: { owner, repo, token, changelog, releaseName }, git: { currentTag } } = ctx;
|
|
180
198
|
const { github: { prerelease: prerelease$1, draft, autoGenerate } } = config;
|
|
181
199
|
const octokit = new Octokit({ auth: token });
|
|
182
200
|
try {
|
|
183
201
|
const { data } = await octokit.repos.createRelease({
|
|
184
202
|
owner,
|
|
185
203
|
repo,
|
|
186
|
-
tag_name:
|
|
204
|
+
tag_name: currentTag,
|
|
187
205
|
target_commitish: void 0,
|
|
188
|
-
name: releaseName
|
|
206
|
+
name: releaseName,
|
|
189
207
|
body: autoGenerate ? "" : truncateBody(changelog),
|
|
190
208
|
draft: draft ?? false,
|
|
191
209
|
prerelease: (toPreRelease || prerelease$1) ?? false,
|
|
@@ -202,7 +220,7 @@ const createCiRelease = async (ctx, config) => {
|
|
|
202
220
|
discussionUrl: discussion_url
|
|
203
221
|
});
|
|
204
222
|
} catch (e) {
|
|
205
|
-
if (e?.status === 422) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(
|
|
223
|
+
if (e?.status === 422) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(currentTag));
|
|
206
224
|
throw e;
|
|
207
225
|
}
|
|
208
226
|
};
|
|
@@ -259,12 +277,17 @@ const getGithubReleaseUrl = (ctx) => {
|
|
|
259
277
|
|
|
260
278
|
//#endregion
|
|
261
279
|
//#region src/git/commit.ts
|
|
262
|
-
const getBranchName = () => {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
const getBranchName = async () => {
|
|
281
|
+
let branch = await runGit(["branch", "--show-current"]);
|
|
282
|
+
if (!branch) {
|
|
283
|
+
branch = await runGit([
|
|
284
|
+
"rev-parse",
|
|
285
|
+
"--abbrev-ref",
|
|
286
|
+
"HEAD"
|
|
287
|
+
]);
|
|
288
|
+
if (branch === "HEAD") return void 0;
|
|
289
|
+
}
|
|
290
|
+
return branch;
|
|
268
291
|
};
|
|
269
292
|
const getRemoteForBranch = (branch) => {
|
|
270
293
|
return runGit([
|
|
@@ -273,6 +296,9 @@ const getRemoteForBranch = (branch) => {
|
|
|
273
296
|
`branch.${branch}.remote`
|
|
274
297
|
]);
|
|
275
298
|
};
|
|
299
|
+
const getAllRemotes = async () => {
|
|
300
|
+
return (await runGit(["remote"]))?.split("\n").filter(Boolean) ?? [];
|
|
301
|
+
};
|
|
276
302
|
const getStatus = () => {
|
|
277
303
|
return runGit([
|
|
278
304
|
"status",
|
|
@@ -293,7 +319,7 @@ const coloredChangeset = (log$1) => {
|
|
|
293
319
|
return log$1.split("\n").filter(Boolean).map((line) => {
|
|
294
320
|
const status = line.slice(0, 2);
|
|
295
321
|
const file = line.slice(3);
|
|
296
|
-
return `${colorStatus(status)} ${
|
|
322
|
+
return `${colorStatus(status)} ${dim(file)}`;
|
|
297
323
|
}).join("\n");
|
|
298
324
|
};
|
|
299
325
|
const getRemote = async () => {
|
|
@@ -334,7 +360,7 @@ const getPreviousTag = async (current) => {
|
|
|
334
360
|
])}^`
|
|
335
361
|
]);
|
|
336
362
|
};
|
|
337
|
-
const resolveChangelogRange = async (isIncrement) => {
|
|
363
|
+
const resolveChangelogRange = async (isIncrement = true) => {
|
|
338
364
|
const latestTag = await getLatestTag();
|
|
339
365
|
const previousTag = await getPreviousTag(latestTag);
|
|
340
366
|
if (!latestTag) return {
|
|
@@ -350,10 +376,10 @@ const resolveChangelogRange = async (isIncrement) => {
|
|
|
350
376
|
to: "HEAD"
|
|
351
377
|
};
|
|
352
378
|
};
|
|
353
|
-
const getLog = async (
|
|
354
|
-
const cmd = ["log", `--pretty=format
|
|
355
|
-
const { from, to } = await resolveChangelogRange(isIncrement);
|
|
379
|
+
const getLog = async (from = "", to = "HEAD", scope) => {
|
|
380
|
+
const cmd = ["log", `--pretty=format:* %s (%h)`];
|
|
356
381
|
if (from) cmd.push(`${from}...${to}`);
|
|
382
|
+
if (scope) cmd.push(...["--", scope]);
|
|
357
383
|
return runGit(cmd, { trim: false });
|
|
358
384
|
};
|
|
359
385
|
const isWorkingDirClean = async () => {
|
|
@@ -372,11 +398,10 @@ const getUpstreamArgs = async (remoteName) => {
|
|
|
372
398
|
const branch = await getBranchName();
|
|
373
399
|
if (!hasUpstream) return [
|
|
374
400
|
"--set-upstream",
|
|
375
|
-
remoteName
|
|
401
|
+
remoteName,
|
|
376
402
|
branch
|
|
377
403
|
];
|
|
378
|
-
|
|
379
|
-
return [];
|
|
404
|
+
return [remoteName, branch];
|
|
380
405
|
};
|
|
381
406
|
const normalizeTag = (tag) => clean(tag) ?? tag;
|
|
382
407
|
const gitCheck = async (ctx, config) => {
|
|
@@ -431,16 +456,29 @@ const commitAndTag = async (ctx, config) => {
|
|
|
431
456
|
]);
|
|
432
457
|
if (!dryRun) Object.assign(ctx.git, { isTagged: true });
|
|
433
458
|
}
|
|
434
|
-
if (push)
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
459
|
+
if (push) {
|
|
460
|
+
const remotes = await getAllRemotes();
|
|
461
|
+
for (const remoteName$1 of remotes) {
|
|
462
|
+
const upstreamArgs = await getUpstreamArgs(remoteName$1);
|
|
463
|
+
await runGit([
|
|
464
|
+
"push",
|
|
465
|
+
remoteName$1,
|
|
466
|
+
`refs/tags/${currentTag}`,
|
|
467
|
+
...args
|
|
468
|
+
]);
|
|
469
|
+
await runGit([
|
|
470
|
+
"push",
|
|
471
|
+
...upstreamArgs,
|
|
472
|
+
...pushArgs,
|
|
473
|
+
...args
|
|
474
|
+
]);
|
|
475
|
+
}
|
|
476
|
+
if (!dryRun) Object.assign(ctx.git, { isPushed: true });
|
|
477
|
+
}
|
|
441
478
|
};
|
|
442
479
|
const gitRollback$1 = (ctx) => {
|
|
443
|
-
const { git: { currentTag, isCommitted, isTagged } } = ctx;
|
|
480
|
+
const { git: { currentTag, isCommitted, isTagged, isPushed } } = ctx;
|
|
481
|
+
if (isPushed) return;
|
|
444
482
|
spawnSync("git", ["restore", "."]);
|
|
445
483
|
if (isTagged) spawnSync("git", [
|
|
446
484
|
"tag",
|
|
@@ -457,13 +495,14 @@ const gitRollback$1 = (ctx) => {
|
|
|
457
495
|
//#endregion
|
|
458
496
|
//#region src/utils.ts
|
|
459
497
|
const info = (msg$1) => {
|
|
460
|
-
console.log(`${rgb(33, 91, 184)(`i`)} ${
|
|
498
|
+
console.log(`${rgb(33, 91, 184)(`i`)} ${msg$1}`);
|
|
461
499
|
};
|
|
462
500
|
const msg = (prefix, msg$1) => {
|
|
463
|
-
log.message(
|
|
501
|
+
log.message(`${rgb(33, 91, 184)(`[${prefix}]`)} ${dim(msg$1)}`);
|
|
464
502
|
};
|
|
465
503
|
const question = (msg$1, type) => {
|
|
466
504
|
return `${{
|
|
505
|
+
package: yellow,
|
|
467
506
|
version: green,
|
|
468
507
|
git: rgb(220, 94, 62),
|
|
469
508
|
npm: rgb(186, 70, 61),
|
|
@@ -476,7 +515,7 @@ const success = (msg$1, dryRun) => {
|
|
|
476
515
|
const runLifeCycleHook = async (hooks, key, dryRun) => {
|
|
477
516
|
const handler = hooks[key];
|
|
478
517
|
if (typeof handler === "function") {
|
|
479
|
-
if (!dryRun) await handler();
|
|
518
|
+
if (!dryRun) await handler(log);
|
|
480
519
|
log.success(success(`${dim`run`} ${inspect(handler)}`, dryRun));
|
|
481
520
|
return;
|
|
482
521
|
}
|
|
@@ -504,14 +543,14 @@ const diff = (from, to, separator = ".") => {
|
|
|
504
543
|
return to.split(separator).map((v, i) => v === a[i] ? dim(v) : green(v)).join(separator);
|
|
505
544
|
};
|
|
506
545
|
const formatTemplate = async (ctx, config) => {
|
|
507
|
-
const { pkg: { next } } = ctx;
|
|
508
|
-
const { git: { commitMessage: cm, tagMessage: tm
|
|
509
|
-
const f = (s, v) => s.replace("${
|
|
546
|
+
const { pkg: { next }, selectedPkg } = ctx;
|
|
547
|
+
const { git: { commitMessage: cm, tagMessage: tm }, github: { releaseName: rm }, toTag } = config;
|
|
548
|
+
const f = (s, v) => s.replace("${tag}", v);
|
|
510
549
|
const latestTag = await getLatestTag() ?? "";
|
|
511
|
-
const currentTag =
|
|
512
|
-
const commitMessage = f(cm,
|
|
513
|
-
const tagMessage = f(tm,
|
|
514
|
-
const releaseName = f(rm,
|
|
550
|
+
const currentTag = toTag(selectedPkg, next);
|
|
551
|
+
const commitMessage = f(cm, currentTag);
|
|
552
|
+
const tagMessage = f(tm, currentTag);
|
|
553
|
+
const releaseName = f(rm, currentTag);
|
|
515
554
|
Object.assign(ctx.git, {
|
|
516
555
|
latestTag,
|
|
517
556
|
currentTag,
|
|
@@ -520,6 +559,15 @@ const formatTemplate = async (ctx, config) => {
|
|
|
520
559
|
});
|
|
521
560
|
Object.assign(ctx.github, { releaseName });
|
|
522
561
|
};
|
|
562
|
+
const getPackageInfo = (pkgName, getPkgDir = (pkg$1) => `packages/${pkg$1}`) => {
|
|
563
|
+
const pkgDir = resolve(getPkgDir(pkgName));
|
|
564
|
+
const pkgPath = resolve(pkgDir, "package.json");
|
|
565
|
+
return {
|
|
566
|
+
pkg: readJsonFile(pkgPath),
|
|
567
|
+
pkgDir,
|
|
568
|
+
pkgPath
|
|
569
|
+
};
|
|
570
|
+
};
|
|
523
571
|
|
|
524
572
|
//#endregion
|
|
525
573
|
//#region src/prompts.ts
|
|
@@ -528,11 +576,11 @@ const abortTask = (msg$1) => {
|
|
|
528
576
|
process.exit(0);
|
|
529
577
|
};
|
|
530
578
|
const abortSinglePrompt = (value) => {
|
|
531
|
-
if (isCancel(value)) abortTask(
|
|
579
|
+
if (isCancel(value)) abortTask(MSG.ABORT.CANCEL);
|
|
532
580
|
};
|
|
533
581
|
const abortGroupPrompt = (ctx) => {
|
|
534
582
|
ctx && gitRollback(ctx);
|
|
535
|
-
abortTask(
|
|
583
|
+
abortTask(MSG.ABORT.CANCEL);
|
|
536
584
|
};
|
|
537
585
|
const abortOnError = (err, ctx) => {
|
|
538
586
|
ctx && gitRollback(ctx);
|
|
@@ -679,7 +727,7 @@ const runVersionPrompts = async (ctx, config) => {
|
|
|
679
727
|
};
|
|
680
728
|
const type = await selectVersion();
|
|
681
729
|
const next = type === "custom" ? await inputVersion() : type === "ignore" ? current : type;
|
|
682
|
-
ctx.isIncrement =
|
|
730
|
+
ctx.isIncrement = !eq(next, current);
|
|
683
731
|
return { next };
|
|
684
732
|
};
|
|
685
733
|
|
|
@@ -698,6 +746,7 @@ const createDefaultContext = () => {
|
|
|
698
746
|
increment: "",
|
|
699
747
|
isIncrement: false,
|
|
700
748
|
configFileExists: false,
|
|
749
|
+
selectedPkg: "",
|
|
701
750
|
pkg: {
|
|
702
751
|
current: "",
|
|
703
752
|
next: "",
|
|
@@ -725,9 +774,9 @@ const createDefaultContext = () => {
|
|
|
725
774
|
currentTag: "",
|
|
726
775
|
isCommitted: false,
|
|
727
776
|
isTagged: false,
|
|
777
|
+
isPushed: false,
|
|
728
778
|
commitMessage: "",
|
|
729
|
-
tagMessage: ""
|
|
730
|
-
tagName: ""
|
|
779
|
+
tagMessage: ""
|
|
731
780
|
},
|
|
732
781
|
github: {
|
|
733
782
|
username: "",
|
|
@@ -757,14 +806,22 @@ const createDefaultConfig = (isCI) => {
|
|
|
757
806
|
"before:release": "",
|
|
758
807
|
"after:release": ""
|
|
759
808
|
},
|
|
809
|
+
isMonorepo: false,
|
|
810
|
+
packages: [],
|
|
811
|
+
getPkgDir(pkg$1) {
|
|
812
|
+
return ".";
|
|
813
|
+
},
|
|
814
|
+
toTag(pkg$1, version) {
|
|
815
|
+
return `v${version}`;
|
|
816
|
+
},
|
|
817
|
+
changelogTagPrefix: void 0,
|
|
760
818
|
git: {
|
|
761
819
|
commit: isCI,
|
|
762
820
|
tag: isCI,
|
|
763
821
|
push: isCI,
|
|
764
|
-
commitMessage: "chore(release): ${
|
|
822
|
+
commitMessage: "chore(release): ${tag}",
|
|
765
823
|
commitArgs: [],
|
|
766
|
-
tagMessage: "
|
|
767
|
-
tagName: "${version}",
|
|
824
|
+
tagMessage: "${tag}",
|
|
768
825
|
tagArgs: [],
|
|
769
826
|
pushArgs: [],
|
|
770
827
|
addUntrackedFiles: false,
|
|
@@ -780,17 +837,13 @@ const createDefaultConfig = (isCI) => {
|
|
|
780
837
|
},
|
|
781
838
|
github: {
|
|
782
839
|
release: isCI,
|
|
783
|
-
releaseName: "
|
|
840
|
+
releaseName: "${tag}",
|
|
784
841
|
autoGenerate: false,
|
|
785
842
|
prerelease: false,
|
|
786
843
|
draft: false,
|
|
787
844
|
tokenRef: "GITHUB_TOKEN",
|
|
788
845
|
assets: [],
|
|
789
846
|
skipChecks: false
|
|
790
|
-
},
|
|
791
|
-
changelog: {
|
|
792
|
-
infile: "CHANGELOG.md",
|
|
793
|
-
types: []
|
|
794
847
|
}
|
|
795
848
|
};
|
|
796
849
|
};
|
|
@@ -803,15 +856,25 @@ const defaultTypes = [
|
|
|
803
856
|
section: "✨ 新功能",
|
|
804
857
|
description: "新增功能"
|
|
805
858
|
},
|
|
859
|
+
{
|
|
860
|
+
type: "feature",
|
|
861
|
+
section: "✨ 新功能",
|
|
862
|
+
description: "新增功能"
|
|
863
|
+
},
|
|
806
864
|
{
|
|
807
865
|
type: "fix",
|
|
808
866
|
section: "🐛 Bug 修复",
|
|
809
867
|
description: "修复 bug"
|
|
810
868
|
},
|
|
811
869
|
{
|
|
812
|
-
type: "
|
|
813
|
-
section: "
|
|
814
|
-
description: "
|
|
870
|
+
type: "perf",
|
|
871
|
+
section: "⚡ 性能优化",
|
|
872
|
+
description: "提升性能"
|
|
873
|
+
},
|
|
874
|
+
{
|
|
875
|
+
type: "revert",
|
|
876
|
+
section: "⏪ 回退",
|
|
877
|
+
description: "回退到之前版本"
|
|
815
878
|
},
|
|
816
879
|
{
|
|
817
880
|
type: "docs",
|
|
@@ -823,26 +886,21 @@ const defaultTypes = [
|
|
|
823
886
|
section: "💄 样式调整",
|
|
824
887
|
description: "样式或格式修改"
|
|
825
888
|
},
|
|
889
|
+
{
|
|
890
|
+
type: "chore",
|
|
891
|
+
section: "🎫 其他更新",
|
|
892
|
+
description: "非功能性更改"
|
|
893
|
+
},
|
|
826
894
|
{
|
|
827
895
|
type: "refactor",
|
|
828
896
|
section: "♻ 代码重构",
|
|
829
897
|
description: "重构代码"
|
|
830
898
|
},
|
|
831
|
-
{
|
|
832
|
-
type: "perf",
|
|
833
|
-
section: "⚡ 性能优化",
|
|
834
|
-
description: "提升性能"
|
|
835
|
-
},
|
|
836
899
|
{
|
|
837
900
|
type: "test",
|
|
838
901
|
section: "✅ 测试",
|
|
839
902
|
description: "测试更新"
|
|
840
903
|
},
|
|
841
|
-
{
|
|
842
|
-
type: "revert",
|
|
843
|
-
section: "⏪ 回退",
|
|
844
|
-
description: "回退到之前版本"
|
|
845
|
-
},
|
|
846
904
|
{
|
|
847
905
|
type: "build",
|
|
848
906
|
section: "👷 构建系统",
|
|
@@ -882,94 +940,80 @@ const defaultTypes = [
|
|
|
882
940
|
type: "hotfix",
|
|
883
941
|
section: "🔥 热修复",
|
|
884
942
|
description: "紧急修复"
|
|
885
|
-
},
|
|
886
|
-
{
|
|
887
|
-
type: "break",
|
|
888
|
-
section: "💥 破坏性变更",
|
|
889
|
-
description: "破坏性变更"
|
|
890
943
|
}
|
|
891
944
|
];
|
|
892
|
-
const mergeTypes = (types) => {
|
|
893
|
-
types.forEach((item) => {
|
|
894
|
-
const index = defaultTypes.findIndex((it) => it.type === item.type);
|
|
895
|
-
if (index >= 0) defaultTypes.splice(index, 1, { ...item });
|
|
896
|
-
else defaultTypes.push({ ...item });
|
|
897
|
-
});
|
|
898
|
-
};
|
|
899
|
-
const findType = (type) => {
|
|
900
|
-
return defaultTypes.find((item) => item.type === type)?.section ?? type;
|
|
901
|
-
};
|
|
902
945
|
|
|
903
946
|
//#endregion
|
|
904
947
|
//#region src/git/changelog.ts
|
|
905
|
-
const
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
const
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
948
|
+
const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
949
|
+
const pkgDir = getPkgDir();
|
|
950
|
+
const infile = join(pkgDir, "CHANGELOG.md");
|
|
951
|
+
if (!existsSync(infile)) await writeFile(infile, "");
|
|
952
|
+
const originalChangelog = readFileSync(infile, "utf-8");
|
|
953
|
+
const preset = await createPreset({ types: defaultTypes.map((t$1) => ({
|
|
954
|
+
...t$1,
|
|
955
|
+
hidden: false
|
|
956
|
+
})) });
|
|
957
|
+
preset.writer ??= {};
|
|
958
|
+
preset.writer.headerPartial = `
|
|
959
|
+
## {{#if isPatch~}} <small> {{~/if~}}
|
|
960
|
+
{{#if @root.linkCompare~}}
|
|
961
|
+
[{{version}}](
|
|
962
|
+
{{~#if @root.repository~}}
|
|
963
|
+
{{~#if @root.host}}
|
|
964
|
+
{{~@root.host}}/
|
|
965
|
+
{{~/if}}
|
|
966
|
+
{{~#if @root.owner}}
|
|
967
|
+
{{~@root.owner}}/
|
|
968
|
+
{{~/if}}
|
|
969
|
+
{{~@root.repository}}
|
|
970
|
+
{{~else}}
|
|
971
|
+
{{~@root.repoUrl}}
|
|
972
|
+
{{~/if~}}
|
|
973
|
+
/compare/{{previousTag}}...{{currentTag}})
|
|
974
|
+
{{~else}}
|
|
975
|
+
{{~version}}
|
|
976
|
+
{{~/if}}
|
|
977
|
+
{{~#if title}} "{{title}}"
|
|
978
|
+
{{~/if}}
|
|
979
|
+
{{~#if date}} ({{date}})
|
|
980
|
+
{{~/if}}
|
|
981
|
+
{{~#if isPatch~}} </small> {{~/if}}
|
|
982
|
+
`.trim() + eol();
|
|
983
|
+
preset.writer.mainTemplate = `
|
|
984
|
+
{{> header}}
|
|
985
|
+
{{#if noteGroups}}
|
|
986
|
+
{{#each noteGroups}}
|
|
987
|
+
|
|
988
|
+
### ⚠ {{title}}
|
|
989
|
+
|
|
990
|
+
{{#each notes}}
|
|
991
|
+
* {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{commit.subject}} {{#if commit.hash}}([{{commit.shortHash}}](https://github.com/{{@root.owner}}/{{@root.repository}}/commit/{{commit.hash}})){{/if}}
|
|
992
|
+
{{/each}}
|
|
993
|
+
{{/each}}
|
|
994
|
+
{{/if}}
|
|
995
|
+
{{#each commitGroups}}
|
|
996
|
+
|
|
997
|
+
{{#if title}}
|
|
998
|
+
### {{title}}
|
|
999
|
+
|
|
1000
|
+
{{/if}}
|
|
1001
|
+
{{#each commits}}
|
|
1002
|
+
{{> commit root=@root}}
|
|
1003
|
+
{{/each}}
|
|
1004
|
+
{{/each}}`.trim() + eol(2);
|
|
1005
|
+
const generator = new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount: 1 }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
1006
|
+
const writeStream = createWriteStream(infile);
|
|
1007
|
+
let changelog = "";
|
|
1008
|
+
for await (const chunk of generator.write()) {
|
|
1009
|
+
changelog += chunk;
|
|
1010
|
+
writeStream.write(chunk);
|
|
929
1011
|
}
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
const { github: { owner, repo }, git: { latestTag, currentTag } } = ctx;
|
|
934
|
-
const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
|
|
935
|
-
const github = getGithubUrl(owner, repo);
|
|
936
|
-
return [
|
|
937
|
-
latestTag ? `## [${currentTag}](${github}/compare/${latestTag}...${currentTag}) (${date})` : `## ${currentTag} (${date})`,
|
|
938
|
-
Object.entries(sections).map((entry) => {
|
|
939
|
-
const [type, items] = entry;
|
|
940
|
-
return [`### ${findType(type)}`, items.map((item) => {
|
|
941
|
-
const [desc, hash, fullHash] = item;
|
|
942
|
-
return `* **${type}** ${desc} [${hash}](${github}/commit/${fullHash})`;
|
|
943
|
-
}).join(eol(1))].join(eol(2));
|
|
944
|
-
}).join(eol(2)),
|
|
945
|
-
latestTag ? `**Full Changelog**: ${github}/compare/${latestTag}...${currentTag}` : ""
|
|
946
|
-
].join(eol(3));
|
|
947
|
-
};
|
|
948
|
-
const generateChangelog = async (ctx) => {
|
|
949
|
-
const { isIncrement } = ctx;
|
|
950
|
-
const commits = await getCommits(isIncrement);
|
|
951
|
-
if (!commits) return "";
|
|
952
|
-
const changelog = await renderChangelog(ctx, classify(commits));
|
|
953
|
-
Object.assign(ctx.github, { changelog });
|
|
1012
|
+
writeStream.write(originalChangelog);
|
|
1013
|
+
writeStream.end();
|
|
1014
|
+
await finished(writeStream);
|
|
954
1015
|
return changelog;
|
|
955
1016
|
};
|
|
956
|
-
const updateChangelog = async (file, newContent) => {
|
|
957
|
-
const infile = file || "CHANGELOG.md";
|
|
958
|
-
const target = join(process.cwd(), infile);
|
|
959
|
-
const SHORT_TITLE = "# Changelog";
|
|
960
|
-
const exist = (existsSync(target) ? await readFile(target, "utf-8") : "").replace(/\r\n/g, "\n");
|
|
961
|
-
await writeFile(target, [
|
|
962
|
-
SHORT_TITLE,
|
|
963
|
-
newContent,
|
|
964
|
-
exist.startsWith(SHORT_TITLE) ? exist.slice(11) : exist
|
|
965
|
-
].map((k) => k.trim()).join(eol(4)) + eol());
|
|
966
|
-
await runGit(["add", infile]);
|
|
967
|
-
};
|
|
968
|
-
const writeChangelog = async (ctx, config) => {
|
|
969
|
-
const { changelog: { infile, types } } = config;
|
|
970
|
-
mergeTypes(types);
|
|
971
|
-
await updateChangelog(infile, await generateChangelog(ctx));
|
|
972
|
-
};
|
|
973
1017
|
|
|
974
1018
|
//#endregion
|
|
975
1019
|
//#region src/git/prompts.ts
|
|
@@ -1063,42 +1107,65 @@ const runNpmOptPrompts = async (ctx, config) => {
|
|
|
1063
1107
|
|
|
1064
1108
|
//#endregion
|
|
1065
1109
|
//#region src/npm/publish.ts
|
|
1066
|
-
const
|
|
1067
|
-
|
|
1110
|
+
const resolvePublishRegistry = (publishConfig = {}) => {
|
|
1111
|
+
if (publishConfig.registry) return publishConfig.registry;
|
|
1112
|
+
return Object.keys(publishConfig).filter((k) => k.endsWith("registry")).map((k) => publishConfig[k])[0];
|
|
1068
1113
|
};
|
|
1069
|
-
const
|
|
1070
|
-
return void 0 !== await runNpm(["ping"
|
|
1114
|
+
const pingRegistry = async () => {
|
|
1115
|
+
return void 0 !== await runNpm(["ping"]);
|
|
1071
1116
|
};
|
|
1072
|
-
const
|
|
1073
|
-
return runNpm(["whoami"
|
|
1117
|
+
const getAuthenticatedUser = () => {
|
|
1118
|
+
return runNpm(["whoami"]);
|
|
1074
1119
|
};
|
|
1075
|
-
const
|
|
1076
|
-
const registryArg = registry ? ["--registry", registry] : [];
|
|
1120
|
+
const getVersionByTag = (name, tag) => {
|
|
1077
1121
|
return runNpm([
|
|
1078
1122
|
"show",
|
|
1079
1123
|
`${name}@${tag}`,
|
|
1080
|
-
"version"
|
|
1081
|
-
|
|
1124
|
+
"version"
|
|
1125
|
+
]);
|
|
1126
|
+
};
|
|
1127
|
+
const getPublishedVersion = (name) => {
|
|
1128
|
+
return runNpm([
|
|
1129
|
+
"info",
|
|
1130
|
+
name,
|
|
1131
|
+
"version"
|
|
1082
1132
|
]);
|
|
1083
1133
|
};
|
|
1084
|
-
const
|
|
1085
|
-
|
|
1134
|
+
const resolvePublishTag = async (pkgName, version) => {
|
|
1135
|
+
const { toPreRelease, preId } = parseVersion(version);
|
|
1136
|
+
if (!toPreRelease) {
|
|
1137
|
+
const active = await getPublishedVersion(pkgName);
|
|
1138
|
+
return active && lt(version, active) ? "previous" : "latest";
|
|
1139
|
+
} else return preId || "prerelease";
|
|
1140
|
+
};
|
|
1141
|
+
const bumpPackageVersion = (version, args = [], cwd = ".") => {
|
|
1086
1142
|
return runNpm([
|
|
1087
1143
|
"version",
|
|
1088
1144
|
version,
|
|
1089
1145
|
"--no-git-tag-version",
|
|
1090
1146
|
"--workspaces=false",
|
|
1147
|
+
"--allow-same-version",
|
|
1091
1148
|
...args
|
|
1092
|
-
]);
|
|
1149
|
+
], { cwd });
|
|
1093
1150
|
};
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1151
|
+
const isOtpError = (err) => err instanceof Error && /one-time password|otp/i.test(err.message);
|
|
1152
|
+
const publishPackage = (pkg$1 = ".", tag = "latest", args = []) => {
|
|
1153
|
+
return runNpm([
|
|
1154
|
+
"publish",
|
|
1155
|
+
"--access",
|
|
1156
|
+
"public",
|
|
1157
|
+
"--tag",
|
|
1158
|
+
tag,
|
|
1159
|
+
"--workspaces=false",
|
|
1160
|
+
...args
|
|
1161
|
+
], { cwd: pkg$1 });
|
|
1162
|
+
};
|
|
1163
|
+
const getPackageUrl = (name, next) => {
|
|
1164
|
+
return `https://www.npmjs.com/package/${name}/v/${next}`;
|
|
1098
1165
|
};
|
|
1099
1166
|
const isCollaborator = async (ctx) => {
|
|
1100
1167
|
const { pkg: { name, publishConfig }, npm: { username } } = ctx;
|
|
1101
|
-
const registry =
|
|
1168
|
+
const registry = resolvePublishRegistry(publishConfig);
|
|
1102
1169
|
const registryArg = registry ? ["--registry", registry] : [];
|
|
1103
1170
|
let npmVersion = await runNpm(["--version"]);
|
|
1104
1171
|
let accessCommand;
|
|
@@ -1114,55 +1181,38 @@ const isCollaborator = async (ctx) => {
|
|
|
1114
1181
|
name,
|
|
1115
1182
|
...registryArg
|
|
1116
1183
|
]);
|
|
1117
|
-
if (res) {
|
|
1118
|
-
|
|
1119
|
-
return permissions && permissions.includes("write");
|
|
1120
|
-
}
|
|
1121
|
-
log.error(`Unable to verify if user ${username} is a collaborator for ${name}.`);
|
|
1122
|
-
};
|
|
1123
|
-
const getPackageUrl = (ctx) => {
|
|
1124
|
-
const { pkg: { name, next } } = ctx;
|
|
1125
|
-
return `https://www.npmjs.com/package/${name}/v/${next}`;
|
|
1184
|
+
if (res) return JSON.parse(res ?? "{}")[username]?.includes("write");
|
|
1185
|
+
return false;
|
|
1126
1186
|
};
|
|
1127
1187
|
const npmCheck = async (ctx, config) => {
|
|
1128
|
-
const { pkg: { name, current, publishConfig }, npm: { username } } = ctx;
|
|
1188
|
+
const { pkg: { name, current, publishConfig: { registry } }, npm: { username } } = ctx;
|
|
1129
1189
|
const { npm: { skipChecks } } = config;
|
|
1130
|
-
const
|
|
1131
|
-
const tag = await resolveTag(current);
|
|
1190
|
+
const tag = await resolvePublishTag(name, current);
|
|
1132
1191
|
Object.assign(ctx.npm, { tag });
|
|
1133
1192
|
if (skipChecks) return "";
|
|
1134
|
-
if (!await
|
|
1193
|
+
if (!await pingRegistry()) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
|
|
1135
1194
|
const getUser = async () => {
|
|
1136
|
-
const username$1 = await
|
|
1195
|
+
const username$1 = await getAuthenticatedUser();
|
|
1137
1196
|
if (!username$1) return false;
|
|
1138
1197
|
Object.assign(ctx.npm, { username: username$1 });
|
|
1139
1198
|
return true;
|
|
1140
1199
|
};
|
|
1141
1200
|
if (!await getUser()) throw new Error(MSG.ERROR.NPM_AUTH);
|
|
1142
|
-
const
|
|
1143
|
-
if (
|
|
1201
|
+
const latest = await getVersionByTag(name, tag);
|
|
1202
|
+
if (latest) {
|
|
1144
1203
|
if (!await isCollaborator(ctx)) throw new Error(MSG.ERROR.NPM_USER(username, name));
|
|
1145
1204
|
}
|
|
1146
|
-
return
|
|
1205
|
+
return latest ? `(${tag} → ${latest})` : `(${tag} → no published version)`;
|
|
1147
1206
|
};
|
|
1148
|
-
const isOtpError = (err) => /one-time password|otp/i.test(err.message);
|
|
1149
1207
|
const publishNpm = async (ctx, config) => {
|
|
1150
|
-
const { dryRun, npm: { otp, tag }
|
|
1151
|
-
const { npm: { publish,
|
|
1152
|
-
const registry = getRegistry(publishConfig);
|
|
1208
|
+
const { dryRun, selectedPkg, npm: { otp, tag } } = ctx;
|
|
1209
|
+
const { npm: { publish, pushArgs }, getPkgDir } = config;
|
|
1153
1210
|
if (!publish) return;
|
|
1154
1211
|
const otpArgs = otp ? ["--otp", otp] : [];
|
|
1155
1212
|
const dryRunArg = dryRun ? ["--dry-run"] : [];
|
|
1156
|
-
|
|
1157
|
-
return runNpm([
|
|
1158
|
-
"publish",
|
|
1159
|
-
publishPath,
|
|
1160
|
-
"--tag",
|
|
1161
|
-
tag,
|
|
1162
|
-
"--workspaces=false",
|
|
1213
|
+
return publishPackage(getPkgDir(selectedPkg), tag, [
|
|
1163
1214
|
...otpArgs,
|
|
1164
1215
|
...pushArgs,
|
|
1165
|
-
...registryArg,
|
|
1166
1216
|
...dryRunArg
|
|
1167
1217
|
]);
|
|
1168
1218
|
};
|
|
@@ -1172,17 +1222,38 @@ const publishNpm = async (ctx, config) => {
|
|
|
1172
1222
|
var Action = class {
|
|
1173
1223
|
async handle(cmdArgs, options) {
|
|
1174
1224
|
const { version: cVersion, name: cName } = readJsonFile(join(__dirname, "../package.json"));
|
|
1175
|
-
|
|
1176
|
-
const { otp, ...others } = options;
|
|
1225
|
+
const { otp, package: defPkg, ...others } = options;
|
|
1177
1226
|
const { showChangelog, showRelease, ci, dryRun } = Object.fromEntries(Object.entries(others).map(([k, v]) => [k, Boolean(v)]));
|
|
1178
1227
|
process.env["dryRun"] = String(dryRun);
|
|
1179
|
-
const {
|
|
1180
|
-
const nextVersion = inc(pkgVersion, cmdArgs) ?? "";
|
|
1181
|
-
const local = await resolveConfig(process.cwd(), (path$1) => {
|
|
1182
|
-
info(`config file: ${underline(dim(path$1))}`);
|
|
1183
|
-
});
|
|
1228
|
+
const { configPath, config: local } = await resolveConfig(process.cwd());
|
|
1184
1229
|
const config = mergeConfig(createDefaultConfig(ci ? true : void 0), local);
|
|
1230
|
+
info(MSG.INFO.TOOL(cName, cVersion));
|
|
1231
|
+
info(MSG.INFO.CONFIG(configPath));
|
|
1232
|
+
console.log();
|
|
1233
|
+
intro(MSG.INTRO(dryRun));
|
|
1234
|
+
const { isMonorepo, packages, getPkgDir } = config;
|
|
1235
|
+
if (isMonorepo) {
|
|
1236
|
+
if (ci) {
|
|
1237
|
+
if (!defPkg) abortTask(MSG.ABORT.MONOREPO_CI_NO_PACKAGE);
|
|
1238
|
+
} else if (packages.length < 1) abortTask(MSG.ABORT.MONOREPO_NO_PACKAGES);
|
|
1239
|
+
}
|
|
1240
|
+
const selectPackage = async () => {
|
|
1241
|
+
const pkg$2 = await select({
|
|
1242
|
+
message: question(MSG.PROMPT.SELECT_PACKAGE, "package"),
|
|
1243
|
+
options: packages.map((pkg$3) => ({
|
|
1244
|
+
label: pkg$3,
|
|
1245
|
+
value: pkg$3
|
|
1246
|
+
}))
|
|
1247
|
+
});
|
|
1248
|
+
abortSinglePrompt(pkg$2);
|
|
1249
|
+
return pkg$2;
|
|
1250
|
+
};
|
|
1251
|
+
const selectedPkg = !isMonorepo ? "" : ci ? defPkg : packages.length === 1 ? packages[0] : await selectPackage();
|
|
1252
|
+
const { pkg: pkg$1, pkgDir } = getPackageInfo(selectedPkg, getPkgDir);
|
|
1253
|
+
const { version: pkgVersion, private: pkgPrivate = false, name: pkgName, publishConfig = {} } = pkg$1;
|
|
1254
|
+
const nextVersion = inc(pkgVersion, cmdArgs) ?? "";
|
|
1185
1255
|
const ctx = mergeConfig(createDefaultContext(), {
|
|
1256
|
+
selectedPkg,
|
|
1186
1257
|
configFileExists: Object.keys(local).length > 0,
|
|
1187
1258
|
dryRun,
|
|
1188
1259
|
showRelease,
|
|
@@ -1208,8 +1279,11 @@ var Action = class {
|
|
|
1208
1279
|
},
|
|
1209
1280
|
noNpm: pkgPrivate
|
|
1210
1281
|
});
|
|
1211
|
-
|
|
1212
|
-
|
|
1282
|
+
const { messages } = await publint({ pkgDir });
|
|
1283
|
+
for (const message of messages) {
|
|
1284
|
+
const formated = formatMessage(message, pkg$1);
|
|
1285
|
+
formated && msg("PKG", formated);
|
|
1286
|
+
}
|
|
1213
1287
|
await this.checkTask(ctx, config);
|
|
1214
1288
|
await this.bumpTask(ctx, config);
|
|
1215
1289
|
await this.changelogTask(ctx, config);
|
|
@@ -1239,8 +1313,8 @@ var Action = class {
|
|
|
1239
1313
|
}]).catch(abortOnError);
|
|
1240
1314
|
}
|
|
1241
1315
|
async bumpTask(ctx, config) {
|
|
1242
|
-
const { pkg: { current }, dryRun, isCI, showRelease, showChangelog } = ctx;
|
|
1243
|
-
const { hooks } = config;
|
|
1316
|
+
const { pkg: { current }, dryRun, isCI, showRelease, showChangelog, selectedPkg } = ctx;
|
|
1317
|
+
const { hooks, getPkgDir } = config;
|
|
1244
1318
|
const need = (ctx$1) => {
|
|
1245
1319
|
const { pkg: { next: next$1 }, isIncrement: isIncrement$1 } = ctx$1;
|
|
1246
1320
|
return !next$1 && isIncrement$1;
|
|
@@ -1255,32 +1329,39 @@ var Action = class {
|
|
|
1255
1329
|
await tasks([{
|
|
1256
1330
|
title: MSG.TASK.VERSION.START,
|
|
1257
1331
|
task: async () => {
|
|
1258
|
-
await
|
|
1332
|
+
await bumpPackageVersion(next, [], getPkgDir(selectedPkg));
|
|
1259
1333
|
await formatTemplate(ctx, config);
|
|
1260
|
-
const to = `(${current}...${diff(current, next)})`;
|
|
1261
|
-
return success(MSG.TASK.VERSION.END(to), dryRun);
|
|
1334
|
+
const to$1 = `(${current}...${diff(current, next)})`;
|
|
1335
|
+
return success(MSG.TASK.VERSION.END(to$1), dryRun);
|
|
1262
1336
|
}
|
|
1263
1337
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1264
1338
|
await runLifeCycleHook(hooks, "after:bump", dryRun);
|
|
1265
|
-
const
|
|
1339
|
+
const { from, to } = await resolveChangelogRange(isIncrement);
|
|
1340
|
+
const logStr = await getLog(from, to, getPkgDir(selectedPkg));
|
|
1266
1341
|
if (logStr) {
|
|
1267
|
-
msg("GIT", `Changelog:${eol(2)}` +
|
|
1342
|
+
msg("GIT", `Changelog:${eol(2)}` + logStr);
|
|
1268
1343
|
if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
|
|
1269
1344
|
} else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
|
|
1270
|
-
const changeset = await getStatus();
|
|
1271
|
-
if (changeset) msg("GIT", `Changes:${eol(2)}` + coloredChangeset(changeset));
|
|
1272
|
-
else msg("GIT", MSG.LOG.CHANGES_EMPTY);
|
|
1273
1345
|
}
|
|
1274
1346
|
async changelogTask(ctx, config) {
|
|
1275
|
-
const { isIncrement, dryRun } = ctx;
|
|
1347
|
+
const { isIncrement, dryRun, selectedPkg } = ctx;
|
|
1348
|
+
const { getPkgDir, changelogTagPrefix } = config;
|
|
1276
1349
|
await tasks([{
|
|
1277
1350
|
title: MSG.TASK.CHANGELOG.START,
|
|
1278
1351
|
task: async () => {
|
|
1279
|
-
await
|
|
1352
|
+
const changelog = await generateChangelog({
|
|
1353
|
+
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1354
|
+
tagPrefix: changelogTagPrefix?.(selectedPkg)
|
|
1355
|
+
});
|
|
1356
|
+
await runGit(["add", `${getPkgDir(selectedPkg)}/CHANGELOG.md`]);
|
|
1357
|
+
Object.assign(ctx.github, { changelog });
|
|
1280
1358
|
return success(MSG.TASK.CHANGELOG.END, dryRun);
|
|
1281
1359
|
},
|
|
1282
1360
|
enabled: isIncrement
|
|
1283
1361
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1362
|
+
const changeset = await getStatus();
|
|
1363
|
+
if (changeset) msg("GIT", `Changes:${eol(2)}` + coloredChangeset(changeset));
|
|
1364
|
+
else msg("GIT", MSG.LOG.CHANGES_EMPTY);
|
|
1284
1365
|
}
|
|
1285
1366
|
async gitTask(ctx, config) {
|
|
1286
1367
|
const { noGit, dryRun } = ctx;
|
|
@@ -1298,7 +1379,7 @@ var Action = class {
|
|
|
1298
1379
|
await runLifeCycleHook(hooks, "after:push", dryRun);
|
|
1299
1380
|
}
|
|
1300
1381
|
async npmTask(ctx, config) {
|
|
1301
|
-
const { noNpm, dryRun, isCI } = ctx;
|
|
1382
|
+
const { noNpm, dryRun, isCI, pkg: { name, next } } = ctx;
|
|
1302
1383
|
const { hooks } = config;
|
|
1303
1384
|
if (noNpm) return;
|
|
1304
1385
|
await runNpmPublishPrompts(ctx, config);
|
|
@@ -1312,7 +1393,7 @@ var Action = class {
|
|
|
1312
1393
|
await publishNpm(ctx, config);
|
|
1313
1394
|
} else throw err;
|
|
1314
1395
|
});
|
|
1315
|
-
const url = getPackageUrl(
|
|
1396
|
+
const url = getPackageUrl(name, next);
|
|
1316
1397
|
return success(MSG.TASK.NPM.END(url), dryRun);
|
|
1317
1398
|
}
|
|
1318
1399
|
}]).catch((err) => abortOnError(err, ctx));
|
|
@@ -1339,9 +1420,33 @@ var Action = class {
|
|
|
1339
1420
|
//#endregion
|
|
1340
1421
|
//#region src/cli.ts
|
|
1341
1422
|
const pkg = readJsonFile(join(__dirname, "..", "package.json"));
|
|
1342
|
-
program.name(
|
|
1343
|
-
await new Action().handle(
|
|
1344
|
-
})
|
|
1423
|
+
program.name("release").description(pkg.description).version(pkg.version, "-v, --version", "Print the tool version and exit.").usage("[release-type] [options]").argument("[release-type]", "Version bump type: patch | minor | major").option("-n, --dry-run", "Run in dry mode: show what would be released without making any changes.", false).option("-p, --package <pkg>", "Specify package name for mono-repo release (CI only).", "").option("--otp <code>", "One-time password for npm publish (used for 2FA).", "").option("--ci", "Enable CI mode: disable prompts and fail on missing required options.", false).option("--show-changelog", "Print the generated changelog and exit.", false).option("--show-release", "Print the version that would be released and exit.", false).helpOption("-h, --help", "Display help information.").action(async (releaseType, options) => {
|
|
1424
|
+
await new Action().handle(releaseType, options);
|
|
1425
|
+
});
|
|
1426
|
+
program.command("init").description("Create a release configuration file").option("-f, --force", "Overwrite existing config file", false).option("-m, --monorepo", "Mono-repo project", false).action((options) => {
|
|
1427
|
+
const { force = false, monorepo = false } = options;
|
|
1428
|
+
const cwd = process.cwd();
|
|
1429
|
+
const { type } = readJsonFile(join(cwd, "package.json"));
|
|
1430
|
+
const isESM = type === "module";
|
|
1431
|
+
const isTS = existsSync(join(cwd, "tsconfig.json"));
|
|
1432
|
+
const configPath = join(cwd, `release.config.${isESM ? "" : "m"}${isTS ? "ts" : "js"}`);
|
|
1433
|
+
if (existsSync(configPath) && !force) {
|
|
1434
|
+
console.error(`${red`Error:`} ${yellow`release.config.ts`} already exists.\nUse ${green`--force`} to overwrite.`);
|
|
1435
|
+
process.exit(1);
|
|
1436
|
+
}
|
|
1437
|
+
const monoConfigToString = () => `{
|
|
1438
|
+
isMonorepo: true,
|
|
1439
|
+
packages: [ 'demo-a', 'demo-b' ],
|
|
1440
|
+
getPkgDir: (pkg) => \`packages/\${ pkg }\`,
|
|
1441
|
+
toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
|
|
1442
|
+
changelogTagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1443
|
+
}`;
|
|
1444
|
+
writeFileSync(configPath, `import { defineConfig } from '@peiyanlu/release'
|
|
1445
|
+
|
|
1446
|
+
export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`, "utf-8");
|
|
1447
|
+
console.log(`${green`✔ release.config.ts`} created successfully`);
|
|
1448
|
+
});
|
|
1449
|
+
program.parse(process.argv);
|
|
1345
1450
|
program.on("command:*", () => {
|
|
1346
1451
|
console.error(`\n${red`Error`} Invalid command: ${red`%s`}`, program.args.join(" "));
|
|
1347
1452
|
console.log(`See ${red`--help`} for a list of available commands.\n`);
|