@peiyanlu/release 0.0.16 → 1.1.0
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 +23 -8
- package/dist/cli.mjs +158 -126
- package/dist/index.d.mts +32 -23
- package/dist/index.mjs +64 -73
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -56,14 +56,21 @@ pnpm release minor --ci
|
|
|
56
56
|
|
|
57
57
|
### 通用参数
|
|
58
58
|
|
|
59
|
-
| 参数
|
|
60
|
-
|
|
61
|
-
| `-v, --version`
|
|
62
|
-
| `-h, --help`
|
|
63
|
-
| `-n, --dry-run`
|
|
64
|
-
| `--show-release`
|
|
65
|
-
| `--show-changelog`
|
|
66
|
-
| `--ci`
|
|
59
|
+
| 参数 | 说明 |
|
|
60
|
+
|---------------------------------------|------------------------------------------------------------|
|
|
61
|
+
| `-v, --version` | 输出当前 CLI 版本并退出 |
|
|
62
|
+
| `-h, --help` | 显示帮助信息 |
|
|
63
|
+
| `-n, --dry-run` | 空跑模式,展示将要执行的发布流程但不做任何修改 |
|
|
64
|
+
| `--show-release` | 打印即将发布的版本号并退出 |
|
|
65
|
+
| `--show-changelog` | 打印生成的 Changelog 并退出 |
|
|
66
|
+
| `--ci` | 启用 CI 模式:禁用交互提示,缺少必要参数时直接失败;GitHub Actions 等 CI/CD 环境中默认启用 |
|
|
67
|
+
| `-r, --release-count <count>` | 生成多少个版本的变更日志 |
|
|
68
|
+
| `--include-hidden` | 生成 CHANGELOG 时包含默认隐藏的 commit 类型 |
|
|
69
|
+
| `--only-changelog` | 仅更新 CHANGELOG |
|
|
70
|
+
| `-C, --no-require-clean-working-tree` | 是否要求工作目录保持干净(无未提交的更改) |
|
|
71
|
+
| `--skip-git` | 跳过 git、github 环境检查以及后续操作 |
|
|
72
|
+
| `--skip-npm` | 跳过 npm 环境检查以及后续操作 |
|
|
73
|
+
| `--skip-github` | 跳过 github 环境检查以及后续操作 |
|
|
67
74
|
|
|
68
75
|
### Prepare 相关参数
|
|
69
76
|
|
|
@@ -81,6 +88,7 @@ pnpm release minor --prepare --ci --package pkg-a
|
|
|
81
88
|
|
|
82
89
|
| 参数 | 说明 |
|
|
83
90
|
|-----------------------|----------------------------------|
|
|
91
|
+
| `-m, --is-monorepo` | Monorepo 项目 |
|
|
84
92
|
| `-p, --package <pkg>` | 指定要发布的子包名称(仅用于 Monorepo 的 CI 场景) |
|
|
85
93
|
|
|
86
94
|
```bash
|
|
@@ -194,6 +202,7 @@ release init
|
|
|
194
202
|
|------------------|---------------------|
|
|
195
203
|
| `-f, --force` | 覆盖已存在的配置文件 |
|
|
196
204
|
| `-m, --monorepo` | 生成 Monorepo 项目的配置模板 |
|
|
205
|
+
| `-a, --add` | 自动暂存配置文件 |
|
|
197
206
|
|
|
198
207
|
#### 行为说明
|
|
199
208
|
|
|
@@ -232,6 +241,12 @@ COMMIT_TYPES:
|
|
|
232
241
|
- 默认:`feat | feature | fix | perf | revert | docs | style | chore | refactor | test | build | ci`
|
|
233
242
|
- 自定义:`config | deps | security | i18n | ux | hotfix`
|
|
234
243
|
|
|
244
|
+
| Effect | Types | 说明 |
|
|
245
|
+
|-----------------|----------------------------------------------------------------|-----------------------------------|
|
|
246
|
+
| **`bump`** | `feat`, `feature`, `fix`, `perf`, `revert` | 影响版本号(Minor/Patch),同时写入 CHANGELOG |
|
|
247
|
+
| **`changelog`** | `docs`, `refactor`, `deps`, `security`, `hotfix`, `i18n`, `ux` | 不影响版本号,仅写入 CHANGELOG |
|
|
248
|
+
| **`hidden`** | `style`, `chore`, `test`, `build`, `ci`, `config`, `tool` | 不影响版本号,也不会写入 CHANGELOG |
|
|
249
|
+
|
|
235
250
|
支持 **--show-changelog** 预览模式:
|
|
236
251
|
|
|
237
252
|
- 仅展示用于生成 Changelog 的 Commits
|
package/dist/cli.mjs
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
import path, { join, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
|
-
import { bumpPackageVersion, canPublish, coloredStatus, eol, execAsync, getAuthenticatedUser, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLogSince, getPackageInfo, getPackageUrl, getPublishedVersion, getRemoteList, getRemoteNames, getShortStatus, gitAddAll, gitAddTracked, gitBranchCurrent, gitCommitMessage, gitPushBranch, gitPushTag, gitResetHardSync, gitTagAnnotated, gitTagDeleteSync, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValidVersion, isWorkingTreeClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, readJsonFile, resolveChangelogRange, resolvePublishTag } from "@peiyanlu/cli-utils";
|
|
3
|
+
import { bumpPackageVersion, canPublish, coloredStatus, eol, execAsync, getAuthenticatedUser, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLogSince, getPackageInfo, getPackageUrl, getPublishedVersion, getRemoteList, getRemoteNames, getShortStatus, gitAddAll, gitAddSync, gitAddTracked, gitBranchCurrent, gitCommitMessage, gitPushBranch, gitPushTag, gitResetHardSync, gitTagAnnotated, gitTagDeleteSync, hasWriteAccess, isGitRepo, isOtpError, isPrerelease, isValidVersion, isWorkingTreeClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, 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
|
-
import { castArray, isFunction, isPlainObject, isUndefined, isZero } from "@peiyanlu/ts-utils";
|
|
8
|
+
import { castArray, isFunction, isPlainObject, isUndefined, isZero, mapObjectValues } from "@peiyanlu/ts-utils";
|
|
9
9
|
import { publint } from "publint";
|
|
10
10
|
import { formatMessage } from "publint/utils";
|
|
11
11
|
import { inc, neq } from "semver";
|
|
12
12
|
import { inspect } from "node:util";
|
|
13
13
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
14
14
|
import createPreset from "conventional-changelog-conventionalcommits";
|
|
15
|
+
import { Bumper } from "conventional-recommended-bump";
|
|
15
16
|
import { writeFile } from "node:fs/promises";
|
|
16
17
|
import { finished } from "node:stream/promises";
|
|
18
|
+
import { bold, compareUrl, each, heading, link, list, newline, segments, small, words } from "@conventional-changelog/template";
|
|
17
19
|
import { Octokit } from "@octokit/rest";
|
|
18
20
|
import { contentType } from "mime-types";
|
|
19
21
|
import open from "open";
|
|
@@ -79,7 +81,8 @@ const MSG = {
|
|
|
79
81
|
SHOW_VERSION: (version) => `🎉 Released ${green(version)}`,
|
|
80
82
|
SHOW_CHANGELOG: "🎉 Changelog collected",
|
|
81
83
|
CHANGELOG_EMPTY: "No commits found since last release",
|
|
82
|
-
CHANGES_EMPTY: "Working tree clean. No files changed"
|
|
84
|
+
CHANGES_EMPTY: "Working tree clean. No files changed",
|
|
85
|
+
ONLY_CHANGELOG: "🎉 Changelog generation completed"
|
|
83
86
|
},
|
|
84
87
|
CHECK: {
|
|
85
88
|
GIT: {
|
|
@@ -129,7 +132,7 @@ const MSG = {
|
|
|
129
132
|
GIT_PUSH: "Push commits and tags to remote?"
|
|
130
133
|
},
|
|
131
134
|
ERROR: {
|
|
132
|
-
GIT_WORKDIR: `[git] Working
|
|
135
|
+
GIT_WORKDIR: `[git] Working tree must be clean`,
|
|
133
136
|
GIT_REGISTRY: (name) => `[git] Repository not found for ${dim(name)}`,
|
|
134
137
|
GIT_REMOTE: (name) => `[git] Remote "${dim(name)}" not found`,
|
|
135
138
|
GITHUB_TAG_EXIT: (tag) => `[github] Release for tag "${tag}" already exists and is not a draft`,
|
|
@@ -155,8 +158,8 @@ const MSG = {
|
|
|
155
158
|
//#region src/git/commit.ts
|
|
156
159
|
const gitCheck = async (ctx, config) => {
|
|
157
160
|
const { pkg: { name } } = ctx;
|
|
158
|
-
const { git: { requireRepository, requireRemote,
|
|
159
|
-
if (
|
|
161
|
+
const { git: { requireRepository, requireRemote, requireCleanWorkingTree }, skipGit, skipGithub } = config;
|
|
162
|
+
if (skipGit) {
|
|
160
163
|
ctx.noGit = true;
|
|
161
164
|
ctx.noGitHub = true;
|
|
162
165
|
return;
|
|
@@ -167,8 +170,8 @@ const gitCheck = async (ctx, config) => {
|
|
|
167
170
|
ctx.noGitHub = true;
|
|
168
171
|
return;
|
|
169
172
|
}
|
|
170
|
-
if (
|
|
171
|
-
if (
|
|
173
|
+
if (requireCleanWorkingTree && !await isWorkingTreeClean()) throw new Error(MSG.ERROR.GIT_WORKDIR);
|
|
174
|
+
if (skipGithub) {
|
|
172
175
|
ctx.noGitHub = true;
|
|
173
176
|
return;
|
|
174
177
|
}
|
|
@@ -306,7 +309,7 @@ const option = (label, value, hint) => ({
|
|
|
306
309
|
value,
|
|
307
310
|
hint: hint ?? value
|
|
308
311
|
});
|
|
309
|
-
const runVersionPrompts = async (ctx, config) => {
|
|
312
|
+
const runVersionPrompts = async (ctx, config, inferred) => {
|
|
310
313
|
const { pkg: { current, fromPreRelease } } = ctx;
|
|
311
314
|
const selectVersion = async () => {
|
|
312
315
|
const versions = createVersionMap(current);
|
|
@@ -326,7 +329,7 @@ const runVersionPrompts = async (ctx, config) => {
|
|
|
326
329
|
const type = await select({
|
|
327
330
|
message: question(MSG.PROMPT.SELECT_VERSION, "version"),
|
|
328
331
|
options,
|
|
329
|
-
initialValue: fromPreRelease ? "prerelease" : "patch"
|
|
332
|
+
initialValue: inferred ?? (fromPreRelease ? "prerelease" : "patch")
|
|
330
333
|
});
|
|
331
334
|
abortSinglePrompt(type);
|
|
332
335
|
return type;
|
|
@@ -357,6 +360,7 @@ const createDefaultContext = () => {
|
|
|
357
360
|
release: false,
|
|
358
361
|
showRelease: false,
|
|
359
362
|
showChangelog: false,
|
|
363
|
+
onlyChangelog: false,
|
|
360
364
|
dryRun: false,
|
|
361
365
|
noGit: false,
|
|
362
366
|
noNpm: false,
|
|
@@ -436,11 +440,13 @@ const createDefaultConfig = (isCI) => {
|
|
|
436
440
|
tagPrefix(pkg) {
|
|
437
441
|
return "";
|
|
438
442
|
},
|
|
439
|
-
releaseCount: 1
|
|
443
|
+
releaseCount: 1,
|
|
444
|
+
includeHidden: false,
|
|
445
|
+
transformTypes: (t) => t
|
|
440
446
|
},
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
447
|
+
skipGit: false,
|
|
448
|
+
skipNpm: false,
|
|
449
|
+
skipGithub: false,
|
|
444
450
|
git: {
|
|
445
451
|
commit: isCI,
|
|
446
452
|
tag: isCI,
|
|
@@ -453,7 +459,7 @@ const createDefaultConfig = (isCI) => {
|
|
|
453
459
|
addUntrackedFiles: false,
|
|
454
460
|
requireRemote: true,
|
|
455
461
|
requireRepository: true,
|
|
456
|
-
|
|
462
|
+
requireCleanWorkingTree: true
|
|
457
463
|
},
|
|
458
464
|
npm: {
|
|
459
465
|
publish: isCI,
|
|
@@ -478,179 +484,173 @@ const defaultTypes = [
|
|
|
478
484
|
{
|
|
479
485
|
type: "feat",
|
|
480
486
|
section: "✨ 新功能",
|
|
487
|
+
effect: "bump",
|
|
481
488
|
description: "新增功能"
|
|
482
489
|
},
|
|
483
490
|
{
|
|
484
491
|
type: "feature",
|
|
485
492
|
section: "✨ 新功能",
|
|
493
|
+
effect: "bump",
|
|
486
494
|
description: "新增功能"
|
|
487
495
|
},
|
|
488
496
|
{
|
|
489
497
|
type: "fix",
|
|
490
498
|
section: "🐛 Bug 修复",
|
|
499
|
+
effect: "bump",
|
|
491
500
|
description: "修复 bug"
|
|
492
501
|
},
|
|
493
502
|
{
|
|
494
503
|
type: "perf",
|
|
495
504
|
section: "⚡ 性能优化",
|
|
505
|
+
effect: "bump",
|
|
496
506
|
description: "提升性能"
|
|
497
507
|
},
|
|
498
508
|
{
|
|
499
509
|
type: "revert",
|
|
500
510
|
section: "⏪ 回退",
|
|
511
|
+
effect: "bump",
|
|
501
512
|
description: "回退到之前版本"
|
|
502
513
|
},
|
|
503
514
|
{
|
|
504
515
|
type: "docs",
|
|
505
516
|
section: "📝 文档",
|
|
517
|
+
effect: "changelog",
|
|
506
518
|
description: "文档更新"
|
|
507
519
|
},
|
|
508
520
|
{
|
|
509
521
|
type: "style",
|
|
510
522
|
section: "💄 样式调整",
|
|
523
|
+
effect: "hidden",
|
|
511
524
|
description: "样式或格式修改"
|
|
512
525
|
},
|
|
513
526
|
{
|
|
514
527
|
type: "chore",
|
|
515
528
|
section: "🧹 其他更新",
|
|
529
|
+
effect: "hidden",
|
|
516
530
|
description: "非功能性更改"
|
|
517
531
|
},
|
|
518
532
|
{
|
|
519
533
|
type: "refactor",
|
|
520
534
|
section: "♻️ 代码重构",
|
|
535
|
+
effect: "changelog",
|
|
521
536
|
description: "重构代码"
|
|
522
537
|
},
|
|
523
538
|
{
|
|
524
539
|
type: "test",
|
|
525
540
|
section: "🧪 测试",
|
|
541
|
+
effect: "hidden",
|
|
526
542
|
description: "测试更新"
|
|
527
543
|
},
|
|
528
544
|
{
|
|
529
545
|
type: "build",
|
|
530
546
|
section: "🏗️ 构建系统",
|
|
547
|
+
effect: "hidden",
|
|
531
548
|
description: "构建工具和系统修改"
|
|
532
549
|
},
|
|
533
550
|
{
|
|
534
551
|
type: "ci",
|
|
535
552
|
section: "🤖 CI",
|
|
553
|
+
effect: "hidden",
|
|
536
554
|
description: "持续集成配置"
|
|
537
555
|
},
|
|
538
|
-
{
|
|
539
|
-
type: "config",
|
|
540
|
-
section: "⚙️ 配置",
|
|
541
|
-
description: "配置文件更新"
|
|
542
|
-
},
|
|
543
556
|
{
|
|
544
557
|
type: "deps",
|
|
545
558
|
section: "📦 依赖更新",
|
|
559
|
+
effect: "changelog",
|
|
546
560
|
description: "依赖版本变更"
|
|
547
561
|
},
|
|
548
562
|
{
|
|
549
563
|
type: "security",
|
|
550
564
|
section: "🛡️ 安全修复",
|
|
565
|
+
effect: "changelog",
|
|
551
566
|
description: "安全性更新"
|
|
552
567
|
},
|
|
568
|
+
{
|
|
569
|
+
type: "hotfix",
|
|
570
|
+
section: "🚑 热修复",
|
|
571
|
+
effect: "changelog",
|
|
572
|
+
description: "紧急修复"
|
|
573
|
+
},
|
|
553
574
|
{
|
|
554
575
|
type: "i18n",
|
|
555
576
|
section: "🌐 国际化",
|
|
577
|
+
effect: "changelog",
|
|
556
578
|
description: "国际化相关更新"
|
|
557
579
|
},
|
|
558
580
|
{
|
|
559
581
|
type: "ux",
|
|
560
582
|
section: "🎨 用户体验",
|
|
583
|
+
effect: "changelog",
|
|
561
584
|
description: "用户体验改进"
|
|
562
585
|
},
|
|
563
586
|
{
|
|
564
|
-
type: "
|
|
565
|
-
section: "
|
|
566
|
-
|
|
587
|
+
type: "config",
|
|
588
|
+
section: "⚙️ 配置",
|
|
589
|
+
effect: "hidden",
|
|
590
|
+
description: "配置文件更新"
|
|
567
591
|
},
|
|
568
592
|
{
|
|
569
593
|
type: "tool",
|
|
570
594
|
section: "🛠️ 工具链",
|
|
595
|
+
effect: "hidden",
|
|
571
596
|
description: "工具链变更"
|
|
572
597
|
}
|
|
573
598
|
];
|
|
574
599
|
//#endregion
|
|
600
|
+
//#region src/git/templates.ts
|
|
601
|
+
const isLikelyHash = (value) => /^[0-9a-f]{7,40}$/i.test(value);
|
|
602
|
+
function headerPartial(context) {
|
|
603
|
+
const { isPatch, linkCompare, version, title, date, previousTag } = context;
|
|
604
|
+
const fullTitle = words(linkCompare && !isLikelyHash(previousTag) ? link(version, compareUrl(context)) : version, title && `"${title}"`, date && `(${date})`);
|
|
605
|
+
return heading(2, isPatch ? small(fullTitle) : fullTitle);
|
|
606
|
+
}
|
|
607
|
+
function template(context) {
|
|
608
|
+
const { headerPartial, preamblePartial, commitPartial, footerPartial, noteGroups, commitGroups } = context;
|
|
609
|
+
return segments(headerPartial(context), preamblePartial(context), each(noteGroups, (group) => segments(heading(3, words("⚠", group.title)), list(group.notes, (note) => {
|
|
610
|
+
return each([words(note.commit.scope && bold(`${note.commit.scope}:`), commitPartial(context, note.commit)), list([note.text], (t) => t)], (t) => t, newline());
|
|
611
|
+
})), newline(2)), each(commitGroups, (group) => segments(group.title && heading(3, group.title), list(group.commits, (commit) => commitPartial(context, commit))), newline(2)), footerPartial(context));
|
|
612
|
+
}
|
|
613
|
+
//#endregion
|
|
575
614
|
//#region src/git/changelog.ts
|
|
576
|
-
const parsePreset =
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
615
|
+
const parsePreset = (options = {}) => {
|
|
616
|
+
const { includeHidden, transformTypes } = options;
|
|
617
|
+
let types = [...defaultTypes];
|
|
618
|
+
if (includeHidden) types.forEach((type) => {
|
|
619
|
+
if (type.effect === "hidden") type.effect = "changelog";
|
|
620
|
+
});
|
|
621
|
+
types = transformTypes?.(types) ?? types;
|
|
622
|
+
const preset = createPreset({ types });
|
|
581
623
|
preset.writer ??= {};
|
|
582
|
-
preset.writer.headerPartial =
|
|
583
|
-
|
|
584
|
-
[{{version}}](
|
|
585
|
-
{{~#if @root.repository~}}
|
|
586
|
-
{{~#if @root.host}}
|
|
587
|
-
{{~@root.host}}/
|
|
588
|
-
{{~/if}}
|
|
589
|
-
{{~#if @root.owner}}
|
|
590
|
-
{{~@root.owner}}/
|
|
591
|
-
{{~/if}}
|
|
592
|
-
{{~@root.repository}}
|
|
593
|
-
{{~else}}
|
|
594
|
-
{{~@root.repoUrl}}
|
|
595
|
-
{{~/if~}}
|
|
596
|
-
/compare/{{previousTag}}...{{currentTag}})
|
|
597
|
-
{{~else}}
|
|
598
|
-
{{~version}}
|
|
599
|
-
{{~/if}}
|
|
600
|
-
{{~#if title}} "{{title}}"
|
|
601
|
-
{{~/if}}
|
|
602
|
-
{{~#if date}} ({{date}})
|
|
603
|
-
{{~/if}}
|
|
604
|
-
{{~#if isPatch~}} </small> {{~/if}}
|
|
605
|
-
`.trim() + eol();
|
|
606
|
-
preset.writer.mainTemplate = `{{> header}}
|
|
607
|
-
{{#if noteGroups}}
|
|
608
|
-
{{#each noteGroups}}
|
|
609
|
-
|
|
610
|
-
### ⚠ {{title}}
|
|
611
|
-
|
|
612
|
-
{{#each notes}}
|
|
613
|
-
* {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{commit.subject}} {{#if commit.hash}}([{{commit.shortHash}}](https://github.com/{{@root.owner}}/{{@root.repository}}/commit/{{commit.hash}})){{/if}}
|
|
614
|
-
{{/each}}
|
|
615
|
-
{{/each}}
|
|
616
|
-
{{/if}}
|
|
617
|
-
{{#each commitGroups}}
|
|
618
|
-
|
|
619
|
-
{{#if title}}
|
|
620
|
-
### {{title}}
|
|
621
|
-
|
|
622
|
-
{{/if}}
|
|
623
|
-
{{#each commits}}
|
|
624
|
-
{{> commit root=@root}}
|
|
625
|
-
{{/each}}
|
|
626
|
-
{{/each}}`.trim();
|
|
624
|
+
preset.writer.headerPartial = headerPartial;
|
|
625
|
+
preset.writer.template = template;
|
|
627
626
|
return preset;
|
|
628
627
|
};
|
|
629
|
-
const
|
|
628
|
+
const inferReleaseType = async (options) => {
|
|
629
|
+
const preset = parsePreset(options);
|
|
630
|
+
const res = await new Bumper().bump(preset.whatBump);
|
|
631
|
+
return "releaseType" in res ? res.releaseType : void 0;
|
|
632
|
+
};
|
|
633
|
+
const createGenerator = async (options) => {
|
|
634
|
+
const { getPkgDir, tagPrefix, releaseCount = 1, includeHidden, transformTypes } = options;
|
|
630
635
|
const pkgDir = getPkgDir();
|
|
631
|
-
|
|
632
|
-
|
|
636
|
+
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(parsePreset({
|
|
637
|
+
includeHidden,
|
|
638
|
+
transformTypes
|
|
639
|
+
})).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
633
640
|
};
|
|
634
|
-
const getChangelog = async (
|
|
635
|
-
const generator = await createGenerator(
|
|
636
|
-
getPkgDir,
|
|
637
|
-
tagPrefix,
|
|
638
|
-
releaseCount
|
|
639
|
-
});
|
|
641
|
+
const getChangelog = async (options) => {
|
|
642
|
+
const generator = await createGenerator(options);
|
|
640
643
|
let changelog = "";
|
|
641
644
|
for await (const chunk of generator.write()) changelog += chunk;
|
|
642
645
|
return changelog.trimEnd();
|
|
643
646
|
};
|
|
644
|
-
const generateChangelog = async (
|
|
647
|
+
const generateChangelog = async (options) => {
|
|
648
|
+
const { getPkgDir, releaseCount = 1 } = options;
|
|
645
649
|
const infile = join(getPkgDir(), "CHANGELOG.md");
|
|
646
650
|
if (!existsSync(infile)) await writeFile(infile, "");
|
|
647
651
|
const exist = readFileSync(infile, "utf-8");
|
|
648
652
|
const writeStream = createWriteStream(infile);
|
|
649
|
-
const generator = await createGenerator(
|
|
650
|
-
getPkgDir,
|
|
651
|
-
tagPrefix,
|
|
652
|
-
releaseCount
|
|
653
|
-
});
|
|
653
|
+
const generator = await createGenerator(options);
|
|
654
654
|
for await (const chunk of generator.write()) writeStream.write(chunk);
|
|
655
655
|
const override = isZero(releaseCount);
|
|
656
656
|
if (!(isZero(exist.trim().length) || override)) {
|
|
@@ -980,16 +980,27 @@ var Action = class {
|
|
|
980
980
|
}
|
|
981
981
|
async createContext(cmdArgs, options, prepare = false) {
|
|
982
982
|
const { version: cVersion, name: cName } = readJsonFile(join(__dirname, "../package.json"));
|
|
983
|
-
const { otp, package: defPkg, ...others } = options;
|
|
984
|
-
const { showChangelog, showRelease, ci, dryRun } =
|
|
983
|
+
const { otp, package: defPkg, releaseCount, ...others } = options;
|
|
984
|
+
const { showChangelog, showRelease, ci, dryRun, onlyChangelog } = mapObjectValues(others, (v) => Boolean(v));
|
|
985
985
|
process.env["dryRun"] = String(dryRun);
|
|
986
986
|
const { configFile, config: local } = await resolveConfig(process.cwd());
|
|
987
987
|
const config = mergeConfig(createDefaultConfig(ci ? true : void 0), local);
|
|
988
988
|
info(MSG.INFO.TOOL(cName, cVersion));
|
|
989
989
|
info(MSG.INFO.CONFIG(configFile));
|
|
990
|
+
{
|
|
991
|
+
const { requireCleanWorkingTree, skipGit, skipNpm, skipGithub, isMonorepo, includeHidden } = mapObjectValues(others, (v) => Boolean(v));
|
|
992
|
+
const count = Number(releaseCount);
|
|
993
|
+
config.changelog.releaseCount = isNaN(count) ? 0 : count;
|
|
994
|
+
config.changelog.includeHidden = includeHidden;
|
|
995
|
+
config.git.requireCleanWorkingTree = requireCleanWorkingTree;
|
|
996
|
+
config.skipGit = skipGit;
|
|
997
|
+
config.skipNpm = skipNpm;
|
|
998
|
+
config.skipGithub = skipGithub;
|
|
999
|
+
config.isMonorepo = isMonorepo;
|
|
1000
|
+
}
|
|
990
1001
|
console.log();
|
|
991
1002
|
intro(prepare ? MSG.INTRO_PREPARE(dryRun) : MSG.INTRO(dryRun));
|
|
992
|
-
const { isMonorepo, packages, getPkgDir,
|
|
1003
|
+
const { isMonorepo, packages, getPkgDir, skipNpm } = config;
|
|
993
1004
|
if (isMonorepo) {
|
|
994
1005
|
if (ci) {
|
|
995
1006
|
if (!defPkg) abortTask(MSG.ABORT.MONOREPO_CI_NO_PACKAGE);
|
|
@@ -1016,6 +1027,7 @@ var Action = class {
|
|
|
1016
1027
|
dryRun,
|
|
1017
1028
|
showRelease,
|
|
1018
1029
|
showChangelog,
|
|
1030
|
+
onlyChangelog,
|
|
1019
1031
|
npm: { otp: String(otp) },
|
|
1020
1032
|
increment: cmdArgs,
|
|
1021
1033
|
isIncrement: true,
|
|
@@ -1035,7 +1047,7 @@ var Action = class {
|
|
|
1035
1047
|
toPreRelease: false,
|
|
1036
1048
|
publishConfig: { ...publishConfig }
|
|
1037
1049
|
},
|
|
1038
|
-
noNpm: pkgPrivate ||
|
|
1050
|
+
noNpm: pkgPrivate || skipNpm
|
|
1039
1051
|
});
|
|
1040
1052
|
const { messages } = await publint({ pkgDir });
|
|
1041
1053
|
for (const message of messages) {
|
|
@@ -1047,8 +1059,32 @@ var Action = class {
|
|
|
1047
1059
|
config
|
|
1048
1060
|
};
|
|
1049
1061
|
}
|
|
1062
|
+
async printChangelog(ctx, config) {
|
|
1063
|
+
const { showChangelog, selectedPkg, noGit, isIncrement } = ctx;
|
|
1064
|
+
const { isMonorepo, getPkgDir, changelog: { tagPrefix, releaseCount, includeHidden, transformTypes } } = config;
|
|
1065
|
+
if (noGit) return;
|
|
1066
|
+
const match = isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*";
|
|
1067
|
+
const { from, to } = isZero(releaseCount) ? {
|
|
1068
|
+
from: "",
|
|
1069
|
+
to: "HEAD"
|
|
1070
|
+
} : await resolveChangelogRange(isIncrement, match);
|
|
1071
|
+
const commits = await getLogSince(from, to, getPkgDir(selectedPkg));
|
|
1072
|
+
const changelog = await getChangelog({
|
|
1073
|
+
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1074
|
+
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1075
|
+
releaseCount,
|
|
1076
|
+
includeHidden,
|
|
1077
|
+
transformTypes
|
|
1078
|
+
});
|
|
1079
|
+
Object.assign(ctx.github, { changelog });
|
|
1080
|
+
if (commits) {
|
|
1081
|
+
msg("GIT", `Changelog(${from}...${to}):${eol(2)}` + changelog);
|
|
1082
|
+
msg("GIT", `Commits(${from}...${to}):${eol(2)}` + commits);
|
|
1083
|
+
if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
|
|
1084
|
+
} else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
|
|
1085
|
+
}
|
|
1050
1086
|
async checkTask(ctx, config) {
|
|
1051
|
-
const { dryRun } = ctx;
|
|
1087
|
+
const { dryRun, onlyChangelog } = ctx;
|
|
1052
1088
|
await tasks([
|
|
1053
1089
|
{
|
|
1054
1090
|
title: MSG.CHECK.GIT.CHECKING,
|
|
@@ -1065,7 +1101,7 @@ var Action = class {
|
|
|
1065
1101
|
const { pkg: { publishConfig: { registry } } } = ctx;
|
|
1066
1102
|
return success(MSG.CHECK.NPM.CHECKED(registry, msg), dryRun);
|
|
1067
1103
|
},
|
|
1068
|
-
enabled: !ctx.noNpm
|
|
1104
|
+
enabled: !(ctx.noNpm || onlyChangelog)
|
|
1069
1105
|
},
|
|
1070
1106
|
{
|
|
1071
1107
|
title: MSG.CHECK.GITHUB.CHECKING,
|
|
@@ -1075,19 +1111,24 @@ var Action = class {
|
|
|
1075
1111
|
const repository = getGithubUrl(owner, repo);
|
|
1076
1112
|
return success(MSG.CHECK.GITHUB.CHECKED(repository, msg), dryRun);
|
|
1077
1113
|
},
|
|
1078
|
-
enabled: !ctx.noGitHub
|
|
1114
|
+
enabled: !(ctx.noGitHub || onlyChangelog)
|
|
1079
1115
|
}
|
|
1080
1116
|
]).catch(abortOnError);
|
|
1081
1117
|
}
|
|
1082
1118
|
async bumpTask(ctx, config) {
|
|
1083
|
-
const { pkg: { current }, dryRun, isCI, showRelease,
|
|
1084
|
-
const {
|
|
1119
|
+
const { pkg: { current }, dryRun, isCI, showRelease, selectedPkg, onlyChangelog } = ctx;
|
|
1120
|
+
const { hooks, getPkgDir, changelog: { includeHidden, transformTypes } } = config;
|
|
1121
|
+
if (onlyChangelog) return;
|
|
1085
1122
|
const need = (ctx) => {
|
|
1086
1123
|
const { pkg: { next }, isIncrement } = ctx;
|
|
1087
1124
|
return !next && isIncrement;
|
|
1088
1125
|
};
|
|
1089
1126
|
if (need(ctx)) {
|
|
1090
|
-
const
|
|
1127
|
+
const inferred = await inferReleaseType({
|
|
1128
|
+
includeHidden,
|
|
1129
|
+
transformTypes
|
|
1130
|
+
});
|
|
1131
|
+
const nextVersion = (isCI ? inferred ?? inc(current, "patch") : void 0) || await runVersionPrompts(ctx, config, inferred);
|
|
1091
1132
|
ctx.isIncrement = neq(current, nextVersion);
|
|
1092
1133
|
const { version: next, isPrerelease: toPreRelease, preId, preBase } = parseVersion(nextVersion);
|
|
1093
1134
|
Object.assign(ctx.pkg, {
|
|
@@ -1097,7 +1138,7 @@ var Action = class {
|
|
|
1097
1138
|
preBase
|
|
1098
1139
|
});
|
|
1099
1140
|
}
|
|
1100
|
-
const { pkg: { next }
|
|
1141
|
+
const { pkg: { next } } = ctx;
|
|
1101
1142
|
if (showRelease) taskEnd(MSG.LOG.SHOW_VERSION(next));
|
|
1102
1143
|
await runLifeCycleHook(hooks, "before:bump", dryRun);
|
|
1103
1144
|
await tasks([{
|
|
@@ -1107,31 +1148,15 @@ var Action = class {
|
|
|
1107
1148
|
await formatTemplate(ctx, config);
|
|
1108
1149
|
const to = `(${current}...${diff(current, next)})`;
|
|
1109
1150
|
return success(MSG.TASK.VERSION.END(to), dryRun);
|
|
1110
|
-
}
|
|
1151
|
+
},
|
|
1152
|
+
enabled: true
|
|
1111
1153
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1112
1154
|
await runLifeCycleHook(hooks, "after:bump", dryRun);
|
|
1113
|
-
|
|
1114
|
-
const match = isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*";
|
|
1115
|
-
const { from, to } = isZero(releaseCount) ? {
|
|
1116
|
-
from: "",
|
|
1117
|
-
to: "HEAD"
|
|
1118
|
-
} : await resolveChangelogRange(isIncrement, match);
|
|
1119
|
-
const commits = await getLogSince(from, to, getPkgDir(selectedPkg));
|
|
1120
|
-
const changelog = await getChangelog({
|
|
1121
|
-
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1122
|
-
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1123
|
-
releaseCount
|
|
1124
|
-
});
|
|
1125
|
-
Object.assign(ctx.github, { changelog });
|
|
1126
|
-
if (commits) {
|
|
1127
|
-
msg("GIT", `Changelog(${from}...${to}):${eol(2)}` + changelog);
|
|
1128
|
-
msg("GIT", `Commits(${from}...${to}):${eol(2)}` + commits);
|
|
1129
|
-
if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
|
|
1130
|
-
} else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
|
|
1155
|
+
await this.printChangelog(ctx, config);
|
|
1131
1156
|
}
|
|
1132
1157
|
async changelogTask(ctx, config) {
|
|
1133
|
-
const { isIncrement, dryRun, selectedPkg, noGit } = ctx;
|
|
1134
|
-
const { getPkgDir, changelog: { tagPrefix, releaseCount } } = config;
|
|
1158
|
+
const { isIncrement, dryRun, selectedPkg, noGit, onlyChangelog } = ctx;
|
|
1159
|
+
const { getPkgDir, changelog: { tagPrefix, releaseCount, includeHidden, transformTypes } } = config;
|
|
1135
1160
|
if (noGit) return;
|
|
1136
1161
|
await tasks([{
|
|
1137
1162
|
title: MSG.TASK.CHANGELOG.START,
|
|
@@ -1139,7 +1164,9 @@ var Action = class {
|
|
|
1139
1164
|
await generateChangelog({
|
|
1140
1165
|
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1141
1166
|
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1142
|
-
releaseCount
|
|
1167
|
+
releaseCount,
|
|
1168
|
+
includeHidden,
|
|
1169
|
+
transformTypes
|
|
1143
1170
|
});
|
|
1144
1171
|
await gitAddAll();
|
|
1145
1172
|
return success(MSG.TASK.CHANGELOG.END, dryRun);
|
|
@@ -1149,6 +1176,7 @@ var Action = class {
|
|
|
1149
1176
|
const changeset = await getShortStatus();
|
|
1150
1177
|
if (changeset) msg("GIT", `Changes:${eol(2)}` + coloredStatus(changeset));
|
|
1151
1178
|
else msg("GIT", MSG.LOG.CHANGES_EMPTY);
|
|
1179
|
+
if (onlyChangelog) taskEnd(MSG.LOG.ONLY_CHANGELOG);
|
|
1152
1180
|
}
|
|
1153
1181
|
async gitTask(ctx, config) {
|
|
1154
1182
|
const { noGit, dryRun } = ctx;
|
|
@@ -1210,13 +1238,13 @@ var Action = class {
|
|
|
1210
1238
|
//#endregion
|
|
1211
1239
|
//#region src/cli.ts
|
|
1212
1240
|
const pkg = readJsonFile(join(__dirname, "..", "package.json"));
|
|
1213
|
-
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) => {
|
|
1241
|
+
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("-m, --is-monorepo", "Mono-repo project", false).option("-p, --package <pkg>", "Specify package name (Mono-repo 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).option("-r, --release-count <count>", "Release count for release.", (value) => Number(value), 1).option("--include-hidden", "Include hidden commit types in the changelog.", false).option("--only-changelog", "Only update the changelog.", false).option("-C, --no-require-clean-working-tree", "Allow releasing with uncommitted changes.", true).option("--skip-git", "Skip all Git-related checks and operations.", false).option("--skip-npm", "Skip all npm-related checks and operations.", false).option("--skip-github", "Skip all GitHub-related checks and operations.", false).helpOption("-h, --help", "Display help information.").action(async (releaseType, options) => {
|
|
1214
1242
|
const { prepare } = options;
|
|
1215
1243
|
const action = new Action();
|
|
1216
1244
|
prepare ? await action.handlePrepareRelease(releaseType, options) : await action.handleRelease(releaseType, options);
|
|
1217
1245
|
});
|
|
1218
|
-
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) => {
|
|
1219
|
-
const { force = false, monorepo = false } = options;
|
|
1246
|
+
program.command("init").description("Create a release configuration file").option("-f, --force", "Overwrite existing config file", false).option("-m, --monorepo", "Mono-repo project", false).option("-a, --add", "Automatically stage the config file.", false).action((options) => {
|
|
1247
|
+
const { force = false, monorepo = false, add = false } = options;
|
|
1220
1248
|
const cwd = process.cwd();
|
|
1221
1249
|
const { type } = readJsonFile(join(cwd, "package.json"));
|
|
1222
1250
|
const isESM = type === "module";
|
|
@@ -1236,9 +1264,13 @@ program.command("init").description("Create a release configuration file").optio
|
|
|
1236
1264
|
tagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1237
1265
|
},
|
|
1238
1266
|
}`;
|
|
1239
|
-
|
|
1240
|
-
|
|
1267
|
+
const content = [`import { defineConfig } from '@peiyanlu/release'`, `export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`].join(eol(2));
|
|
1268
|
+
writeFileSync(configFile, content, "utf-8");
|
|
1269
|
+
console.log(`Wrote to ${underline(dim(configFile))}`);
|
|
1270
|
+
if (add) gitAddSync([infile]);
|
|
1271
|
+
console.log(`\n${content}`);
|
|
1241
1272
|
});
|
|
1273
|
+
program.addHelpText("afterAll", "\nThanks for using!");
|
|
1242
1274
|
program.parse(process.argv);
|
|
1243
1275
|
program.on("command:*", () => {
|
|
1244
1276
|
console.error(`\n${red`Error`} Invalid command: ${red`%s`}`, program.args.join(" "));
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DeepPartial, DeepRequired } from "@peiyanlu/ts-utils";
|
|
2
2
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
3
|
+
import { CommitType } from "conventional-changelog-conventionalcommits";
|
|
3
4
|
import { log } from "@clack/prompts";
|
|
4
5
|
|
|
5
6
|
//#region src/types.d.ts
|
|
@@ -28,15 +29,29 @@ interface ReleaseConfig {
|
|
|
28
29
|
* 影响生成的日志数量、日志标题(monorepo)。
|
|
29
30
|
*/
|
|
30
31
|
changelog: {
|
|
31
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* 作为 CHANGELOG 标题的 Tag 前缀,多包场景下需要 `${pkg}@`
|
|
34
|
+
*/
|
|
35
|
+
tagPrefix: ((pkg: string) => string);
|
|
36
|
+
/**
|
|
37
|
+
* 生成多少个版本的变更日志,详见 {@link Options}
|
|
38
|
+
*/
|
|
32
39
|
releaseCount: number;
|
|
40
|
+
/**
|
|
41
|
+
* 生成 CHANGELOG 时包含默认隐藏的 commit 类型
|
|
42
|
+
*/
|
|
43
|
+
includeHidden: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* 对默认 commit types 进行转换
|
|
46
|
+
*/
|
|
47
|
+
transformTypes: (types: CommitType[]) => CommitType[];
|
|
33
48
|
};
|
|
34
|
-
/**
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
|
|
49
|
+
/** 跳过 git、github 环境检查以及后续操作 */
|
|
50
|
+
skipGit: boolean;
|
|
51
|
+
/** 跳过 npm 环境检查以及后续操作 */
|
|
52
|
+
skipNpm: boolean;
|
|
53
|
+
/** 跳过 github 环境检查以及后续操作 */
|
|
54
|
+
skipGithub: boolean;
|
|
40
55
|
/**
|
|
41
56
|
* Git 相关配置
|
|
42
57
|
* 影响提交、打 tag、推送等本地/远程 Git 行为。
|
|
@@ -90,7 +105,7 @@ interface ReleaseConfig {
|
|
|
90
105
|
/**
|
|
91
106
|
* 是否要求工作目录保持干净(无未提交的更改)
|
|
92
107
|
*/
|
|
93
|
-
|
|
108
|
+
requireCleanWorkingTree: boolean;
|
|
94
109
|
};
|
|
95
110
|
/**
|
|
96
111
|
* NPM 发布相关配置
|
|
@@ -173,21 +188,15 @@ interface GenerateOptions {
|
|
|
173
188
|
/** 生成多少个变更日志,详见 {@link Options} */
|
|
174
189
|
releaseCount?: number;
|
|
175
190
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}: GenerateOptions) => Promise<string>;
|
|
186
|
-
declare const generateChangelog: ({
|
|
187
|
-
getPkgDir,
|
|
188
|
-
tagPrefix,
|
|
189
|
-
releaseCount
|
|
190
|
-
}: GenerateOptions) => Promise<void>;
|
|
191
|
+
interface ParsePresetOptions {
|
|
192
|
+
/** 生成 CHANGELOG 时包含默认隐藏的 commit 类型 */
|
|
193
|
+
includeHidden?: boolean;
|
|
194
|
+
/** 对默认 commit types 进行转换 */
|
|
195
|
+
transformTypes?: (types: CommitType[]) => CommitType[];
|
|
196
|
+
}
|
|
197
|
+
declare const createGenerator: (options: GenerateOptions & ParsePresetOptions) => Promise<ConventionalChangelog>;
|
|
198
|
+
declare const getChangelog: (options: GenerateOptions & ParsePresetOptions) => Promise<string>;
|
|
199
|
+
declare const generateChangelog: (options: GenerateOptions & ParsePresetOptions) => Promise<void>;
|
|
191
200
|
//#endregion
|
|
192
201
|
//#region src/npm/publish.d.ts
|
|
193
202
|
interface PublishTagOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -4,8 +4,10 @@ import { join } from "node:path";
|
|
|
4
4
|
import { eol, getPackageInfo, publishPackage, resolvePublishTag } from "@peiyanlu/cli-utils";
|
|
5
5
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
6
6
|
import createPreset from "conventional-changelog-conventionalcommits";
|
|
7
|
+
import "conventional-recommended-bump";
|
|
7
8
|
import { writeFile } from "node:fs/promises";
|
|
8
9
|
import { finished } from "node:stream/promises";
|
|
10
|
+
import { bold, compareUrl, each, heading, link, list, newline, segments, small, words } from "@conventional-changelog/template";
|
|
9
11
|
import { blue, cyan, green, magenta, red, underline, yellow } from "ansis";
|
|
10
12
|
//#region src/config.ts
|
|
11
13
|
function defineConfig(config) {
|
|
@@ -31,179 +33,168 @@ const defaultTypes = [
|
|
|
31
33
|
{
|
|
32
34
|
type: "feat",
|
|
33
35
|
section: "✨ 新功能",
|
|
36
|
+
effect: "bump",
|
|
34
37
|
description: "新增功能"
|
|
35
38
|
},
|
|
36
39
|
{
|
|
37
40
|
type: "feature",
|
|
38
41
|
section: "✨ 新功能",
|
|
42
|
+
effect: "bump",
|
|
39
43
|
description: "新增功能"
|
|
40
44
|
},
|
|
41
45
|
{
|
|
42
46
|
type: "fix",
|
|
43
47
|
section: "🐛 Bug 修复",
|
|
48
|
+
effect: "bump",
|
|
44
49
|
description: "修复 bug"
|
|
45
50
|
},
|
|
46
51
|
{
|
|
47
52
|
type: "perf",
|
|
48
53
|
section: "⚡ 性能优化",
|
|
54
|
+
effect: "bump",
|
|
49
55
|
description: "提升性能"
|
|
50
56
|
},
|
|
51
57
|
{
|
|
52
58
|
type: "revert",
|
|
53
59
|
section: "⏪ 回退",
|
|
60
|
+
effect: "bump",
|
|
54
61
|
description: "回退到之前版本"
|
|
55
62
|
},
|
|
56
63
|
{
|
|
57
64
|
type: "docs",
|
|
58
65
|
section: "📝 文档",
|
|
66
|
+
effect: "changelog",
|
|
59
67
|
description: "文档更新"
|
|
60
68
|
},
|
|
61
69
|
{
|
|
62
70
|
type: "style",
|
|
63
71
|
section: "💄 样式调整",
|
|
72
|
+
effect: "hidden",
|
|
64
73
|
description: "样式或格式修改"
|
|
65
74
|
},
|
|
66
75
|
{
|
|
67
76
|
type: "chore",
|
|
68
77
|
section: "🧹 其他更新",
|
|
78
|
+
effect: "hidden",
|
|
69
79
|
description: "非功能性更改"
|
|
70
80
|
},
|
|
71
81
|
{
|
|
72
82
|
type: "refactor",
|
|
73
83
|
section: "♻️ 代码重构",
|
|
84
|
+
effect: "changelog",
|
|
74
85
|
description: "重构代码"
|
|
75
86
|
},
|
|
76
87
|
{
|
|
77
88
|
type: "test",
|
|
78
89
|
section: "🧪 测试",
|
|
90
|
+
effect: "hidden",
|
|
79
91
|
description: "测试更新"
|
|
80
92
|
},
|
|
81
93
|
{
|
|
82
94
|
type: "build",
|
|
83
95
|
section: "🏗️ 构建系统",
|
|
96
|
+
effect: "hidden",
|
|
84
97
|
description: "构建工具和系统修改"
|
|
85
98
|
},
|
|
86
99
|
{
|
|
87
100
|
type: "ci",
|
|
88
101
|
section: "🤖 CI",
|
|
102
|
+
effect: "hidden",
|
|
89
103
|
description: "持续集成配置"
|
|
90
104
|
},
|
|
91
|
-
{
|
|
92
|
-
type: "config",
|
|
93
|
-
section: "⚙️ 配置",
|
|
94
|
-
description: "配置文件更新"
|
|
95
|
-
},
|
|
96
105
|
{
|
|
97
106
|
type: "deps",
|
|
98
107
|
section: "📦 依赖更新",
|
|
108
|
+
effect: "changelog",
|
|
99
109
|
description: "依赖版本变更"
|
|
100
110
|
},
|
|
101
111
|
{
|
|
102
112
|
type: "security",
|
|
103
113
|
section: "🛡️ 安全修复",
|
|
114
|
+
effect: "changelog",
|
|
104
115
|
description: "安全性更新"
|
|
105
116
|
},
|
|
117
|
+
{
|
|
118
|
+
type: "hotfix",
|
|
119
|
+
section: "🚑 热修复",
|
|
120
|
+
effect: "changelog",
|
|
121
|
+
description: "紧急修复"
|
|
122
|
+
},
|
|
106
123
|
{
|
|
107
124
|
type: "i18n",
|
|
108
125
|
section: "🌐 国际化",
|
|
126
|
+
effect: "changelog",
|
|
109
127
|
description: "国际化相关更新"
|
|
110
128
|
},
|
|
111
129
|
{
|
|
112
130
|
type: "ux",
|
|
113
131
|
section: "🎨 用户体验",
|
|
132
|
+
effect: "changelog",
|
|
114
133
|
description: "用户体验改进"
|
|
115
134
|
},
|
|
116
135
|
{
|
|
117
|
-
type: "
|
|
118
|
-
section: "
|
|
119
|
-
|
|
136
|
+
type: "config",
|
|
137
|
+
section: "⚙️ 配置",
|
|
138
|
+
effect: "hidden",
|
|
139
|
+
description: "配置文件更新"
|
|
120
140
|
},
|
|
121
141
|
{
|
|
122
142
|
type: "tool",
|
|
123
143
|
section: "🛠️ 工具链",
|
|
144
|
+
effect: "hidden",
|
|
124
145
|
description: "工具链变更"
|
|
125
146
|
}
|
|
126
147
|
];
|
|
127
148
|
//#endregion
|
|
149
|
+
//#region src/git/templates.ts
|
|
150
|
+
const isLikelyHash = (value) => /^[0-9a-f]{7,40}$/i.test(value);
|
|
151
|
+
function headerPartial(context) {
|
|
152
|
+
const { isPatch, linkCompare, version, title, date, previousTag } = context;
|
|
153
|
+
const fullTitle = words(linkCompare && !isLikelyHash(previousTag) ? link(version, compareUrl(context)) : version, title && `"${title}"`, date && `(${date})`);
|
|
154
|
+
return heading(2, isPatch ? small(fullTitle) : fullTitle);
|
|
155
|
+
}
|
|
156
|
+
function template(context) {
|
|
157
|
+
const { headerPartial, preamblePartial, commitPartial, footerPartial, noteGroups, commitGroups } = context;
|
|
158
|
+
return segments(headerPartial(context), preamblePartial(context), each(noteGroups, (group) => segments(heading(3, words("⚠", group.title)), list(group.notes, (note) => {
|
|
159
|
+
return each([words(note.commit.scope && bold(`${note.commit.scope}:`), commitPartial(context, note.commit)), list([note.text], (t) => t)], (t) => t, newline());
|
|
160
|
+
})), newline(2)), each(commitGroups, (group) => segments(group.title && heading(3, group.title), list(group.commits, (commit) => commitPartial(context, commit))), newline(2)), footerPartial(context));
|
|
161
|
+
}
|
|
162
|
+
//#endregion
|
|
128
163
|
//#region src/git/changelog.ts
|
|
129
|
-
const parsePreset =
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
164
|
+
const parsePreset = (options = {}) => {
|
|
165
|
+
const { includeHidden, transformTypes } = options;
|
|
166
|
+
let types = [...defaultTypes];
|
|
167
|
+
if (includeHidden) types.forEach((type) => {
|
|
168
|
+
if (type.effect === "hidden") type.effect = "changelog";
|
|
169
|
+
});
|
|
170
|
+
types = transformTypes?.(types) ?? types;
|
|
171
|
+
const preset = createPreset({ types });
|
|
134
172
|
preset.writer ??= {};
|
|
135
|
-
preset.writer.headerPartial =
|
|
136
|
-
|
|
137
|
-
[{{version}}](
|
|
138
|
-
{{~#if @root.repository~}}
|
|
139
|
-
{{~#if @root.host}}
|
|
140
|
-
{{~@root.host}}/
|
|
141
|
-
{{~/if}}
|
|
142
|
-
{{~#if @root.owner}}
|
|
143
|
-
{{~@root.owner}}/
|
|
144
|
-
{{~/if}}
|
|
145
|
-
{{~@root.repository}}
|
|
146
|
-
{{~else}}
|
|
147
|
-
{{~@root.repoUrl}}
|
|
148
|
-
{{~/if~}}
|
|
149
|
-
/compare/{{previousTag}}...{{currentTag}})
|
|
150
|
-
{{~else}}
|
|
151
|
-
{{~version}}
|
|
152
|
-
{{~/if}}
|
|
153
|
-
{{~#if title}} "{{title}}"
|
|
154
|
-
{{~/if}}
|
|
155
|
-
{{~#if date}} ({{date}})
|
|
156
|
-
{{~/if}}
|
|
157
|
-
{{~#if isPatch~}} </small> {{~/if}}
|
|
158
|
-
`.trim() + eol();
|
|
159
|
-
preset.writer.mainTemplate = `{{> header}}
|
|
160
|
-
{{#if noteGroups}}
|
|
161
|
-
{{#each noteGroups}}
|
|
162
|
-
|
|
163
|
-
### ⚠ {{title}}
|
|
164
|
-
|
|
165
|
-
{{#each notes}}
|
|
166
|
-
* {{#if commit.scope}}**{{commit.scope}}:** {{/if}}{{commit.subject}} {{#if commit.hash}}([{{commit.shortHash}}](https://github.com/{{@root.owner}}/{{@root.repository}}/commit/{{commit.hash}})){{/if}}
|
|
167
|
-
{{/each}}
|
|
168
|
-
{{/each}}
|
|
169
|
-
{{/if}}
|
|
170
|
-
{{#each commitGroups}}
|
|
171
|
-
|
|
172
|
-
{{#if title}}
|
|
173
|
-
### {{title}}
|
|
174
|
-
|
|
175
|
-
{{/if}}
|
|
176
|
-
{{#each commits}}
|
|
177
|
-
{{> commit root=@root}}
|
|
178
|
-
{{/each}}
|
|
179
|
-
{{/each}}`.trim();
|
|
173
|
+
preset.writer.headerPartial = headerPartial;
|
|
174
|
+
preset.writer.template = template;
|
|
180
175
|
return preset;
|
|
181
176
|
};
|
|
182
|
-
const createGenerator = async (
|
|
177
|
+
const createGenerator = async (options) => {
|
|
178
|
+
const { getPkgDir, tagPrefix, releaseCount = 1, includeHidden, transformTypes } = options;
|
|
183
179
|
const pkgDir = getPkgDir();
|
|
184
|
-
|
|
185
|
-
|
|
180
|
+
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(parsePreset({
|
|
181
|
+
includeHidden,
|
|
182
|
+
transformTypes
|
|
183
|
+
})).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
186
184
|
};
|
|
187
|
-
const getChangelog = async (
|
|
188
|
-
const generator = await createGenerator(
|
|
189
|
-
getPkgDir,
|
|
190
|
-
tagPrefix,
|
|
191
|
-
releaseCount
|
|
192
|
-
});
|
|
185
|
+
const getChangelog = async (options) => {
|
|
186
|
+
const generator = await createGenerator(options);
|
|
193
187
|
let changelog = "";
|
|
194
188
|
for await (const chunk of generator.write()) changelog += chunk;
|
|
195
189
|
return changelog.trimEnd();
|
|
196
190
|
};
|
|
197
|
-
const generateChangelog = async (
|
|
191
|
+
const generateChangelog = async (options) => {
|
|
192
|
+
const { getPkgDir, releaseCount = 1 } = options;
|
|
198
193
|
const infile = join(getPkgDir(), "CHANGELOG.md");
|
|
199
194
|
if (!existsSync(infile)) await writeFile(infile, "");
|
|
200
195
|
const exist = readFileSync(infile, "utf-8");
|
|
201
196
|
const writeStream = createWriteStream(infile);
|
|
202
|
-
const generator = await createGenerator(
|
|
203
|
-
getPkgDir,
|
|
204
|
-
tagPrefix,
|
|
205
|
-
releaseCount
|
|
206
|
-
});
|
|
197
|
+
const generator = await createGenerator(options);
|
|
207
198
|
for await (const chunk of generator.write()) writeStream.write(chunk);
|
|
208
199
|
const override = isZero(releaseCount);
|
|
209
200
|
if (!(isZero(exist.trim().length) || override)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peiyanlu/release",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Release project tool.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -31,12 +31,13 @@
|
|
|
31
31
|
"@clack/prompts": "^1.5.0",
|
|
32
32
|
"@octokit/rest": "^22.0.1",
|
|
33
33
|
"@peiyanlu/cli-utils": "^0.0.12",
|
|
34
|
-
"@peiyanlu/ts-utils": "^0.0.
|
|
34
|
+
"@peiyanlu/ts-utils": "^0.0.8",
|
|
35
35
|
"ansis": "^4.3.1",
|
|
36
36
|
"commander": "^15.0.0",
|
|
37
|
-
"conventional-changelog": "^
|
|
38
|
-
"conventional-changelog-conventionalcommits": "^
|
|
39
|
-
"conventional-
|
|
37
|
+
"conventional-changelog": "^8.0.0",
|
|
38
|
+
"conventional-changelog-conventionalcommits": "^10.2.0",
|
|
39
|
+
"conventional-recommended-bump": "^12.0.0",
|
|
40
|
+
"@conventional-changelog/template": "^1.2.0",
|
|
40
41
|
"mime-types": "^3.0.2",
|
|
41
42
|
"open": "^11.0.0",
|
|
42
43
|
"publint": "^0.3.21",
|