@peiyanlu/release 0.0.6 → 0.0.8
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 +43 -1
- package/dist/cli.mjs +115 -165
- package/dist/index.d.mts +2 -3
- package/dist/index.mjs +23 -139
- package/package.json +24 -24
package/README.md
CHANGED
|
@@ -65,6 +65,18 @@ pnpm release minor --ci
|
|
|
65
65
|
| `--show-changelog` | 打印生成的 Changelog 并退出 |
|
|
66
66
|
| `--ci` | 启用 CI 模式:禁用交互提示,缺少必要参数时直接失败;GitHub Actions 等 CI/CD 环境中默认启用 |
|
|
67
67
|
|
|
68
|
+
### Prepare 相关参数
|
|
69
|
+
|
|
70
|
+
| 参数 | 说明 |
|
|
71
|
+
|-------------|--------------------------|
|
|
72
|
+
| `--prepare` | 仅仅变更版本、提交代码、创建 Tag、推送到远程 |
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pnpm release minor --prepare --ci --package pkg-a
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
> 后续可以通过 tag 进行 npm 发布和创建 GitHub release
|
|
79
|
+
|
|
68
80
|
### Monorepo / CI 相关参数
|
|
69
81
|
|
|
70
82
|
| 参数 | 说明 |
|
|
@@ -72,7 +84,7 @@ pnpm release minor --ci
|
|
|
72
84
|
| `-p, --package <pkg>` | 指定要发布的子包名称(仅用于 Monorepo 的 CI 场景) |
|
|
73
85
|
|
|
74
86
|
```bash
|
|
75
|
-
pnpm release minor --ci --package
|
|
87
|
+
pnpm release minor --ci --package pkg-a
|
|
76
88
|
```
|
|
77
89
|
|
|
78
90
|
### npm 发布相关参数
|
|
@@ -268,6 +280,36 @@ GitHub Release 可基于 Git Tag 自动创建,并支持附带:
|
|
|
268
280
|
|
|
269
281
|
工具默认配置偏向于 **单仓库(Single Repo)** 项目,但是可以通过配置使其支持 **Monorepo**
|
|
270
282
|
|
|
283
|
+
## 流程拆分
|
|
284
|
+
|
|
285
|
+
### 本地准备 Release(仅创建 Tag)
|
|
286
|
+
|
|
287
|
+
在本地完成版本确认与变更准备:
|
|
288
|
+
|
|
289
|
+
- 确定下一个版本
|
|
290
|
+
- 更新 `package.json` / `CHANGELOG`
|
|
291
|
+
- 创建发布提交
|
|
292
|
+
- 创建并推送 git 标签
|
|
293
|
+
|
|
294
|
+
### CI/CD 自动发布(由 Tag 触发)
|
|
295
|
+
|
|
296
|
+
当 Tag 推送到远端后,CI/CD 自动执行:
|
|
297
|
+
|
|
298
|
+
- 发布到 npm
|
|
299
|
+
- 创建 GItHub release
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
// publishCI.ts
|
|
303
|
+
import { publishTagToNpm } from '@peiyanlu/release'
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
await publishTagToNpm({
|
|
307
|
+
gitTag: '1.0.16',
|
|
308
|
+
getPkgDir: () => '.',
|
|
309
|
+
defaultPackage: '.',
|
|
310
|
+
})
|
|
311
|
+
```
|
|
312
|
+
|
|
271
313
|
## Dry Run
|
|
272
314
|
|
|
273
315
|
```bash
|
package/dist/cli.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import path, { join, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
|
-
import { bumpPackageVersion, coloredChangeset, eol, execAsync, getAllRemotes, getAuthenticatedUser, getCurrentBranch, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLog, getPackageInfo, getPackageUrl, getPublishedVersion, getRemote, getStatus, gitAddAll, gitAddTracked, gitCommit, gitTagAnnotated, hasWriteAccess, isGitRepo, isWorkingDirClean, parseGitHubRepo, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, resolveChangelogRange } from "@peiyanlu/cli-utils";
|
|
3
|
+
import { bumpPackageVersion, coloredChangeset, eol, execAsync, getAllRemotes, getAuthenticatedUser, getCurrentBranch, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLog, getPackageInfo, getPackageUrl, getPublishedVersion, getRemote, getStatus, gitAddAll, gitAddTracked, gitCommit, gitTagAnnotated, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValid, isWorkingDirClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, resolveChangelogRange, resolvePublishTag } from "@peiyanlu/cli-utils";
|
|
4
4
|
import { dim, green, red, rgb, underline, yellow } from "ansis";
|
|
5
5
|
import { program } from "commander";
|
|
6
6
|
import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
|
|
8
8
|
import { publint } from "publint";
|
|
9
9
|
import { formatMessage } from "publint/utils";
|
|
10
|
-
import {
|
|
11
|
-
import { castArray, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
|
|
10
|
+
import { inc, neq } from "semver";
|
|
11
|
+
import { castArray, isFunction, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
|
|
12
12
|
import { spawnSync } from "node:child_process";
|
|
13
13
|
import { inspect } from "node:util";
|
|
14
14
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
@@ -19,12 +19,10 @@ import { Octokit } from "@octokit/rest";
|
|
|
19
19
|
import { contentType } from "mime-types";
|
|
20
20
|
import open from "open";
|
|
21
21
|
import { glob } from "tinyglobby";
|
|
22
|
-
|
|
23
|
-
//#region node_modules/.pnpm/tsdown@0.18.4_publint@0.3.17_typescript@5.9.3/node_modules/tsdown/esm-shims.js
|
|
22
|
+
//#region node_modules/.pnpm/tsdown@0.21.7_publint@0.3.18_typescript@6.0.2/node_modules/tsdown/esm-shims.js
|
|
24
23
|
const getFilename = () => fileURLToPath(import.meta.url);
|
|
25
24
|
const getDirname = () => path.dirname(getFilename());
|
|
26
25
|
const __dirname = /* @__PURE__ */ getDirname();
|
|
27
|
-
|
|
28
26
|
//#endregion
|
|
29
27
|
//#region src/config.ts
|
|
30
28
|
const findConfigFile = (cwd = process.cwd()) => {
|
|
@@ -34,8 +32,8 @@ const findConfigFile = (cwd = process.cwd()) => {
|
|
|
34
32
|
"js",
|
|
35
33
|
"mjs"
|
|
36
34
|
]) {
|
|
37
|
-
const path
|
|
38
|
-
if (existsSync(path
|
|
35
|
+
const path = resolve(cwd, `release.config.${ext}`);
|
|
36
|
+
if (existsSync(path)) return path;
|
|
39
37
|
}
|
|
40
38
|
};
|
|
41
39
|
const loadConfig = async (file) => {
|
|
@@ -55,7 +53,6 @@ const resolveConfig = async (cwd) => {
|
|
|
55
53
|
config: await loadConfig(configFile)
|
|
56
54
|
};
|
|
57
55
|
};
|
|
58
|
-
const isFunction = (v) => typeof v === "function";
|
|
59
56
|
const mergeConfig = (defaults, overrides) => {
|
|
60
57
|
if (!overrides) return structuredClone(defaults);
|
|
61
58
|
if (Object.keys(defaults).length === 0) return structuredClone(overrides);
|
|
@@ -71,15 +68,15 @@ const mergeConfig = (defaults, overrides) => {
|
|
|
71
68
|
}
|
|
72
69
|
return result;
|
|
73
70
|
};
|
|
74
|
-
|
|
75
71
|
//#endregion
|
|
76
72
|
//#region src/messages.ts
|
|
77
73
|
const n = (b) => b ? "(dry run)" : "";
|
|
78
74
|
const t = () => green(`${Math.floor(process.uptime())}s`);
|
|
79
75
|
const MSG = {
|
|
80
76
|
INTRO: (dryRun) => `🚀 Starting release ${n(dryRun)}`,
|
|
77
|
+
INTRO_PREPARE: (dryRun) => `🚀 Starting prepare release ${n(dryRun)}`,
|
|
81
78
|
OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()} ${n(dryRun)}`,
|
|
82
|
-
OUTRO_PREPARE: (dryRun) => `🎉
|
|
79
|
+
OUTRO_PREPARE: (dryRun) => `🎉 Prepare release finished successfully in ${t()} ${n(dryRun)}`,
|
|
83
80
|
LOG: {
|
|
84
81
|
SHOW_VERSION: (version) => `🎉 Released ${green(version)}`,
|
|
85
82
|
SHOW_CHANGELOG: "🎉 Changelog collected",
|
|
@@ -93,11 +90,11 @@ const MSG = {
|
|
|
93
90
|
},
|
|
94
91
|
NPM: {
|
|
95
92
|
CHECKING: "Checking npm registry",
|
|
96
|
-
CHECKED: (registry, msg
|
|
93
|
+
CHECKED: (registry, msg) => `NPM registry: ${underline(dim(registry))} ${underline(dim(msg))}`
|
|
97
94
|
},
|
|
98
95
|
GITHUB: {
|
|
99
96
|
CHECKING: "Checking github repository",
|
|
100
|
-
CHECKED: (repository, msg
|
|
97
|
+
CHECKED: (repository, msg) => `GitHub repository: ${underline(dim(repository))} ${underline(dim(msg))}`
|
|
101
98
|
}
|
|
102
99
|
},
|
|
103
100
|
TASK: {
|
|
@@ -126,11 +123,11 @@ const MSG = {
|
|
|
126
123
|
SELECT_PACKAGE: "Select package release:",
|
|
127
124
|
SELECT_VERSION: "Select version bump:",
|
|
128
125
|
INPUT_VERSION: "Enter custom version:",
|
|
129
|
-
NPM_PUBLISH: (pkg
|
|
126
|
+
NPM_PUBLISH: (pkg) => `Publish to npm ${underline(dim`(${pkg})`)}?`,
|
|
130
127
|
NPM_OTP: "Enter npm one-time password (OTP):",
|
|
131
128
|
GITHUB_RELEASE: (title) => `Create a new GitHub release ${underline(dim`(${title})`)}?`,
|
|
132
|
-
GIT_COMMIT: (msg
|
|
133
|
-
GIT_TAG: (msg
|
|
129
|
+
GIT_COMMIT: (msg) => `Create git commit ${underline(dim`(${msg})`)}?`,
|
|
130
|
+
GIT_TAG: (msg) => `Create git tag ${underline(dim`(${msg})`)}?`,
|
|
134
131
|
GIT_PUSH: "Push commits and tags to remote?"
|
|
135
132
|
},
|
|
136
133
|
ERROR: {
|
|
@@ -152,10 +149,9 @@ const MSG = {
|
|
|
152
149
|
},
|
|
153
150
|
INFO: {
|
|
154
151
|
TOOL: (name, version) => `${name} ${dim(`v${version}`)}`,
|
|
155
|
-
CONFIG: (path
|
|
152
|
+
CONFIG: (path) => `config file: ${underline(dim(path || "not found"))}`
|
|
156
153
|
}
|
|
157
154
|
};
|
|
158
|
-
|
|
159
155
|
//#endregion
|
|
160
156
|
//#region src/git/commit.ts
|
|
161
157
|
const gitCheck = async (ctx, config) => {
|
|
@@ -197,9 +193,9 @@ const commitAndTag = async (ctx, config) => {
|
|
|
197
193
|
}
|
|
198
194
|
if (push) {
|
|
199
195
|
const remotes = await getAllRemotes();
|
|
200
|
-
for (const remoteName
|
|
201
|
-
await pushTag(remoteName
|
|
202
|
-
await pushBranch(remoteName
|
|
196
|
+
for (const remoteName of remotes) {
|
|
197
|
+
await pushTag(remoteName, currentTag, args);
|
|
198
|
+
await pushBranch(remoteName, await getCurrentBranch(), [...pushArgs, ...args]);
|
|
203
199
|
}
|
|
204
200
|
if (!dryRun) Object.assign(ctx.git, { isPushed: true });
|
|
205
201
|
}
|
|
@@ -219,11 +215,10 @@ const gitRollback = (ctx) => {
|
|
|
219
215
|
isCommitted ? "HEAD~1" : "HEAD"
|
|
220
216
|
]);
|
|
221
217
|
};
|
|
222
|
-
|
|
223
218
|
//#endregion
|
|
224
219
|
//#region src/prompts.ts
|
|
225
|
-
const abortTask = (msg
|
|
226
|
-
cancel(`${red("✕")} ${msg
|
|
220
|
+
const abortTask = (msg) => {
|
|
221
|
+
cancel(`${red("✕")} ${msg}`);
|
|
227
222
|
process.exit(0);
|
|
228
223
|
};
|
|
229
224
|
const abortSinglePrompt = (value) => {
|
|
@@ -235,34 +230,33 @@ const abortGroupPrompt = (ctx) => {
|
|
|
235
230
|
};
|
|
236
231
|
const abortOnError = (err, ctx) => {
|
|
237
232
|
ctx && gitRollback(ctx);
|
|
238
|
-
const msg
|
|
239
|
-
log.message(`\n${msg
|
|
233
|
+
const msg = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
|
|
234
|
+
log.message(`\n${msg}\n`);
|
|
240
235
|
process.exit(1);
|
|
241
236
|
};
|
|
242
|
-
const taskEnd = (msg
|
|
243
|
-
outro(msg
|
|
237
|
+
const taskEnd = (msg) => {
|
|
238
|
+
outro(msg);
|
|
244
239
|
process.exit(0);
|
|
245
240
|
};
|
|
246
|
-
|
|
247
241
|
//#endregion
|
|
248
242
|
//#region src/utils.ts
|
|
249
|
-
const info = (msg
|
|
250
|
-
console.log(`${rgb(33, 91, 184)(`i`)} ${msg
|
|
243
|
+
const info = (msg) => {
|
|
244
|
+
console.log(`${rgb(33, 91, 184)(`i`)} ${msg}`);
|
|
251
245
|
};
|
|
252
|
-
const msg = (prefix, msg
|
|
253
|
-
log.message(`${rgb(33, 91, 184)(`[${prefix}]`)} ${dim(msg
|
|
246
|
+
const msg = (prefix, msg) => {
|
|
247
|
+
log.message(`${rgb(33, 91, 184)(`[${prefix}]`)} ${dim(msg)}`);
|
|
254
248
|
};
|
|
255
|
-
const question = (msg
|
|
249
|
+
const question = (msg, type) => {
|
|
256
250
|
return `${{
|
|
257
251
|
package: yellow,
|
|
258
252
|
version: green,
|
|
259
253
|
git: rgb(220, 94, 62),
|
|
260
254
|
npm: rgb(186, 70, 61),
|
|
261
255
|
github: rgb(64, 110, 228)
|
|
262
|
-
}[type]`?`} ${msg
|
|
256
|
+
}[type]`?`} ${msg}`;
|
|
263
257
|
};
|
|
264
|
-
const success = (msg
|
|
265
|
-
return `${(dryRun ? rgb(123, 115, 66) : green)("✔")} ${msg
|
|
258
|
+
const success = (msg, dryRun) => {
|
|
259
|
+
return `${(dryRun ? rgb(123, 115, 66) : green)("✔")} ${msg}`;
|
|
266
260
|
};
|
|
267
261
|
const runLifeCycleHook = async (hooks, key, dryRun) => {
|
|
268
262
|
const handler = hooks[key];
|
|
@@ -297,60 +291,31 @@ const formatTemplate = async (ctx, config) => {
|
|
|
297
291
|
});
|
|
298
292
|
Object.assign(ctx.github, { releaseName });
|
|
299
293
|
};
|
|
300
|
-
|
|
301
|
-
//#endregion
|
|
302
|
-
//#region src/version/bump.ts
|
|
303
|
-
const isPreRelease = (version) => {
|
|
304
|
-
return Boolean(prerelease(version));
|
|
305
|
-
};
|
|
306
|
-
const isValid = (version) => {
|
|
307
|
-
return Boolean(valid(version));
|
|
308
|
-
};
|
|
309
|
-
const parseVersion = (raw) => {
|
|
310
|
-
const next = isValid(raw) ? raw : coerce(raw)?.toString();
|
|
311
|
-
if (!next) return {};
|
|
312
|
-
const { prerelease: prerelease$1 } = parse(next);
|
|
313
|
-
const toPreRelease = Boolean(prerelease$1.length);
|
|
314
|
-
const [preId, preBase] = prerelease$1.map(String);
|
|
315
|
-
return {
|
|
316
|
-
next,
|
|
317
|
-
toPreRelease,
|
|
318
|
-
preId,
|
|
319
|
-
preBase
|
|
320
|
-
};
|
|
321
|
-
};
|
|
322
|
-
const getCIVersion = (current) => {
|
|
323
|
-
return inc(current, Release.PATCH);
|
|
324
|
-
};
|
|
325
|
-
const isNeq = (current, next) => {
|
|
326
|
-
return neq(current, next);
|
|
327
|
-
};
|
|
328
|
-
|
|
329
294
|
//#endregion
|
|
330
295
|
//#region src/version/prompts.ts
|
|
331
|
-
let Release = /* @__PURE__ */ function(Release
|
|
332
|
-
Release
|
|
333
|
-
Release
|
|
334
|
-
Release
|
|
335
|
-
Release
|
|
336
|
-
return Release
|
|
296
|
+
let Release = /* @__PURE__ */ function(Release) {
|
|
297
|
+
Release["MAJOR"] = "major";
|
|
298
|
+
Release["MINOR"] = "minor";
|
|
299
|
+
Release["PATCH"] = "patch";
|
|
300
|
+
Release["RELEASE"] = "release";
|
|
301
|
+
return Release;
|
|
337
302
|
}({});
|
|
338
|
-
let PreRelease = /* @__PURE__ */ function(PreRelease
|
|
339
|
-
PreRelease
|
|
340
|
-
PreRelease
|
|
341
|
-
PreRelease
|
|
342
|
-
PreRelease
|
|
343
|
-
return PreRelease
|
|
303
|
+
let PreRelease = /* @__PURE__ */ function(PreRelease) {
|
|
304
|
+
PreRelease["MAJOR"] = "premajor";
|
|
305
|
+
PreRelease["MINOR"] = "preminor";
|
|
306
|
+
PreRelease["PATCH"] = "prepatch";
|
|
307
|
+
PreRelease["RELEASE"] = "prerelease";
|
|
308
|
+
return PreRelease;
|
|
344
309
|
}({});
|
|
345
|
-
let PreId = /* @__PURE__ */ function(PreId
|
|
346
|
-
PreId
|
|
347
|
-
PreId
|
|
348
|
-
PreId
|
|
349
|
-
PreId
|
|
350
|
-
PreId
|
|
351
|
-
PreId
|
|
352
|
-
PreId
|
|
353
|
-
return PreId
|
|
310
|
+
let PreId = /* @__PURE__ */ function(PreId) {
|
|
311
|
+
PreId["ALPHA"] = "alpha";
|
|
312
|
+
PreId["BETA"] = "beta";
|
|
313
|
+
PreId["RC"] = "rc";
|
|
314
|
+
PreId["NET"] = "next";
|
|
315
|
+
PreId["CANARY"] = "canary";
|
|
316
|
+
PreId["NIGHT"] = "nightly";
|
|
317
|
+
PreId["DEV"] = "dev";
|
|
318
|
+
return PreId;
|
|
354
319
|
}({});
|
|
355
320
|
const createVersionMap = (current) => ({
|
|
356
321
|
patch: inc(current, Release.PATCH),
|
|
@@ -403,14 +368,13 @@ const runVersionPrompts = async (ctx, config) => {
|
|
|
403
368
|
abortSinglePrompt(version);
|
|
404
369
|
return version;
|
|
405
370
|
};
|
|
406
|
-
const resolveNextVersion = async (type, current
|
|
407
|
-
if (type === "ignore") return current
|
|
371
|
+
const resolveNextVersion = async (type, current) => {
|
|
372
|
+
if (type === "ignore") return current;
|
|
408
373
|
if (type === "custom") return inputVersion();
|
|
409
|
-
return createVersionMap(current
|
|
374
|
+
return createVersionMap(current)[type];
|
|
410
375
|
};
|
|
411
376
|
return await resolveNextVersion(await selectVersion(), current);
|
|
412
377
|
};
|
|
413
|
-
|
|
414
378
|
//#endregion
|
|
415
379
|
//#region src/defaults.ts
|
|
416
380
|
const createDefaultContext = () => {
|
|
@@ -488,10 +452,10 @@ const createDefaultConfig = (isCI) => {
|
|
|
488
452
|
},
|
|
489
453
|
isMonorepo: false,
|
|
490
454
|
packages: [],
|
|
491
|
-
getPkgDir(pkg
|
|
455
|
+
getPkgDir(pkg) {
|
|
492
456
|
return ".";
|
|
493
457
|
},
|
|
494
|
-
toTag(pkg
|
|
458
|
+
toTag(pkg, version) {
|
|
495
459
|
return `v${version}`;
|
|
496
460
|
},
|
|
497
461
|
tagPrefix: void 0,
|
|
@@ -526,7 +490,6 @@ const createDefaultConfig = (isCI) => {
|
|
|
526
490
|
}
|
|
527
491
|
};
|
|
528
492
|
};
|
|
529
|
-
|
|
530
493
|
//#endregion
|
|
531
494
|
//#region src/git/changetype.ts
|
|
532
495
|
const defaultTypes = [
|
|
@@ -621,13 +584,11 @@ const defaultTypes = [
|
|
|
621
584
|
description: "紧急修复"
|
|
622
585
|
}
|
|
623
586
|
];
|
|
624
|
-
|
|
625
587
|
//#endregion
|
|
626
588
|
//#region src/git/changelog.ts
|
|
627
|
-
const
|
|
628
|
-
const
|
|
629
|
-
|
|
630
|
-
...t$1,
|
|
589
|
+
const parsePreset = async () => {
|
|
590
|
+
const preset = await createPreset({ types: defaultTypes.map((t) => ({
|
|
591
|
+
...t,
|
|
631
592
|
hidden: false
|
|
632
593
|
})) });
|
|
633
594
|
preset.writer ??= {};
|
|
@@ -678,6 +639,11 @@ const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
|
678
639
|
{{> commit root=@root}}
|
|
679
640
|
{{/each}}
|
|
680
641
|
{{/each}}`.trim() + eol(2);
|
|
642
|
+
return preset;
|
|
643
|
+
};
|
|
644
|
+
const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
645
|
+
const pkgDir = getPkgDir();
|
|
646
|
+
const preset = await parsePreset();
|
|
681
647
|
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount: 1 }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
682
648
|
};
|
|
683
649
|
const getChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
@@ -703,7 +669,6 @@ const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
|
703
669
|
writeStream.end();
|
|
704
670
|
await finished(writeStream);
|
|
705
671
|
};
|
|
706
|
-
|
|
707
672
|
//#endregion
|
|
708
673
|
//#region src/git/prompts.ts
|
|
709
674
|
const runGitPrompts = async (ctx, config) => {
|
|
@@ -739,7 +704,6 @@ const runGitPrompts = async (ctx, config) => {
|
|
|
739
704
|
} });
|
|
740
705
|
Object.assign(config.git, reset);
|
|
741
706
|
};
|
|
742
|
-
|
|
743
707
|
//#endregion
|
|
744
708
|
//#region src/github/prompts.ts
|
|
745
709
|
const runGithubPrompts = async (ctx, config) => {
|
|
@@ -762,7 +726,6 @@ const runGithubPrompts = async (ctx, config) => {
|
|
|
762
726
|
Object.assign(config.github, { release: res.release });
|
|
763
727
|
return res;
|
|
764
728
|
};
|
|
765
|
-
|
|
766
729
|
//#endregion
|
|
767
730
|
//#region src/github/release.ts
|
|
768
731
|
const truncateBody = (body) => {
|
|
@@ -779,7 +742,7 @@ const getReleaseByTagSafe = async (octokit, options) => {
|
|
|
779
742
|
}
|
|
780
743
|
};
|
|
781
744
|
const createCRelease = async (octokit, options) => {
|
|
782
|
-
const { owner, repo, tag, name, body, draft, prerelease
|
|
745
|
+
const { owner, repo, tag, name, body, draft, prerelease, generateReleaseNotes, makeLatest, targetCommitish, discussionCategoryName } = options;
|
|
783
746
|
const { data } = await octokit.repos.createRelease({
|
|
784
747
|
owner,
|
|
785
748
|
repo,
|
|
@@ -787,7 +750,7 @@ const createCRelease = async (octokit, options) => {
|
|
|
787
750
|
name,
|
|
788
751
|
body,
|
|
789
752
|
draft,
|
|
790
|
-
prerelease
|
|
753
|
+
prerelease,
|
|
791
754
|
generate_release_notes: generateReleaseNotes,
|
|
792
755
|
make_latest: makeLatest,
|
|
793
756
|
target_commitish: targetCommitish,
|
|
@@ -796,7 +759,7 @@ const createCRelease = async (octokit, options) => {
|
|
|
796
759
|
return data;
|
|
797
760
|
};
|
|
798
761
|
const updateCRelease = async (octokit, options) => {
|
|
799
|
-
const { owner, repo, tag, name, body, draft, prerelease
|
|
762
|
+
const { owner, repo, tag, name, body, draft, prerelease, generateReleaseNotes, makeLatest, targetCommitish, discussionCategoryName, releaseId } = options;
|
|
800
763
|
const { data } = await octokit.repos.updateRelease({
|
|
801
764
|
owner,
|
|
802
765
|
repo,
|
|
@@ -805,7 +768,7 @@ const updateCRelease = async (octokit, options) => {
|
|
|
805
768
|
name,
|
|
806
769
|
body,
|
|
807
770
|
draft,
|
|
808
|
-
prerelease
|
|
771
|
+
prerelease,
|
|
809
772
|
generate_release_notes: generateReleaseNotes,
|
|
810
773
|
make_latest: makeLatest,
|
|
811
774
|
target_commitish: targetCommitish,
|
|
@@ -842,13 +805,13 @@ const uploadAssets = async (octokit, options) => {
|
|
|
842
805
|
}));
|
|
843
806
|
};
|
|
844
807
|
const createWebRelease = async (options) => {
|
|
845
|
-
const { owner, repo, tag, title, body, prerelease
|
|
808
|
+
const { owner, repo, tag, title, body, prerelease } = options;
|
|
846
809
|
const url = new URL(`${getGithubUrl(owner, repo)}/releases/new`);
|
|
847
810
|
Object.entries({
|
|
848
811
|
tag,
|
|
849
812
|
title,
|
|
850
813
|
body,
|
|
851
|
-
prerelease
|
|
814
|
+
prerelease
|
|
852
815
|
}).forEach(([key, value]) => {
|
|
853
816
|
url.searchParams.set(key, value);
|
|
854
817
|
});
|
|
@@ -891,7 +854,7 @@ const githubCheck = async (ctx, config) => {
|
|
|
891
854
|
const createRelease = async (ctx, config) => {
|
|
892
855
|
const { pkg: { name, toPreRelease }, git: { currentTag }, isCI, dryRun } = ctx;
|
|
893
856
|
const { github: { isWeb, token, owner, repo, changelog, releaseName } } = ctx;
|
|
894
|
-
const { github: { release, assets, prerelease
|
|
857
|
+
const { github: { release, assets, prerelease, autoGenerate, draft } } = config;
|
|
895
858
|
if (!release || dryRun) return;
|
|
896
859
|
const octokit = new Octokit({ auth: token });
|
|
897
860
|
if (isWeb) await createWebRelease({
|
|
@@ -900,7 +863,7 @@ const createRelease = async (ctx, config) => {
|
|
|
900
863
|
tag: currentTag,
|
|
901
864
|
title: releaseName,
|
|
902
865
|
body: truncateBody(changelog),
|
|
903
|
-
prerelease: String(toPreRelease || prerelease
|
|
866
|
+
prerelease: String(toPreRelease || prerelease)
|
|
904
867
|
});
|
|
905
868
|
else if (isCI) {
|
|
906
869
|
const res = await createCReleaseByTag(octokit, {
|
|
@@ -910,7 +873,7 @@ const createRelease = async (ctx, config) => {
|
|
|
910
873
|
name: releaseName,
|
|
911
874
|
body: autoGenerate ? "" : truncateBody(changelog),
|
|
912
875
|
draft,
|
|
913
|
-
prerelease: toPreRelease || prerelease
|
|
876
|
+
prerelease: toPreRelease || prerelease,
|
|
914
877
|
generateReleaseNotes: autoGenerate,
|
|
915
878
|
makeLatest: "true"
|
|
916
879
|
});
|
|
@@ -928,7 +891,6 @@ const createRelease = async (ctx, config) => {
|
|
|
928
891
|
}
|
|
929
892
|
}
|
|
930
893
|
};
|
|
931
|
-
|
|
932
894
|
//#endregion
|
|
933
895
|
//#region src/npm/prompts.ts
|
|
934
896
|
const runNpmPublishPrompts = async (ctx, config) => {
|
|
@@ -959,17 +921,8 @@ const runNpmOptPrompts = async (ctx, config) => {
|
|
|
959
921
|
Object.assign(ctx.npm, { otp: res.otp });
|
|
960
922
|
return res;
|
|
961
923
|
};
|
|
962
|
-
|
|
963
924
|
//#endregion
|
|
964
925
|
//#region src/npm/publish.ts
|
|
965
|
-
const resolvePublishTag = async (pkgName, version) => {
|
|
966
|
-
const { toPreRelease, preId } = parseVersion(version);
|
|
967
|
-
if (toPreRelease) return preId || "next";
|
|
968
|
-
const active = await getPublishedVersion(pkgName);
|
|
969
|
-
if (!active) return "latest";
|
|
970
|
-
return lt(version, active) ? "previous" : "latest";
|
|
971
|
-
};
|
|
972
|
-
const isOtpError = (err) => err instanceof Error && /one-time password|otp/i.test(err.message);
|
|
973
926
|
const npmCheck = async (ctx, config) => {
|
|
974
927
|
const { pkg: { name, current, publishConfig: { registry } } } = ctx;
|
|
975
928
|
const { npm: { skipChecks } } = config;
|
|
@@ -1002,7 +955,6 @@ const publishNpm = async (ctx, config) => {
|
|
|
1002
955
|
cwd: getPkgDir(selectedPkg)
|
|
1003
956
|
});
|
|
1004
957
|
};
|
|
1005
|
-
|
|
1006
958
|
//#endregion
|
|
1007
959
|
//#region src/action.ts
|
|
1008
960
|
var Action = class {
|
|
@@ -1018,7 +970,7 @@ var Action = class {
|
|
|
1018
970
|
taskEnd(MSG.OUTRO(ctx.dryRun));
|
|
1019
971
|
}
|
|
1020
972
|
async handlePrepareRelease(cmdArgs, options) {
|
|
1021
|
-
const { ctx, config } = await this.createContext(cmdArgs, options);
|
|
973
|
+
const { ctx, config } = await this.createContext(cmdArgs, options, true);
|
|
1022
974
|
await this.checkTask(ctx, config);
|
|
1023
975
|
await this.bumpTask(ctx, config);
|
|
1024
976
|
await this.changelogTask(ctx, config);
|
|
@@ -1026,7 +978,7 @@ var Action = class {
|
|
|
1026
978
|
if (ctx.dryRun) gitRollback(ctx);
|
|
1027
979
|
taskEnd(MSG.OUTRO_PREPARE(ctx.dryRun));
|
|
1028
980
|
}
|
|
1029
|
-
async createContext(cmdArgs, options) {
|
|
981
|
+
async createContext(cmdArgs, options, prepare = false) {
|
|
1030
982
|
const { version: cVersion, name: cName } = readJsonFile(join(__dirname, "../package.json"));
|
|
1031
983
|
const { otp, package: defPkg, ...others } = options;
|
|
1032
984
|
const { showChangelog, showRelease, ci, dryRun } = Object.fromEntries(Object.entries(others).map(([k, v]) => [k, Boolean(v)]));
|
|
@@ -1036,7 +988,7 @@ var Action = class {
|
|
|
1036
988
|
info(MSG.INFO.TOOL(cName, cVersion));
|
|
1037
989
|
info(MSG.INFO.CONFIG(configPath));
|
|
1038
990
|
console.log();
|
|
1039
|
-
intro(MSG.INTRO(dryRun));
|
|
991
|
+
intro(prepare ? MSG.INTRO_PREPARE(dryRun) : MSG.INTRO(dryRun));
|
|
1040
992
|
const { isMonorepo, packages, getPkgDir } = config;
|
|
1041
993
|
if (isMonorepo) {
|
|
1042
994
|
if (ci) {
|
|
@@ -1044,19 +996,19 @@ var Action = class {
|
|
|
1044
996
|
} else if (packages.length < 1) abortTask(MSG.ABORT.MONOREPO_NO_PACKAGES);
|
|
1045
997
|
}
|
|
1046
998
|
const selectPackage = async () => {
|
|
1047
|
-
const pkg
|
|
999
|
+
const pkg = await select({
|
|
1048
1000
|
message: question(MSG.PROMPT.SELECT_PACKAGE, "package"),
|
|
1049
|
-
options: packages.map((pkg
|
|
1050
|
-
label: pkg
|
|
1051
|
-
value: pkg
|
|
1001
|
+
options: packages.map((pkg) => ({
|
|
1002
|
+
label: pkg,
|
|
1003
|
+
value: pkg
|
|
1052
1004
|
}))
|
|
1053
1005
|
});
|
|
1054
|
-
abortSinglePrompt(pkg
|
|
1055
|
-
return pkg
|
|
1006
|
+
abortSinglePrompt(pkg);
|
|
1007
|
+
return pkg;
|
|
1056
1008
|
};
|
|
1057
1009
|
const selectedPkg = !isMonorepo ? "" : ci ? defPkg : packages.length === 1 ? packages[0] : await selectPackage();
|
|
1058
|
-
const { pkg
|
|
1059
|
-
const { version: pkgVersion, private: pkgPrivate = false, name: pkgName, publishConfig = {} } = pkg
|
|
1010
|
+
const { pkg, pkgDir } = getPackageInfo(selectedPkg, getPkgDir);
|
|
1011
|
+
const { version: pkgVersion, private: pkgPrivate = false, name: pkgName, publishConfig = {} } = pkg;
|
|
1060
1012
|
const nextVersion = inc(pkgVersion, cmdArgs) ?? "";
|
|
1061
1013
|
const ctx = mergeConfig(createDefaultContext(), {
|
|
1062
1014
|
selectedPkg,
|
|
@@ -1077,7 +1029,7 @@ var Action = class {
|
|
|
1077
1029
|
pkg: {
|
|
1078
1030
|
name: pkgName,
|
|
1079
1031
|
isPrivate: pkgPrivate,
|
|
1080
|
-
fromPreRelease:
|
|
1032
|
+
fromPreRelease: isPrerelease(pkgVersion),
|
|
1081
1033
|
current: pkgVersion,
|
|
1082
1034
|
next: nextVersion,
|
|
1083
1035
|
toPreRelease: false,
|
|
@@ -1087,7 +1039,7 @@ var Action = class {
|
|
|
1087
1039
|
});
|
|
1088
1040
|
const { messages } = await publint({ pkgDir });
|
|
1089
1041
|
for (const message of messages) {
|
|
1090
|
-
const formated = formatMessage(message, pkg
|
|
1042
|
+
const formated = formatMessage(message, pkg);
|
|
1091
1043
|
formated && msg("PKG", formated);
|
|
1092
1044
|
}
|
|
1093
1045
|
return {
|
|
@@ -1109,19 +1061,19 @@ var Action = class {
|
|
|
1109
1061
|
{
|
|
1110
1062
|
title: MSG.CHECK.NPM.CHECKING,
|
|
1111
1063
|
task: async () => {
|
|
1112
|
-
const msg
|
|
1064
|
+
const msg = await npmCheck(ctx, config);
|
|
1113
1065
|
const { pkg: { publishConfig: { registry } } } = ctx;
|
|
1114
|
-
return success(MSG.CHECK.NPM.CHECKED(registry, msg
|
|
1066
|
+
return success(MSG.CHECK.NPM.CHECKED(registry, msg), dryRun);
|
|
1115
1067
|
},
|
|
1116
1068
|
enabled: !ctx.noNpm
|
|
1117
1069
|
},
|
|
1118
1070
|
{
|
|
1119
1071
|
title: MSG.CHECK.GITHUB.CHECKING,
|
|
1120
1072
|
task: async () => {
|
|
1121
|
-
const msg
|
|
1073
|
+
const msg = await githubCheck(ctx, config);
|
|
1122
1074
|
const { github: { owner, repo } } = ctx;
|
|
1123
1075
|
const repository = getGithubUrl(owner, repo);
|
|
1124
|
-
return success(MSG.CHECK.GITHUB.CHECKED(repository, msg
|
|
1076
|
+
return success(MSG.CHECK.GITHUB.CHECKED(repository, msg), dryRun);
|
|
1125
1077
|
},
|
|
1126
1078
|
enabled: !ctx.noGitHub
|
|
1127
1079
|
}
|
|
@@ -1130,15 +1082,20 @@ var Action = class {
|
|
|
1130
1082
|
async bumpTask(ctx, config) {
|
|
1131
1083
|
const { pkg: { current }, dryRun, isCI, showRelease, showChangelog, selectedPkg } = ctx;
|
|
1132
1084
|
const { isMonorepo, hooks, getPkgDir, tagPrefix } = config;
|
|
1133
|
-
const need = (ctx
|
|
1134
|
-
const { pkg: { next
|
|
1135
|
-
return !next
|
|
1085
|
+
const need = (ctx) => {
|
|
1086
|
+
const { pkg: { next }, isIncrement } = ctx;
|
|
1087
|
+
return !next && isIncrement;
|
|
1136
1088
|
};
|
|
1137
1089
|
if (need(ctx)) {
|
|
1138
|
-
const
|
|
1139
|
-
ctx.isIncrement =
|
|
1140
|
-
const
|
|
1141
|
-
Object.assign(ctx.pkg, {
|
|
1090
|
+
const nextVersion = (isCI ? inc(current, "patch") : void 0) || await runVersionPrompts(ctx, config);
|
|
1091
|
+
ctx.isIncrement = neq(current, nextVersion);
|
|
1092
|
+
const { version: next, isPrerelease: toPreRelease, preId, preBase } = parseVersion(nextVersion);
|
|
1093
|
+
Object.assign(ctx.pkg, {
|
|
1094
|
+
next,
|
|
1095
|
+
toPreRelease,
|
|
1096
|
+
preId,
|
|
1097
|
+
preBase
|
|
1098
|
+
});
|
|
1142
1099
|
}
|
|
1143
1100
|
const { pkg: { next }, isIncrement } = ctx;
|
|
1144
1101
|
if (showRelease) taskEnd(MSG.LOG.SHOW_VERSION(next));
|
|
@@ -1148,8 +1105,8 @@ var Action = class {
|
|
|
1148
1105
|
task: async () => {
|
|
1149
1106
|
await bumpPackageVersion(next, [], getPkgDir(selectedPkg));
|
|
1150
1107
|
await formatTemplate(ctx, config);
|
|
1151
|
-
const to
|
|
1152
|
-
return success(MSG.TASK.VERSION.END(to
|
|
1108
|
+
const to = `(${current}...${diff(current, next)})`;
|
|
1109
|
+
return success(MSG.TASK.VERSION.END(to), dryRun);
|
|
1153
1110
|
}
|
|
1154
1111
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1155
1112
|
await runLifeCycleHook(hooks, "after:bump", dryRun);
|
|
@@ -1239,18 +1196,13 @@ var Action = class {
|
|
|
1239
1196
|
await runLifeCycleHook(hooks, "after:release", dryRun);
|
|
1240
1197
|
}
|
|
1241
1198
|
};
|
|
1242
|
-
|
|
1243
1199
|
//#endregion
|
|
1244
1200
|
//#region src/cli.ts
|
|
1245
1201
|
const pkg = readJsonFile(join(__dirname, "..", "package.json"));
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
await new Action().handleRelease(releaseType, options);
|
|
1251
|
-
});
|
|
1252
|
-
withCommonOptions(program.command("prepare").description("Prepare a release (bump version, commit, tag)")).action(async (releaseType, options) => {
|
|
1253
|
-
await new Action().handlePrepareRelease(releaseType, options);
|
|
1202
|
+
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", false).option("-p, --package <pkg>", "Specify package name (CI only).", "").option("--otp <code>", "One-time password for npm publish.", "").option("--prepare", "Prepare a release.", false).option("--ci", "Enable CI mode.", false).option("--show-changelog", "Print changelog and exit.", false).option("--show-release", "Print release version and exit.", false).helpOption("-h, --help", "Display help information.").action(async (releaseType, options) => {
|
|
1203
|
+
const { prepare } = options;
|
|
1204
|
+
const action = new Action();
|
|
1205
|
+
prepare ? await action.handlePrepareRelease(releaseType, options) : await action.handleRelease(releaseType, options);
|
|
1254
1206
|
});
|
|
1255
1207
|
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) => {
|
|
1256
1208
|
const { force = false, monorepo = false } = options;
|
|
@@ -1258,9 +1210,10 @@ program.command("init").description("Create a release configuration file").optio
|
|
|
1258
1210
|
const { type } = readJsonFile(join(cwd, "package.json"));
|
|
1259
1211
|
const isESM = type === "module";
|
|
1260
1212
|
const isTS = existsSync(join(cwd, "tsconfig.json"));
|
|
1261
|
-
const
|
|
1213
|
+
const infile = `release.config.${isESM ? "" : "m"}${isTS ? "ts" : "js"}`;
|
|
1214
|
+
const configPath = join(cwd, infile);
|
|
1262
1215
|
if (existsSync(configPath) && !force) {
|
|
1263
|
-
console.error(`${red`Error:`} ${yellow
|
|
1216
|
+
console.error(`${red`Error:`} ${yellow(infile)} already exists.\nUse ${green`--force`} to overwrite.`);
|
|
1264
1217
|
process.exit(1);
|
|
1265
1218
|
}
|
|
1266
1219
|
const monoConfigToString = () => `{
|
|
@@ -1270,10 +1223,8 @@ program.command("init").description("Create a release configuration file").optio
|
|
|
1270
1223
|
toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
|
|
1271
1224
|
tagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1272
1225
|
}`;
|
|
1273
|
-
writeFileSync(configPath, `import { defineConfig } from '@peiyanlu/release'
|
|
1274
|
-
|
|
1275
|
-
export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`, "utf-8");
|
|
1276
|
-
console.log(`${green`✔ release.config.ts`} created successfully`);
|
|
1226
|
+
writeFileSync(configPath, [`import { defineConfig } from '@peiyanlu/release'`, `export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`].join(eol(2)), "utf-8");
|
|
1227
|
+
console.log(`${green`✔`} ${infile} created successfully`);
|
|
1277
1228
|
});
|
|
1278
1229
|
program.parse(process.argv);
|
|
1279
1230
|
program.on("command:*", () => {
|
|
@@ -1281,6 +1232,5 @@ program.on("command:*", () => {
|
|
|
1281
1232
|
console.log(`See ${red`--help`} for a list of available commands.\n`);
|
|
1282
1233
|
process.exit(1);
|
|
1283
1234
|
});
|
|
1284
|
-
|
|
1285
1235
|
//#endregion
|
|
1286
|
-
export {
|
|
1236
|
+
export {};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { log } from "@clack/prompts";
|
|
2
1
|
import { DeepPartial, DeepRequired } from "@peiyanlu/ts-utils";
|
|
3
2
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
3
|
+
import { log } from "@clack/prompts";
|
|
4
4
|
|
|
5
5
|
//#region src/types.d.ts
|
|
6
|
-
|
|
7
6
|
type Logger = typeof log;
|
|
8
7
|
type HookTiming = 'before' | 'after';
|
|
9
8
|
type HookAction = 'bump' | 'publish' | 'push' | 'release';
|
|
@@ -172,7 +171,7 @@ declare const generateChangelog: ({
|
|
|
172
171
|
tagPrefix
|
|
173
172
|
}: Options) => Promise<void>;
|
|
174
173
|
//#endregion
|
|
175
|
-
//#region src/
|
|
174
|
+
//#region src/npm/publish.d.ts
|
|
176
175
|
interface PublishTagOptions {
|
|
177
176
|
/**
|
|
178
177
|
* 支持通过命令行参数提供
|
package/dist/index.mjs
CHANGED
|
@@ -1,133 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { coerce, lt, parse, valid } from "semver";
|
|
3
|
-
import { dim, green, underline, yellow } from "ansis";
|
|
4
|
-
import { log } from "@clack/prompts";
|
|
5
|
-
import { isPlainObject } from "@peiyanlu/ts-utils";
|
|
1
|
+
import { isFunction, isPlainObject } from "@peiyanlu/ts-utils";
|
|
6
2
|
import { createWriteStream, existsSync, readFileSync } from "node:fs";
|
|
7
3
|
import { join } from "node:path";
|
|
4
|
+
import { eol, getPackageInfo, publishPackage, resolvePublishTag } from "@peiyanlu/cli-utils";
|
|
8
5
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
9
6
|
import createPreset from "conventional-changelog-conventionalcommits";
|
|
10
7
|
import { writeFile } from "node:fs/promises";
|
|
11
8
|
import { finished } from "node:stream/promises";
|
|
12
|
-
|
|
13
|
-
//#region src/messages.ts
|
|
14
|
-
const n = (b) => b ? "(dry run)" : "";
|
|
15
|
-
const t = () => green(`${Math.floor(process.uptime())}s`);
|
|
16
|
-
const MSG = {
|
|
17
|
-
INTRO: (dryRun) => `🚀 Starting release ${n(dryRun)}`,
|
|
18
|
-
OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()} ${n(dryRun)}`,
|
|
19
|
-
OUTRO_PREPARE: (dryRun) => `🎉 PrepareRelease finished successfully in ${t()} ${n(dryRun)}`,
|
|
20
|
-
LOG: {
|
|
21
|
-
SHOW_VERSION: (version) => `🎉 Released ${green(version)}`,
|
|
22
|
-
SHOW_CHANGELOG: "🎉 Changelog collected",
|
|
23
|
-
CHANGELOG_EMPTY: "No commits found since last release",
|
|
24
|
-
CHANGES_EMPTY: "Working tree clean. No files changed"
|
|
25
|
-
},
|
|
26
|
-
CHECK: {
|
|
27
|
-
GIT: {
|
|
28
|
-
CHECKING: "Checking Git repository",
|
|
29
|
-
CHECKED: (name, url) => `Git remote resolved: ${url ? `${dim(name)} → ${underline(dim(url))}` : "not found"}`
|
|
30
|
-
},
|
|
31
|
-
NPM: {
|
|
32
|
-
CHECKING: "Checking npm registry",
|
|
33
|
-
CHECKED: (registry, msg) => `NPM registry: ${underline(dim(registry))} ${underline(dim(msg))}`
|
|
34
|
-
},
|
|
35
|
-
GITHUB: {
|
|
36
|
-
CHECKING: "Checking github repository",
|
|
37
|
-
CHECKED: (repository, msg) => `GitHub repository: ${underline(dim(repository))} ${underline(dim(msg))}`
|
|
38
|
-
}
|
|
39
|
-
},
|
|
40
|
-
TASK: {
|
|
41
|
-
VERSION: {
|
|
42
|
-
START: "Bumping version",
|
|
43
|
-
END: (to) => `Version bumped: ${underline(dim(to))}`
|
|
44
|
-
},
|
|
45
|
-
CHANGELOG: {
|
|
46
|
-
START: "Generating changelog",
|
|
47
|
-
END: `Changelog generated`
|
|
48
|
-
},
|
|
49
|
-
NPM: {
|
|
50
|
-
START: "Publishing npm package",
|
|
51
|
-
END: (url) => `NPM package published: ${underline(dim(url))}`
|
|
52
|
-
},
|
|
53
|
-
GIT: {
|
|
54
|
-
START: "Creating git commit, tag & push",
|
|
55
|
-
END: "Git commit, tag & push completed"
|
|
56
|
-
},
|
|
57
|
-
GITHUB: {
|
|
58
|
-
START: "Creating GitHub release",
|
|
59
|
-
END: (url) => `GitHub release created: ${underline(dim(url))}`
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
PROMPT: {
|
|
63
|
-
SELECT_PACKAGE: "Select package release:",
|
|
64
|
-
SELECT_VERSION: "Select version bump:",
|
|
65
|
-
INPUT_VERSION: "Enter custom version:",
|
|
66
|
-
NPM_PUBLISH: (pkg) => `Publish to npm ${underline(dim`(${pkg})`)}?`,
|
|
67
|
-
NPM_OTP: "Enter npm one-time password (OTP):",
|
|
68
|
-
GITHUB_RELEASE: (title) => `Create a new GitHub release ${underline(dim`(${title})`)}?`,
|
|
69
|
-
GIT_COMMIT: (msg) => `Create git commit ${underline(dim`(${msg})`)}?`,
|
|
70
|
-
GIT_TAG: (msg) => `Create git tag ${underline(dim`(${msg})`)}?`,
|
|
71
|
-
GIT_PUSH: "Push commits and tags to remote?"
|
|
72
|
-
},
|
|
73
|
-
ERROR: {
|
|
74
|
-
GIT_WORKDIR: `[git] Working dir must be clean`,
|
|
75
|
-
GIT_REGISTRY: (name) => `[git] Repository not found for ${dim(name)}`,
|
|
76
|
-
GIT_REMOTE: (name) => `[git] Remote "${dim(name)}" not found`,
|
|
77
|
-
GITHUB_TAG_EXIT: (tag) => `[github] Release or tag "${tag}" already exists`,
|
|
78
|
-
GITHUB_TOKEN: (tokenRef) => `[github] Missing GitHub token. Please set ${yellow(tokenRef ?? "GITHUB_TOKEN")}`,
|
|
79
|
-
GITHUB_AUTH: "[github] Invalid GitHub token or insufficient permissions",
|
|
80
|
-
GITHUB_USER: "[github] User does not have permission to create releases",
|
|
81
|
-
NPM_REGISTRY: (registry) => `[npm] Unable to reach npm registry ${underline(dim(registry))}`,
|
|
82
|
-
NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again.`,
|
|
83
|
-
NPM_USER: (user, name) => `[npm] User ${dim(user)} is not a collaborator of ${dim(name)}.`
|
|
84
|
-
},
|
|
85
|
-
ABORT: {
|
|
86
|
-
CANCEL: "Operation cancelled",
|
|
87
|
-
MONOREPO_CI_NO_PACKAGE: "CI mode requires a target package in monorepo. Please specify it via \"--package <pkg>\"",
|
|
88
|
-
MONOREPO_NO_PACKAGES: "Monorepo detected, but no packages found. Please configure \"config.packages\""
|
|
89
|
-
},
|
|
90
|
-
INFO: {
|
|
91
|
-
TOOL: (name, version) => `${name} ${dim(`v${version}`)}`,
|
|
92
|
-
CONFIG: (path) => `config file: ${underline(dim(path || "not found"))}`
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
//#endregion
|
|
97
|
-
//#region src/version/bump.ts
|
|
98
|
-
const isValid = (version) => {
|
|
99
|
-
return Boolean(valid(version));
|
|
100
|
-
};
|
|
101
|
-
const parseVersion = (raw) => {
|
|
102
|
-
const next = isValid(raw) ? raw : coerce(raw)?.toString();
|
|
103
|
-
if (!next) return {};
|
|
104
|
-
const { prerelease } = parse(next);
|
|
105
|
-
const toPreRelease = Boolean(prerelease.length);
|
|
106
|
-
const [preId, preBase] = prerelease.map(String);
|
|
107
|
-
return {
|
|
108
|
-
next,
|
|
109
|
-
toPreRelease,
|
|
110
|
-
preId,
|
|
111
|
-
preBase
|
|
112
|
-
};
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
//#endregion
|
|
116
|
-
//#region src/npm/publish.ts
|
|
117
|
-
const resolvePublishTag = async (pkgName, version) => {
|
|
118
|
-
const { toPreRelease, preId } = parseVersion(version);
|
|
119
|
-
if (toPreRelease) return preId || "next";
|
|
120
|
-
const active = await getPublishedVersion(pkgName);
|
|
121
|
-
if (!active) return "latest";
|
|
122
|
-
return lt(version, active) ? "previous" : "latest";
|
|
123
|
-
};
|
|
124
|
-
|
|
125
|
-
//#endregion
|
|
9
|
+
import { blue, cyan, green, magenta, red, underline, yellow } from "ansis";
|
|
126
10
|
//#region src/config.ts
|
|
127
11
|
function defineConfig(config) {
|
|
128
12
|
return config;
|
|
129
13
|
}
|
|
130
|
-
const isFunction = (v) => typeof v === "function";
|
|
131
14
|
const mergeConfig = (defaults, overrides) => {
|
|
132
15
|
if (!overrides) return structuredClone(defaults);
|
|
133
16
|
if (Object.keys(defaults).length === 0) return structuredClone(overrides);
|
|
@@ -143,7 +26,6 @@ const mergeConfig = (defaults, overrides) => {
|
|
|
143
26
|
}
|
|
144
27
|
return result;
|
|
145
28
|
};
|
|
146
|
-
|
|
147
29
|
//#endregion
|
|
148
30
|
//#region src/git/changetype.ts
|
|
149
31
|
const defaultTypes = [
|
|
@@ -238,13 +120,11 @@ const defaultTypes = [
|
|
|
238
120
|
description: "紧急修复"
|
|
239
121
|
}
|
|
240
122
|
];
|
|
241
|
-
|
|
242
123
|
//#endregion
|
|
243
124
|
//#region src/git/changelog.ts
|
|
244
|
-
const
|
|
245
|
-
const
|
|
246
|
-
|
|
247
|
-
...t$1,
|
|
125
|
+
const parsePreset = async () => {
|
|
126
|
+
const preset = await createPreset({ types: defaultTypes.map((t) => ({
|
|
127
|
+
...t,
|
|
248
128
|
hidden: false
|
|
249
129
|
})) });
|
|
250
130
|
preset.writer ??= {};
|
|
@@ -295,6 +175,11 @@ const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
|
295
175
|
{{> commit root=@root}}
|
|
296
176
|
{{/each}}
|
|
297
177
|
{{/each}}`.trim() + eol(2);
|
|
178
|
+
return preset;
|
|
179
|
+
};
|
|
180
|
+
const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
181
|
+
const pkgDir = getPkgDir();
|
|
182
|
+
const preset = await parsePreset();
|
|
298
183
|
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount: 1 }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
299
184
|
};
|
|
300
185
|
const getChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
@@ -320,43 +205,42 @@ const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
|
320
205
|
writeStream.end();
|
|
321
206
|
await finished(writeStream);
|
|
322
207
|
};
|
|
323
|
-
|
|
208
|
+
`${yellow("npm login")}`;
|
|
324
209
|
//#endregion
|
|
325
|
-
//#region src/
|
|
210
|
+
//#region src/npm/publish.ts
|
|
326
211
|
const publishTagToNpm = async (options) => {
|
|
327
212
|
const { gitTag, defaultPackage, tagSeparator = "@", getPkgDir = () => ".", provenance } = options;
|
|
328
213
|
const tag = process.argv.slice(2)[0] || gitTag;
|
|
329
214
|
if (!tag) throw new Error(`❌ No git tag specified.`);
|
|
330
|
-
console.log(`\n📦 Start publishing from git tag: ${tag}`);
|
|
215
|
+
console.log(`\n📦 Start publishing from git tag: ${cyan(tag)}`);
|
|
331
216
|
let pkgName = defaultPackage;
|
|
332
217
|
let version;
|
|
333
218
|
if (tag.includes(tagSeparator)) {
|
|
334
219
|
[pkgName, version] = tag.split(tagSeparator);
|
|
335
|
-
console.log(`🔍 Parsed tag → package: "${pkgName}", version: "${version}"`);
|
|
220
|
+
console.log(`🔍 Parsed tag → package: "${yellow(pkgName)}", version: "${green(version)}"`);
|
|
336
221
|
} else {
|
|
337
222
|
version = tag;
|
|
338
|
-
console.log(`🔍 Parsed tag → version only: "${version}"`);
|
|
223
|
+
console.log(`🔍 Parsed tag → version only: "${green(version)}"`);
|
|
339
224
|
}
|
|
340
225
|
if (version.startsWith("v")) {
|
|
341
226
|
version = version.slice(1);
|
|
342
227
|
console.log(`✂️ Normalized version (strip v): ${version}`);
|
|
343
228
|
}
|
|
344
229
|
if (!pkgName) throw new Error(`❌ Package name should be specified in tag "${tag}" when defaultPackage is not set`);
|
|
345
|
-
console.log(`📁 Resolving package info for "${pkgName}"...`);
|
|
230
|
+
console.log(`📁 Resolving package info for "${magenta(pkgName)}"...`);
|
|
346
231
|
const { pkg, pkgDir } = getPackageInfo(pkgName, getPkgDir);
|
|
347
|
-
console.log(`📄 package.json → name: ${pkg.name}, version: ${pkg.version}`);
|
|
348
|
-
console.log(`📂 Package directory: ${pkgDir}`);
|
|
232
|
+
console.log(`📄 package.json → name: ${yellow(pkg.name)}, version: ${green(pkg.version)}`);
|
|
233
|
+
console.log(`📂 Package directory: ${underline(pkgDir)}`);
|
|
349
234
|
if (pkg.version !== version) throw new Error(`❌ Package version from tag "${version}" mismatches with current version "${pkg.version}"`);
|
|
350
235
|
const publishTag = await resolvePublishTag(pkg.name, version);
|
|
351
|
-
console.log(`🏷 Resolved npm dist-tag: "${publishTag}" (version ${version})`);
|
|
352
|
-
console.log(`🚀 Publishing "${pkg.name}@${version}" to npm...`);
|
|
236
|
+
console.log(`🏷 Resolved npm dist-tag: "${blue(publishTag)}" (version ${green(version)})`);
|
|
237
|
+
console.log(`🚀 Publishing "${red`${pkg.name}@${version}`}" to npm...`);
|
|
353
238
|
await publishPackage({
|
|
354
239
|
tag: publishTag,
|
|
355
240
|
args: provenance ? ["--provenance"] : [],
|
|
356
241
|
cwd: pkgDir
|
|
357
242
|
});
|
|
358
|
-
console.log(
|
|
243
|
+
console.log(`🎉 Successfully published ${red`${pkg.name}@${version}`} with dist-tag "${blue(publishTag)}"\n`);
|
|
359
244
|
};
|
|
360
|
-
|
|
361
245
|
//#endregion
|
|
362
|
-
export { createGenerator, defineConfig, generateChangelog, getChangelog, mergeConfig, publishTagToNpm };
|
|
246
|
+
export { createGenerator, defineConfig, generateChangelog, getChangelog, mergeConfig, publishTagToNpm };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peiyanlu/release",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Release project tool.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,29 +17,37 @@
|
|
|
17
17
|
},
|
|
18
18
|
"./package.json": "./package.json"
|
|
19
19
|
},
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "tsdown -w",
|
|
22
|
+
"build": "tsdown",
|
|
23
|
+
"prepublish": "tsdown",
|
|
24
|
+
"release": "node index.js",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:cov": "vitest run --coverage"
|
|
27
|
+
},
|
|
20
28
|
"dependencies": {
|
|
21
|
-
"@clack/prompts": "^
|
|
29
|
+
"@clack/prompts": "^1.2.0",
|
|
22
30
|
"@octokit/rest": "^22.0.1",
|
|
23
|
-
"@peiyanlu/cli-utils": "^0.0.
|
|
24
|
-
"@peiyanlu/ts-utils": "^0.0.
|
|
31
|
+
"@peiyanlu/cli-utils": "^0.0.8",
|
|
32
|
+
"@peiyanlu/ts-utils": "^0.0.5",
|
|
25
33
|
"ansis": "^4.2.0",
|
|
26
|
-
"commander": "^14.0.
|
|
27
|
-
"conventional-changelog": "^7.
|
|
28
|
-
"conventional-changelog-conventionalcommits": "^9.1
|
|
34
|
+
"commander": "^14.0.3",
|
|
35
|
+
"conventional-changelog": "^7.2.0",
|
|
36
|
+
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
29
37
|
"mime-types": "^3.0.2",
|
|
30
38
|
"open": "^11.0.0",
|
|
31
|
-
"publint": "^0.3.
|
|
32
|
-
"semver": "^7.7.
|
|
39
|
+
"publint": "^0.3.18",
|
|
40
|
+
"semver": "^7.7.4",
|
|
33
41
|
"tinyglobby": "^0.2.15"
|
|
34
42
|
},
|
|
35
43
|
"devDependencies": {
|
|
36
44
|
"@types/mime-types": "^3.0.1",
|
|
37
|
-
"@types/node": "^
|
|
45
|
+
"@types/node": "^25.5.2",
|
|
38
46
|
"@types/semver": "^7.7.1",
|
|
39
|
-
"@vitest/coverage-v8": "^4.
|
|
40
|
-
"tsdown": "^0.
|
|
41
|
-
"typescript": "^
|
|
42
|
-
"vitest": "^4.
|
|
47
|
+
"@vitest/coverage-v8": "^4.1.3",
|
|
48
|
+
"tsdown": "^0.21.7",
|
|
49
|
+
"typescript": "^6.0.2",
|
|
50
|
+
"vitest": "^4.1.3"
|
|
43
51
|
},
|
|
44
52
|
"author": {
|
|
45
53
|
"name": "YanluPei",
|
|
@@ -66,13 +74,5 @@
|
|
|
66
74
|
"bugs": {
|
|
67
75
|
"url": "https://github.com/peiyanlu/release/issues"
|
|
68
76
|
},
|
|
69
|
-
"homepage": "https://github.com/peiyanlu/release#readme"
|
|
70
|
-
|
|
71
|
-
"dev": "tsdown -w",
|
|
72
|
-
"build": "tsdown",
|
|
73
|
-
"prepublish": "tsdown",
|
|
74
|
-
"release": "node index.js",
|
|
75
|
-
"test": "vitest run",
|
|
76
|
-
"test:cov": "vitest run --coverage"
|
|
77
|
-
}
|
|
78
|
-
}
|
|
77
|
+
"homepage": "https://github.com/peiyanlu/release#readme"
|
|
78
|
+
}
|