@peiyanlu/release 0.0.13 → 0.0.15
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 +6 -2
- package/dist/cli.mjs +96 -50
- package/dist/index.d.mts +25 -13
- package/dist/index.mjs +35 -25
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -122,7 +122,9 @@ export default defineConfig({
|
|
|
122
122
|
packages: [],
|
|
123
123
|
getPkgDir: (pkg) => `.`,
|
|
124
124
|
toTag: (pkg: string, version: string) => `v${ version }`,
|
|
125
|
-
|
|
125
|
+
changelog: {
|
|
126
|
+
tagPrefix: undefined,
|
|
127
|
+
},
|
|
126
128
|
|
|
127
129
|
git: {
|
|
128
130
|
commit: true,
|
|
@@ -157,7 +159,9 @@ export default defineConfig({
|
|
|
157
159
|
packages: [ 'demo-a', 'demo-b' ],
|
|
158
160
|
getPkgDir: (pkg) => `packages/${ pkg }`,
|
|
159
161
|
toTag: (pkg: string, version: string) => `${ pkg }@${ version }`,
|
|
160
|
-
|
|
162
|
+
changelog: {
|
|
163
|
+
tagPrefix: (pkg: string) => `${ pkg }@`,
|
|
164
|
+
},
|
|
161
165
|
|
|
162
166
|
git: {
|
|
163
167
|
commit: true,
|
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, isOtpError, isPrerelease, isValid, isWorkingDirClean, parseGitHubRepo, parseVersion, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, resolveChangelogRange, resolvePublishTag } 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, registryArg, resolveChangelogRange, resolvePublishTag, runNpm } 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
9
|
import { publint } from "publint";
|
|
9
10
|
import { formatMessage } from "publint/utils";
|
|
10
11
|
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";
|
|
@@ -54,13 +54,12 @@ const resolveConfig = async (cwd) => {
|
|
|
54
54
|
};
|
|
55
55
|
};
|
|
56
56
|
const mergeConfig = (defaults, overrides) => {
|
|
57
|
-
if (!overrides) return structuredClone(defaults);
|
|
58
|
-
if (Object.keys(defaults).length === 0) return structuredClone(overrides);
|
|
59
57
|
const result = {};
|
|
60
|
-
for (const key
|
|
58
|
+
for (const key of Object.keys(defaults)) {
|
|
61
59
|
const defaultValue = defaults[key];
|
|
62
60
|
const overrideValue = overrides[key];
|
|
63
|
-
if (overrideValue
|
|
61
|
+
if (isUndefined(overrideValue)) if (isFunction(defaultValue)) result[key] = defaultValue;
|
|
62
|
+
else if (isPlainObject(defaultValue)) result[key] = mergeConfig(defaultValue, {});
|
|
64
63
|
else result[key] = structuredClone(defaultValue);
|
|
65
64
|
else if (isPlainObject(defaultValue) && isPlainObject(overrideValue)) result[key] = mergeConfig(defaultValue, overrideValue);
|
|
66
65
|
else if (isFunction(overrideValue)) result[key] = overrideValue;
|
|
@@ -108,15 +107,15 @@ const MSG = {
|
|
|
108
107
|
},
|
|
109
108
|
NPM: {
|
|
110
109
|
START: "Publishing npm package",
|
|
111
|
-
END: (url) => `
|
|
110
|
+
END: (url) => url ? `Published npm package: ${underline(dim(url))}` : "NPM publish skipped"
|
|
112
111
|
},
|
|
113
112
|
GIT: {
|
|
114
113
|
START: "Creating git commit, tag & push",
|
|
115
|
-
END: "Git commit, tag & push completed"
|
|
114
|
+
END: (push) => push ? "Git commit, tag & push completed" : "Git commit, tag & push skipped"
|
|
116
115
|
},
|
|
117
116
|
GITHUB: {
|
|
118
117
|
START: "Creating GitHub release",
|
|
119
|
-
END: (url) => `GitHub release
|
|
118
|
+
END: (url) => url ? `Created GitHub release: ${underline(dim(url))}` : "GitHub release skipped"
|
|
120
119
|
}
|
|
121
120
|
},
|
|
122
121
|
PROMPT: {
|
|
@@ -134,13 +133,14 @@ const MSG = {
|
|
|
134
133
|
GIT_WORKDIR: `[git] Working dir must be clean`,
|
|
135
134
|
GIT_REGISTRY: (name) => `[git] Repository not found for ${dim(name)}`,
|
|
136
135
|
GIT_REMOTE: (name) => `[git] Remote "${dim(name)}" not found`,
|
|
137
|
-
GITHUB_TAG_EXIT: (tag) => `[github] Release
|
|
136
|
+
GITHUB_TAG_EXIT: (tag) => `[github] Release for tag "${tag}" already exists and is not a draft`,
|
|
138
137
|
GITHUB_TOKEN: (tokenRef) => `[github] Missing GitHub token. Please set ${yellow(tokenRef ?? "GITHUB_TOKEN")}`,
|
|
139
138
|
GITHUB_AUTH: "[github] Invalid GitHub token or insufficient permissions",
|
|
140
139
|
GITHUB_USER: "[github] User does not have permission to create releases",
|
|
141
140
|
NPM_REGISTRY: (registry) => `[npm] Unable to reach npm registry ${underline(dim(registry))}`,
|
|
142
|
-
NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again
|
|
143
|
-
|
|
141
|
+
NPM_AUTH: `[npm] Not authenticated with npm. Please run ${yellow("npm login")} and try again`,
|
|
142
|
+
NPM_PERMISSION: `[npm] Missing publish permission. Please use a token with publish access and try again`,
|
|
143
|
+
NPM_USER: (user, name) => `[npm] User ${dim(user)} is not a collaborator of ${dim(name)}`
|
|
144
144
|
},
|
|
145
145
|
ABORT: {
|
|
146
146
|
CANCEL: "Operation cancelled",
|
|
@@ -444,8 +444,11 @@ const createDefaultConfig = (isCI) => {
|
|
|
444
444
|
toTag(pkg, version) {
|
|
445
445
|
return `v${version}`;
|
|
446
446
|
},
|
|
447
|
-
|
|
448
|
-
|
|
447
|
+
changelog: {
|
|
448
|
+
tagPrefix(pkg) {
|
|
449
|
+
return "";
|
|
450
|
+
},
|
|
451
|
+
releaseCount: 1
|
|
449
452
|
},
|
|
450
453
|
ignoreGit: false,
|
|
451
454
|
ignoreNpm: false,
|
|
@@ -521,42 +524,42 @@ const defaultTypes = [
|
|
|
521
524
|
},
|
|
522
525
|
{
|
|
523
526
|
type: "chore",
|
|
524
|
-
section: "
|
|
527
|
+
section: "🧹 其他更新",
|
|
525
528
|
description: "非功能性更改"
|
|
526
529
|
},
|
|
527
530
|
{
|
|
528
531
|
type: "refactor",
|
|
529
|
-
section: "
|
|
532
|
+
section: "♻️ 代码重构",
|
|
530
533
|
description: "重构代码"
|
|
531
534
|
},
|
|
532
535
|
{
|
|
533
536
|
type: "test",
|
|
534
|
-
section: "
|
|
537
|
+
section: "🧪 测试",
|
|
535
538
|
description: "测试更新"
|
|
536
539
|
},
|
|
537
540
|
{
|
|
538
541
|
type: "build",
|
|
539
|
-
section: "
|
|
542
|
+
section: "🏗️ 构建系统",
|
|
540
543
|
description: "构建工具和系统修改"
|
|
541
544
|
},
|
|
542
545
|
{
|
|
543
546
|
type: "ci",
|
|
544
|
-
section: "
|
|
547
|
+
section: "🤖 CI",
|
|
545
548
|
description: "持续集成配置"
|
|
546
549
|
},
|
|
547
550
|
{
|
|
548
551
|
type: "config",
|
|
549
|
-
section: "
|
|
552
|
+
section: "⚙️ 配置",
|
|
550
553
|
description: "配置文件更新"
|
|
551
554
|
},
|
|
552
555
|
{
|
|
553
556
|
type: "deps",
|
|
554
|
-
section: "
|
|
557
|
+
section: "📦 依赖更新",
|
|
555
558
|
description: "依赖版本变更"
|
|
556
559
|
},
|
|
557
560
|
{
|
|
558
561
|
type: "security",
|
|
559
|
-
section: "
|
|
562
|
+
section: "🛡️ 安全修复",
|
|
560
563
|
description: "安全性更新"
|
|
561
564
|
},
|
|
562
565
|
{
|
|
@@ -566,13 +569,18 @@ const defaultTypes = [
|
|
|
566
569
|
},
|
|
567
570
|
{
|
|
568
571
|
type: "ux",
|
|
569
|
-
section: "
|
|
572
|
+
section: "🎨 用户体验",
|
|
570
573
|
description: "用户体验改进"
|
|
571
574
|
},
|
|
572
575
|
{
|
|
573
576
|
type: "hotfix",
|
|
574
|
-
section: "
|
|
577
|
+
section: "🚑 热修复",
|
|
575
578
|
description: "紧急修复"
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
type: "tool",
|
|
582
|
+
section: "🛠️ 工具链",
|
|
583
|
+
description: "工具链变更"
|
|
576
584
|
}
|
|
577
585
|
];
|
|
578
586
|
//#endregion
|
|
@@ -629,34 +637,40 @@ const parsePreset = async () => {
|
|
|
629
637
|
{{#each commits}}
|
|
630
638
|
{{> commit root=@root}}
|
|
631
639
|
{{/each}}
|
|
632
|
-
{{/each}}`.trim()
|
|
640
|
+
{{/each}}`.trim();
|
|
633
641
|
return preset;
|
|
634
642
|
};
|
|
635
|
-
const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
643
|
+
const createGenerator = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
|
|
636
644
|
const pkgDir = getPkgDir();
|
|
637
645
|
const preset = await parsePreset();
|
|
638
|
-
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount
|
|
646
|
+
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
639
647
|
};
|
|
640
|
-
const getChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
648
|
+
const getChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
|
|
641
649
|
const generator = await createGenerator({
|
|
642
650
|
getPkgDir,
|
|
643
|
-
tagPrefix
|
|
651
|
+
tagPrefix,
|
|
652
|
+
releaseCount
|
|
644
653
|
});
|
|
645
654
|
let changelog = "";
|
|
646
655
|
for await (const chunk of generator.write()) changelog += chunk;
|
|
647
656
|
return changelog;
|
|
648
657
|
};
|
|
649
|
-
const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
658
|
+
const generateChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
|
|
650
659
|
const infile = join(getPkgDir(), "CHANGELOG.md");
|
|
651
660
|
if (!existsSync(infile)) await writeFile(infile, "");
|
|
652
|
-
const
|
|
661
|
+
const exist = readFileSync(infile, "utf-8");
|
|
662
|
+
const writeStream = createWriteStream(infile);
|
|
653
663
|
const generator = await createGenerator({
|
|
654
664
|
getPkgDir,
|
|
655
|
-
tagPrefix
|
|
665
|
+
tagPrefix,
|
|
666
|
+
releaseCount
|
|
656
667
|
});
|
|
657
|
-
const writeStream = createWriteStream(infile);
|
|
658
668
|
for await (const chunk of generator.write()) writeStream.write(chunk);
|
|
659
|
-
|
|
669
|
+
const override = isZero(releaseCount);
|
|
670
|
+
if (!(isZero(exist.trim().length) || override)) {
|
|
671
|
+
writeStream.write(eol());
|
|
672
|
+
writeStream.write(exist);
|
|
673
|
+
}
|
|
660
674
|
writeStream.end();
|
|
661
675
|
await finished(writeStream);
|
|
662
676
|
};
|
|
@@ -674,14 +688,18 @@ const runGitPrompts = async (ctx, config) => {
|
|
|
674
688
|
return Promise.resolve(commit);
|
|
675
689
|
})();
|
|
676
690
|
},
|
|
677
|
-
tag: () => {
|
|
678
|
-
return shouldPrompt(tag) ? confirm({ message: question(MSG.PROMPT.GIT_TAG(currentTag), "git") }) : (() => {
|
|
691
|
+
tag: ({ results: { commit } }) => {
|
|
692
|
+
return shouldPrompt(tag) ? commit ? confirm({ message: question(MSG.PROMPT.GIT_TAG(currentTag), "git") }) : (() => {
|
|
693
|
+
return Promise.resolve(false);
|
|
694
|
+
})() : (() => {
|
|
679
695
|
logs.push(yellow`git.tag`);
|
|
680
696
|
return Promise.resolve(tag);
|
|
681
697
|
})();
|
|
682
698
|
},
|
|
683
|
-
push: () => {
|
|
684
|
-
return shouldPrompt(push) ? confirm({ message: question(MSG.PROMPT.GIT_PUSH, "git") }) : (() => {
|
|
699
|
+
push: ({ results: { commit, tag } }) => {
|
|
700
|
+
return shouldPrompt(push) ? commit || tag ? confirm({ message: question(MSG.PROMPT.GIT_PUSH, "git") }) : (() => {
|
|
701
|
+
return Promise.resolve(false);
|
|
702
|
+
})() : (() => {
|
|
685
703
|
logs.push(yellow`git.push`);
|
|
686
704
|
return Promise.resolve(push);
|
|
687
705
|
})();
|
|
@@ -771,7 +789,7 @@ const createCReleaseByTag = async (octokit, options) => {
|
|
|
771
789
|
const existing = await getReleaseByTagSafe(octokit, options);
|
|
772
790
|
if (!existing) return createCRelease(octokit, { ...options });
|
|
773
791
|
const { draft, id: releaseId } = existing;
|
|
774
|
-
if (!draft) throw new Error(
|
|
792
|
+
if (!draft) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(options.tag));
|
|
775
793
|
return updateCRelease(octokit, {
|
|
776
794
|
releaseId,
|
|
777
795
|
...options
|
|
@@ -914,15 +932,32 @@ const runNpmOptPrompts = async (ctx, config) => {
|
|
|
914
932
|
};
|
|
915
933
|
//#endregion
|
|
916
934
|
//#region src/npm/publish.ts
|
|
935
|
+
const canPublish = async (registry) => {
|
|
936
|
+
const res = await runNpm([
|
|
937
|
+
"publish",
|
|
938
|
+
"--dry-run",
|
|
939
|
+
"--no-git-checks",
|
|
940
|
+
"--access",
|
|
941
|
+
"public",
|
|
942
|
+
...registryArg(registry)
|
|
943
|
+
], { error: "throw" }).catch((err) => err);
|
|
944
|
+
if (!(res instanceof Error)) return true;
|
|
945
|
+
return [/previously published versions/i, /cannot publish over/i].some((reg) => reg.test(res.message));
|
|
946
|
+
};
|
|
917
947
|
const npmCheck = async (ctx, config) => {
|
|
918
948
|
const { pkg: { name, current, publishConfig: { registry } } } = ctx;
|
|
919
949
|
const { npm: { skipChecks } } = config;
|
|
920
950
|
const tag = await resolvePublishTag(name, current);
|
|
921
951
|
Object.assign(ctx.npm, { tag });
|
|
922
952
|
if (skipChecks) return "";
|
|
923
|
-
const [pinged, username] = await Promise.all([
|
|
953
|
+
const [pinged, username, canPublished] = await Promise.all([
|
|
954
|
+
pingRegistry(registry),
|
|
955
|
+
getAuthenticatedUser(registry),
|
|
956
|
+
canPublish(registry)
|
|
957
|
+
]);
|
|
924
958
|
if (!pinged) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
|
|
925
959
|
if (!username) throw new Error(MSG.ERROR.NPM_AUTH);
|
|
960
|
+
if (!canPublished) throw new Error(MSG.ERROR.NPM_PERMISSION);
|
|
926
961
|
Object.assign(ctx.npm, { username });
|
|
927
962
|
const latest = await getPublishedVersion(`${name}@${tag}`);
|
|
928
963
|
if (latest) {
|
|
@@ -1072,7 +1107,7 @@ var Action = class {
|
|
|
1072
1107
|
}
|
|
1073
1108
|
async bumpTask(ctx, config) {
|
|
1074
1109
|
const { pkg: { current }, dryRun, isCI, showRelease, showChangelog, selectedPkg, noGit } = ctx;
|
|
1075
|
-
const { isMonorepo, hooks, getPkgDir, tagPrefix } = config;
|
|
1110
|
+
const { isMonorepo, hooks, getPkgDir, changelog: { tagPrefix, releaseCount } } = config;
|
|
1076
1111
|
const need = (ctx) => {
|
|
1077
1112
|
const { pkg: { next }, isIncrement } = ctx;
|
|
1078
1113
|
return !next && isIncrement;
|
|
@@ -1102,11 +1137,16 @@ var Action = class {
|
|
|
1102
1137
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1103
1138
|
await runLifeCycleHook(hooks, "after:bump", dryRun);
|
|
1104
1139
|
if (noGit) return;
|
|
1105
|
-
const
|
|
1140
|
+
const match = isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*";
|
|
1141
|
+
const { from, to } = isZero(releaseCount) ? {
|
|
1142
|
+
from: "",
|
|
1143
|
+
to: "HEAD"
|
|
1144
|
+
} : await resolveChangelogRange(isIncrement, match);
|
|
1106
1145
|
const commits = await getLog(from, to, getPkgDir(selectedPkg));
|
|
1107
1146
|
const changelog = await getChangelog({
|
|
1108
1147
|
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1109
|
-
tagPrefix: tagPrefix?.(selectedPkg)
|
|
1148
|
+
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1149
|
+
releaseCount
|
|
1110
1150
|
});
|
|
1111
1151
|
Object.assign(ctx.github, { changelog });
|
|
1112
1152
|
if (commits) {
|
|
@@ -1117,14 +1157,15 @@ var Action = class {
|
|
|
1117
1157
|
}
|
|
1118
1158
|
async changelogTask(ctx, config) {
|
|
1119
1159
|
const { isIncrement, dryRun, selectedPkg, noGit } = ctx;
|
|
1120
|
-
const { getPkgDir, tagPrefix } = config;
|
|
1160
|
+
const { getPkgDir, changelog: { tagPrefix, releaseCount } } = config;
|
|
1121
1161
|
if (noGit) return;
|
|
1122
1162
|
await tasks([{
|
|
1123
1163
|
title: MSG.TASK.CHANGELOG.START,
|
|
1124
1164
|
task: async () => {
|
|
1125
1165
|
await generateChangelog({
|
|
1126
1166
|
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1127
|
-
tagPrefix: tagPrefix?.(selectedPkg)
|
|
1167
|
+
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1168
|
+
releaseCount
|
|
1128
1169
|
});
|
|
1129
1170
|
await gitAddAll();
|
|
1130
1171
|
return success(MSG.TASK.CHANGELOG.END, dryRun);
|
|
@@ -1145,7 +1186,8 @@ var Action = class {
|
|
|
1145
1186
|
title: MSG.TASK.GIT.START,
|
|
1146
1187
|
task: async () => {
|
|
1147
1188
|
await commitAndTag(ctx, config);
|
|
1148
|
-
|
|
1189
|
+
const { git: { push } } = config;
|
|
1190
|
+
return success(MSG.TASK.GIT.END(Boolean(push)), dryRun);
|
|
1149
1191
|
}
|
|
1150
1192
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1151
1193
|
await runLifeCycleHook(hooks, "after:push", dryRun);
|
|
@@ -1165,7 +1207,8 @@ var Action = class {
|
|
|
1165
1207
|
await publishNpm(ctx, config);
|
|
1166
1208
|
} else throw err;
|
|
1167
1209
|
});
|
|
1168
|
-
const
|
|
1210
|
+
const { npm: { publish } } = config;
|
|
1211
|
+
const url = publish ? getPackageUrl(name, next) : void 0;
|
|
1169
1212
|
return success(MSG.TASK.NPM.END(url), dryRun);
|
|
1170
1213
|
}
|
|
1171
1214
|
}]).catch((err) => abortOnError(err, ctx, false));
|
|
@@ -1180,9 +1223,10 @@ var Action = class {
|
|
|
1180
1223
|
await tasks([{
|
|
1181
1224
|
title: MSG.TASK.GITHUB.START,
|
|
1182
1225
|
task: async () => {
|
|
1183
|
-
const { github: { owner, repo }, git: { currentTag } } = ctx;
|
|
1184
1226
|
await createRelease(ctx, config);
|
|
1185
|
-
const
|
|
1227
|
+
const { github: { owner, repo }, git: { currentTag } } = ctx;
|
|
1228
|
+
const { github: { release } } = config;
|
|
1229
|
+
const url = release ? getGithubReleaseUrl(owner, repo, currentTag) : void 0;
|
|
1186
1230
|
return success(MSG.TASK.GITHUB.END(url), dryRun);
|
|
1187
1231
|
}
|
|
1188
1232
|
}]).catch((err) => abortOnError(err, ctx, false));
|
|
@@ -1214,7 +1258,9 @@ program.command("init").description("Create a release configuration file").optio
|
|
|
1214
1258
|
packages: [ 'demo-a', 'demo-b' ],
|
|
1215
1259
|
getPkgDir: (pkg) => \`packages/\${ pkg }\`,
|
|
1216
1260
|
toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
|
|
1217
|
-
|
|
1261
|
+
changelog: {
|
|
1262
|
+
tagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1263
|
+
},
|
|
1218
1264
|
}`;
|
|
1219
1265
|
writeFileSync(configPath, [`import { defineConfig } from '@peiyanlu/release'`, `export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`].join(eol(2)), "utf-8");
|
|
1220
1266
|
console.log(`${green`✔`} ${infile} created successfully`);
|
package/dist/index.d.mts
CHANGED
|
@@ -23,14 +23,20 @@ interface ReleaseConfig {
|
|
|
23
23
|
getPkgDir: (pkg: string) => string;
|
|
24
24
|
/** 使用 monorepo 时使用 toTag,例如 `${pkg}@${version}`,默认为 `v${version}` 作为单仓库 */
|
|
25
25
|
toTag: (pkg: string, version: string) => string;
|
|
26
|
-
/**
|
|
27
|
-
|
|
26
|
+
/**
|
|
27
|
+
* CHANGELOG 相关配置
|
|
28
|
+
* 影响生成的日志数量、日志标题(monorepo)。
|
|
29
|
+
*/
|
|
30
|
+
changelog: {
|
|
31
|
+
/** 作为 CHANGELOG 标题的 Tag 前缀,多包场景下需要 `${pkg}@` */tagPrefix: ((pkg: string) => string); /** 生成多少个版本的变更日志,详见 {@link Options} */
|
|
32
|
+
releaseCount: number;
|
|
33
|
+
};
|
|
28
34
|
/** 忽略 git、github 环境检查以及后续操作 */
|
|
29
|
-
ignoreGit: boolean
|
|
35
|
+
ignoreGit: boolean;
|
|
30
36
|
/** 忽略 npm 环境检查以及后续操作 */
|
|
31
|
-
ignoreNpm: boolean
|
|
37
|
+
ignoreNpm: boolean;
|
|
32
38
|
/** 忽略 github 环境检查以及后续操作 */
|
|
33
|
-
ignoreGithub: boolean
|
|
39
|
+
ignoreGithub: boolean;
|
|
34
40
|
/**
|
|
35
41
|
* Git 相关配置
|
|
36
42
|
* 影响提交、打 tag、推送等本地/远程 Git 行为。
|
|
@@ -38,6 +44,7 @@ interface ReleaseConfig {
|
|
|
38
44
|
git: {
|
|
39
45
|
/**
|
|
40
46
|
* 是否创建 Git 提交
|
|
47
|
+
* 影响创建 Tag 和推送
|
|
41
48
|
*/
|
|
42
49
|
commit: boolean | undefined;
|
|
43
50
|
/**
|
|
@@ -155,27 +162,32 @@ declare function defineConfig(config: UserConfigFnObject): UserConfigFnObject;
|
|
|
155
162
|
declare function defineConfig(config: UserConfigFnPromise): UserConfigFnPromise;
|
|
156
163
|
declare function defineConfig(config: UserConfigFn): UserConfigFn;
|
|
157
164
|
declare function defineConfig(config: UserConfigExport): UserConfigExport;
|
|
158
|
-
declare const mergeConfig: <T extends object>(defaults: DeepRequired<T>, overrides
|
|
165
|
+
declare const mergeConfig: <T extends object>(defaults: DeepRequired<T>, overrides: DeepPartial<T>) => DeepRequired<T>;
|
|
159
166
|
//#endregion
|
|
160
167
|
//#region src/git/changelog.d.ts
|
|
161
|
-
interface
|
|
168
|
+
interface GenerateOptions {
|
|
162
169
|
/** @example () => `packages/${pkg}` */
|
|
163
170
|
getPkgDir: () => string;
|
|
164
171
|
/** @example `${pkg}@` */
|
|
165
172
|
tagPrefix?: string;
|
|
173
|
+
/** 生成多少个变更日志,详见 {@link Options} */
|
|
174
|
+
releaseCount?: number;
|
|
166
175
|
}
|
|
167
176
|
declare const createGenerator: ({
|
|
168
177
|
getPkgDir,
|
|
169
|
-
tagPrefix
|
|
170
|
-
|
|
178
|
+
tagPrefix,
|
|
179
|
+
releaseCount
|
|
180
|
+
}: GenerateOptions) => Promise<ConventionalChangelog>;
|
|
171
181
|
declare const getChangelog: ({
|
|
172
182
|
getPkgDir,
|
|
173
|
-
tagPrefix
|
|
174
|
-
|
|
183
|
+
tagPrefix,
|
|
184
|
+
releaseCount
|
|
185
|
+
}: GenerateOptions) => Promise<string>;
|
|
175
186
|
declare const generateChangelog: ({
|
|
176
187
|
getPkgDir,
|
|
177
|
-
tagPrefix
|
|
178
|
-
|
|
188
|
+
tagPrefix,
|
|
189
|
+
releaseCount
|
|
190
|
+
}: GenerateOptions) => Promise<void>;
|
|
179
191
|
//#endregion
|
|
180
192
|
//#region src/npm/publish.d.ts
|
|
181
193
|
interface PublishTagOptions {
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isFunction, isPlainObject } from "@peiyanlu/ts-utils";
|
|
1
|
+
import { isFunction, isPlainObject, isUndefined, isZero } from "@peiyanlu/ts-utils";
|
|
2
2
|
import { createWriteStream, existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { eol, getPackageInfo, publishPackage, resolvePublishTag } from "@peiyanlu/cli-utils";
|
|
@@ -12,13 +12,12 @@ function defineConfig(config) {
|
|
|
12
12
|
return config;
|
|
13
13
|
}
|
|
14
14
|
const mergeConfig = (defaults, overrides) => {
|
|
15
|
-
if (!overrides) return structuredClone(defaults);
|
|
16
|
-
if (Object.keys(defaults).length === 0) return structuredClone(overrides);
|
|
17
15
|
const result = {};
|
|
18
|
-
for (const key
|
|
16
|
+
for (const key of Object.keys(defaults)) {
|
|
19
17
|
const defaultValue = defaults[key];
|
|
20
18
|
const overrideValue = overrides[key];
|
|
21
|
-
if (overrideValue
|
|
19
|
+
if (isUndefined(overrideValue)) if (isFunction(defaultValue)) result[key] = defaultValue;
|
|
20
|
+
else if (isPlainObject(defaultValue)) result[key] = mergeConfig(defaultValue, {});
|
|
22
21
|
else result[key] = structuredClone(defaultValue);
|
|
23
22
|
else if (isPlainObject(defaultValue) && isPlainObject(overrideValue)) result[key] = mergeConfig(defaultValue, overrideValue);
|
|
24
23
|
else if (isFunction(overrideValue)) result[key] = overrideValue;
|
|
@@ -66,42 +65,42 @@ const defaultTypes = [
|
|
|
66
65
|
},
|
|
67
66
|
{
|
|
68
67
|
type: "chore",
|
|
69
|
-
section: "
|
|
68
|
+
section: "🧹 其他更新",
|
|
70
69
|
description: "非功能性更改"
|
|
71
70
|
},
|
|
72
71
|
{
|
|
73
72
|
type: "refactor",
|
|
74
|
-
section: "
|
|
73
|
+
section: "♻️ 代码重构",
|
|
75
74
|
description: "重构代码"
|
|
76
75
|
},
|
|
77
76
|
{
|
|
78
77
|
type: "test",
|
|
79
|
-
section: "
|
|
78
|
+
section: "🧪 测试",
|
|
80
79
|
description: "测试更新"
|
|
81
80
|
},
|
|
82
81
|
{
|
|
83
82
|
type: "build",
|
|
84
|
-
section: "
|
|
83
|
+
section: "🏗️ 构建系统",
|
|
85
84
|
description: "构建工具和系统修改"
|
|
86
85
|
},
|
|
87
86
|
{
|
|
88
87
|
type: "ci",
|
|
89
|
-
section: "
|
|
88
|
+
section: "🤖 CI",
|
|
90
89
|
description: "持续集成配置"
|
|
91
90
|
},
|
|
92
91
|
{
|
|
93
92
|
type: "config",
|
|
94
|
-
section: "
|
|
93
|
+
section: "⚙️ 配置",
|
|
95
94
|
description: "配置文件更新"
|
|
96
95
|
},
|
|
97
96
|
{
|
|
98
97
|
type: "deps",
|
|
99
|
-
section: "
|
|
98
|
+
section: "📦 依赖更新",
|
|
100
99
|
description: "依赖版本变更"
|
|
101
100
|
},
|
|
102
101
|
{
|
|
103
102
|
type: "security",
|
|
104
|
-
section: "
|
|
103
|
+
section: "🛡️ 安全修复",
|
|
105
104
|
description: "安全性更新"
|
|
106
105
|
},
|
|
107
106
|
{
|
|
@@ -111,13 +110,18 @@ const defaultTypes = [
|
|
|
111
110
|
},
|
|
112
111
|
{
|
|
113
112
|
type: "ux",
|
|
114
|
-
section: "
|
|
113
|
+
section: "🎨 用户体验",
|
|
115
114
|
description: "用户体验改进"
|
|
116
115
|
},
|
|
117
116
|
{
|
|
118
117
|
type: "hotfix",
|
|
119
|
-
section: "
|
|
118
|
+
section: "🚑 热修复",
|
|
120
119
|
description: "紧急修复"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "tool",
|
|
123
|
+
section: "🛠️ 工具链",
|
|
124
|
+
description: "工具链变更"
|
|
121
125
|
}
|
|
122
126
|
];
|
|
123
127
|
//#endregion
|
|
@@ -174,34 +178,40 @@ const parsePreset = async () => {
|
|
|
174
178
|
{{#each commits}}
|
|
175
179
|
{{> commit root=@root}}
|
|
176
180
|
{{/each}}
|
|
177
|
-
{{/each}}`.trim()
|
|
181
|
+
{{/each}}`.trim();
|
|
178
182
|
return preset;
|
|
179
183
|
};
|
|
180
|
-
const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
184
|
+
const createGenerator = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
|
|
181
185
|
const pkgDir = getPkgDir();
|
|
182
186
|
const preset = await parsePreset();
|
|
183
|
-
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount
|
|
187
|
+
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
184
188
|
};
|
|
185
|
-
const getChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
189
|
+
const getChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
|
|
186
190
|
const generator = await createGenerator({
|
|
187
191
|
getPkgDir,
|
|
188
|
-
tagPrefix
|
|
192
|
+
tagPrefix,
|
|
193
|
+
releaseCount
|
|
189
194
|
});
|
|
190
195
|
let changelog = "";
|
|
191
196
|
for await (const chunk of generator.write()) changelog += chunk;
|
|
192
197
|
return changelog;
|
|
193
198
|
};
|
|
194
|
-
const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
199
|
+
const generateChangelog = async ({ getPkgDir, tagPrefix, releaseCount = 1 }) => {
|
|
195
200
|
const infile = join(getPkgDir(), "CHANGELOG.md");
|
|
196
201
|
if (!existsSync(infile)) await writeFile(infile, "");
|
|
197
|
-
const
|
|
202
|
+
const exist = readFileSync(infile, "utf-8");
|
|
203
|
+
const writeStream = createWriteStream(infile);
|
|
198
204
|
const generator = await createGenerator({
|
|
199
205
|
getPkgDir,
|
|
200
|
-
tagPrefix
|
|
206
|
+
tagPrefix,
|
|
207
|
+
releaseCount
|
|
201
208
|
});
|
|
202
|
-
const writeStream = createWriteStream(infile);
|
|
203
209
|
for await (const chunk of generator.write()) writeStream.write(chunk);
|
|
204
|
-
|
|
210
|
+
const override = isZero(releaseCount);
|
|
211
|
+
if (!(isZero(exist.trim().length) || override)) {
|
|
212
|
+
writeStream.write(eol());
|
|
213
|
+
writeStream.write(exist);
|
|
214
|
+
}
|
|
205
215
|
writeStream.end();
|
|
206
216
|
await finished(writeStream);
|
|
207
217
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peiyanlu/release",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.15",
|
|
4
4
|
"description": "Release project tool.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"dev": "tsdown -w",
|
|
22
22
|
"build": "tsdown",
|
|
23
|
-
"
|
|
23
|
+
"prepublishOnly": "tsdown",
|
|
24
24
|
"release": "node index.js",
|
|
25
25
|
"test": "vitest run",
|
|
26
26
|
"test:cov": "vitest run --coverage"
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
"@actions/github": "^9.1.1",
|
|
31
31
|
"@clack/prompts": "^1.5.0",
|
|
32
32
|
"@octokit/rest": "^22.0.1",
|
|
33
|
-
"@peiyanlu/cli-utils": "^0.0.
|
|
34
|
-
"@peiyanlu/ts-utils": "^0.0.
|
|
33
|
+
"@peiyanlu/cli-utils": "^0.0.9",
|
|
34
|
+
"@peiyanlu/ts-utils": "^0.0.7",
|
|
35
35
|
"ansis": "^4.3.1",
|
|
36
36
|
"commander": "^15.0.0",
|
|
37
37
|
"conventional-changelog": "^7.2.0",
|