@peiyanlu/release 1.2.1 → 1.3.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/dist/cli.mjs +116 -72
- package/dist/index.d.mts +18 -12
- package/dist/index.mjs +14 -5
- package/package.json +4 -3
package/dist/cli.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { bold, compareUrl, each, heading, link, list, newline, segments, small, words } from "@conventional-changelog/template";
|
|
2
2
|
import { bumpPackageVersion, 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, registryArg, resolveChangelogRange, resolvePublishTag, runNpm } from "@peiyanlu/cli-utils";
|
|
3
|
-
import { copyDir, createMatcher, createTempDir, editFile, readJsonFile, readJsonFileSync, writeJsonFile } from "@peiyanlu/node-utils";
|
|
3
|
+
import { copyDir, createMatcher, createTempDir, editFile, normalizePath, readJsonFile, readJsonFileSync, writeJsonFile } from "@peiyanlu/node-utils";
|
|
4
|
+
import { castArray, dedent, isBoolean, isEmpty, isEmptyObject, isFunction, isNotEmpty, isNumber, isPlainObject, isUndefined, isZero, nonNullable, omit, partition, unset } from "@peiyanlu/ts-utils";
|
|
4
5
|
import { dim, green, red, rgb, underline, yellow } from "ansis";
|
|
5
6
|
import { program } from "commander";
|
|
6
7
|
import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
7
|
-
import { join, resolve } from "node:path";
|
|
8
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
8
9
|
import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
|
|
9
|
-
import { castArray, isBoolean, isEmptyObject, isFunction, isNotEmpty, isNumber, isPlainObject, isUndefined, isZero, omit, partition, unset } from "@peiyanlu/ts-utils";
|
|
10
10
|
import { publint } from "publint";
|
|
11
11
|
import { formatMessage } from "publint/utils";
|
|
12
12
|
import { inc, neq } from "semver";
|
|
@@ -20,10 +20,11 @@ import { finished } from "node:stream/promises";
|
|
|
20
20
|
import { Octokit } from "@octokit/rest";
|
|
21
21
|
import { contentType } from "mime-types";
|
|
22
22
|
import open from "open";
|
|
23
|
-
import { glob } from "tinyglobby";
|
|
23
|
+
import { glob, globSync } from "tinyglobby";
|
|
24
|
+
import { parse } from "yaml";
|
|
24
25
|
//#region package.json
|
|
25
26
|
var name = "@peiyanlu/release";
|
|
26
|
-
var version = "1.2.
|
|
27
|
+
var version = "1.2.1";
|
|
27
28
|
var description = "Release project tool.";
|
|
28
29
|
//#endregion
|
|
29
30
|
//#region src/config.ts
|
|
@@ -92,11 +93,11 @@ const MSG = {
|
|
|
92
93
|
},
|
|
93
94
|
NPM: {
|
|
94
95
|
CHECKING: "Checking npm registry",
|
|
95
|
-
CHECKED: (registry, msg) => `NPM registry: ${underline(dim(registry))} ${
|
|
96
|
+
CHECKED: (registry, msg) => `NPM registry: ${underline(dim(registry))} ${dim(msg)}`
|
|
96
97
|
},
|
|
97
98
|
GITHUB: {
|
|
98
99
|
CHECKING: "Checking github repository",
|
|
99
|
-
CHECKED: (repository
|
|
100
|
+
CHECKED: (repository) => `GitHub repository: ${underline(dim(repository))}`
|
|
100
101
|
}
|
|
101
102
|
},
|
|
102
103
|
TASK: {
|
|
@@ -158,44 +159,25 @@ const MSG = {
|
|
|
158
159
|
//#region src/git/commit.ts
|
|
159
160
|
const gitCheck = async (ctx, config) => {
|
|
160
161
|
const { pkg: { name } } = ctx;
|
|
161
|
-
const { git: { requireRepository, requireRemote, requireCleanWorkingTree }
|
|
162
|
-
if (skipGit) {
|
|
163
|
-
ctx.noGit = true;
|
|
164
|
-
ctx.noGitHub = true;
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
162
|
+
const { git: { requireRepository, requireRemote, requireCleanWorkingTree } } = config;
|
|
167
163
|
if (!await isGitRepo()) if (requireRepository) throw new Error(MSG.ERROR.GIT_REGISTRY(name));
|
|
168
164
|
else {
|
|
169
165
|
ctx.noGit = true;
|
|
170
|
-
ctx.noGitHub = true;
|
|
171
166
|
return;
|
|
172
167
|
}
|
|
173
168
|
if (requireCleanWorkingTree && !await isWorkingTreeClean()) throw new Error(MSG.ERROR.GIT_WORKDIR);
|
|
174
|
-
if (skipGithub) {
|
|
175
|
-
ctx.noGitHub = true;
|
|
176
|
-
return;
|
|
177
|
-
}
|
|
178
169
|
const remote = (await getRemoteList())[0];
|
|
179
170
|
const { name: remoteName = "origin", url: remoteUrl } = remote ?? {};
|
|
180
|
-
if (!remote || !remoteUrl)
|
|
181
|
-
|
|
182
|
-
ctx.noGitHub = true;
|
|
183
|
-
return;
|
|
171
|
+
if (!remote || !remoteUrl) {
|
|
172
|
+
if (requireRemote) throw new Error(MSG.ERROR.GIT_REMOTE(remoteName));
|
|
184
173
|
}
|
|
185
174
|
Object.assign(ctx.git, {
|
|
186
175
|
remoteName,
|
|
187
176
|
remoteUrl
|
|
188
177
|
});
|
|
189
|
-
const [owner, repo] = parseGitHubRepo(remoteUrl);
|
|
190
|
-
const url = getGithubUrl(owner, repo);
|
|
191
|
-
Object.assign(ctx.github, {
|
|
192
|
-
owner,
|
|
193
|
-
repo,
|
|
194
|
-
url
|
|
195
|
-
});
|
|
196
178
|
};
|
|
197
179
|
const commitAndTag = async (ctx, config) => {
|
|
198
|
-
const { git: {
|
|
180
|
+
const { git: { currentTag, commitMessage, tagMessage }, dryRun } = ctx;
|
|
199
181
|
const { git: { commit, tag, push, addUntrackedFiles, commitArgs, tagArgs, pushArgs } } = config;
|
|
200
182
|
if (!commit) return;
|
|
201
183
|
const args = dryRun ? ["--dry-run"] : [];
|
|
@@ -384,7 +366,8 @@ const createDefaultContext = () => {
|
|
|
384
366
|
registry: "https://registry.npmjs.org"
|
|
385
367
|
},
|
|
386
368
|
preId: "beta",
|
|
387
|
-
preBase: "0"
|
|
369
|
+
preBase: "0",
|
|
370
|
+
scope: void 0
|
|
388
371
|
},
|
|
389
372
|
npm: {
|
|
390
373
|
username: "",
|
|
@@ -483,7 +466,8 @@ const createDefaultConfig = (isCI) => {
|
|
|
483
466
|
draft: false,
|
|
484
467
|
tokenRef: "GITHUB_TOKEN",
|
|
485
468
|
assets: [],
|
|
486
|
-
skipChecks: false
|
|
469
|
+
skipChecks: false,
|
|
470
|
+
shortNotes: false
|
|
487
471
|
}
|
|
488
472
|
};
|
|
489
473
|
};
|
|
@@ -613,6 +597,10 @@ function headerPartial(context) {
|
|
|
613
597
|
const fullTitle = words(linkCompare && !isLikelyHash(previousTag) ? link(version, compareUrl(context)) : version, title && `"${title}"`, date && `(${date})`);
|
|
614
598
|
return heading(2, isPatch ? small(fullTitle) : fullTitle);
|
|
615
599
|
}
|
|
600
|
+
function preamblePartial(contest) {
|
|
601
|
+
const { preamble, commitGroups, noteGroups } = contest;
|
|
602
|
+
return segments(preamble, isEmpty(preamble) && isEmpty(commitGroups) && isEmpty(noteGroups) && "Version bump without any changes.");
|
|
603
|
+
}
|
|
616
604
|
function template(context) {
|
|
617
605
|
const { headerPartial, preamblePartial, commitPartial, footerPartial, noteGroups, commitGroups } = context;
|
|
618
606
|
return segments(headerPartial(context), preamblePartial(context), each(noteGroups, (group) => segments(heading(3, words("⚠", group.title)), list(group.notes, (note) => {
|
|
@@ -622,14 +610,18 @@ function template(context) {
|
|
|
622
610
|
//#endregion
|
|
623
611
|
//#region src/git/changelog.ts
|
|
624
612
|
const parsePreset = (options = {}) => {
|
|
625
|
-
const { includeHidden, transformTypes } = options;
|
|
613
|
+
const { includeHidden, transformTypes, scope } = options;
|
|
626
614
|
let types = [...defaultTypes];
|
|
627
615
|
if (includeHidden) types.forEach((type) => {
|
|
628
616
|
if (type.effect === "hidden") type.effect = "changelog";
|
|
629
617
|
});
|
|
630
618
|
types = transformTypes?.(types) ?? types;
|
|
631
|
-
const preset = createPreset({
|
|
619
|
+
const preset = createPreset({
|
|
620
|
+
types,
|
|
621
|
+
scope
|
|
622
|
+
});
|
|
632
623
|
preset.writer ??= {};
|
|
624
|
+
preset.writer.preamblePartial = preamblePartial;
|
|
633
625
|
preset.writer.headerPartial = headerPartial;
|
|
634
626
|
preset.writer.template = template;
|
|
635
627
|
return preset;
|
|
@@ -645,11 +637,12 @@ const inferReleaseType = async (options) => {
|
|
|
645
637
|
} : releaseType;
|
|
646
638
|
};
|
|
647
639
|
const createGenerator = async (options) => {
|
|
648
|
-
const { getPkgDir, tagPrefix, releaseCount = 1, includeHidden, transformTypes } = options;
|
|
640
|
+
const { getPkgDir, tagPrefix, releaseCount = 1, includeHidden, transformTypes, scope } = options;
|
|
649
641
|
const pkgDir = getPkgDir();
|
|
650
642
|
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(parsePreset({
|
|
651
643
|
includeHidden,
|
|
652
|
-
transformTypes
|
|
644
|
+
transformTypes,
|
|
645
|
+
scope
|
|
653
646
|
})).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
654
647
|
};
|
|
655
648
|
const getChangelog = async (options) => {
|
|
@@ -677,10 +670,10 @@ const generateChangelog = async (options) => {
|
|
|
677
670
|
//#endregion
|
|
678
671
|
//#region src/git/prompts.ts
|
|
679
672
|
const runGitPrompts = async (ctx, config) => {
|
|
680
|
-
const { git: { currentTag, commitMessage }, configFileExists: cfe, dryRun } = ctx;
|
|
673
|
+
const { git: { currentTag, commitMessage }, configFileExists: cfe, dryRun, isCI } = ctx;
|
|
681
674
|
const { git: { commit, tag, push } } = config;
|
|
682
675
|
const logs = [];
|
|
683
|
-
const shouldPrompt = (val) => isUndefined(val) || dryRun;
|
|
676
|
+
const shouldPrompt = (val) => isUndefined(val) || dryRun && !isCI;
|
|
684
677
|
const { log: _, ...reset } = await group({
|
|
685
678
|
commit: () => {
|
|
686
679
|
return shouldPrompt(commit) ? confirm({ message: question(MSG.PROMPT.GIT_COMMIT(commitMessage), "git") }) : (() => {
|
|
@@ -716,10 +709,10 @@ const runGitPrompts = async (ctx, config) => {
|
|
|
716
709
|
//#endregion
|
|
717
710
|
//#region src/github/prompts.ts
|
|
718
711
|
const runGithubPrompts = async (ctx, config) => {
|
|
719
|
-
const { configFileExists: cfe, dryRun, github: { releaseName } } = ctx;
|
|
712
|
+
const { configFileExists: cfe, dryRun, github: { releaseName }, isCI } = ctx;
|
|
720
713
|
const { github: { release } } = config;
|
|
721
714
|
const logs = [];
|
|
722
|
-
const shouldPrompt = (val) => isUndefined(val) || dryRun;
|
|
715
|
+
const shouldPrompt = (val) => isUndefined(val) || dryRun && !isCI;
|
|
723
716
|
const res = await group({
|
|
724
717
|
release: () => shouldPrompt(release) ? confirm({ message: question(MSG.PROMPT.GITHUB_RELEASE(releaseName), "github") }) : (() => {
|
|
725
718
|
logs.push(yellow`github.release`);
|
|
@@ -824,24 +817,33 @@ const createWebRelease = async (options) => {
|
|
|
824
817
|
}).forEach(([key, value]) => {
|
|
825
818
|
url.searchParams.set(key, value);
|
|
826
819
|
});
|
|
827
|
-
|
|
828
|
-
await open(url.toString(), { wait: isWindows });
|
|
820
|
+
await open(url.toString());
|
|
829
821
|
};
|
|
822
|
+
const isGitHubActions = Boolean(process.env.GITHUB_ACTIONS);
|
|
830
823
|
const githubCheck = async (ctx, config) => {
|
|
831
|
-
const {
|
|
824
|
+
const { git: { remoteUrl } } = ctx;
|
|
832
825
|
const { github: { tokenRef, skipChecks } } = config;
|
|
826
|
+
if (!remoteUrl || isEmpty(parseGitHubRepo(remoteUrl))) {
|
|
827
|
+
ctx.noGitHub = true;
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
const [owner, repo] = parseGitHubRepo(remoteUrl);
|
|
831
|
+
const url = getGithubUrl(owner, repo);
|
|
832
|
+
Object.assign(ctx.github, {
|
|
833
|
+
owner,
|
|
834
|
+
repo,
|
|
835
|
+
url
|
|
836
|
+
});
|
|
833
837
|
const token = tokenRef ? process.env[tokenRef] : process.env.GITHUB_TOKEN;
|
|
834
838
|
if (!token) {
|
|
835
839
|
Object.assign(ctx.github, { isWeb: true });
|
|
836
|
-
if (
|
|
837
|
-
return
|
|
840
|
+
if (isGitHubActions) throw new Error(MSG.ERROR.GITHUB_TOKEN(tokenRef));
|
|
841
|
+
return;
|
|
838
842
|
}
|
|
839
843
|
Object.assign(ctx.github, { token });
|
|
840
|
-
if (skipChecks)
|
|
841
|
-
let username = "";
|
|
842
|
-
if (process.env.GITHUB_ACTIONS) username = process.env.GITHUB_ACTOR;
|
|
843
|
-
else {
|
|
844
|
+
if (!isGitHubActions && !skipChecks) {
|
|
844
845
|
const octokit = new Octokit({ auth: token });
|
|
846
|
+
let username = "";
|
|
845
847
|
try {
|
|
846
848
|
const { data: { login } } = await octokit.users.getAuthenticated();
|
|
847
849
|
username = login;
|
|
@@ -858,7 +860,6 @@ const githubCheck = async (ctx, config) => {
|
|
|
858
860
|
throw new Error(MSG.ERROR.GITHUB_USER);
|
|
859
861
|
}
|
|
860
862
|
}
|
|
861
|
-
return username;
|
|
862
863
|
};
|
|
863
864
|
const createRelease = async (ctx, config) => {
|
|
864
865
|
const { pkg: { name, toPreRelease }, git: { currentTag }, isCI, dryRun } = ctx;
|
|
@@ -903,11 +904,11 @@ const createRelease = async (ctx, config) => {
|
|
|
903
904
|
//#endregion
|
|
904
905
|
//#region src/npm/prompts.ts
|
|
905
906
|
const runNpmPublishPrompts = async (ctx, config) => {
|
|
906
|
-
const { pkg: { name, next }, configFileExists: cfe, dryRun } = ctx;
|
|
907
|
+
const { pkg: { name, next }, configFileExists: cfe, dryRun, isCI } = ctx;
|
|
907
908
|
const { npm: { publish } } = config;
|
|
908
909
|
const logs = [];
|
|
909
910
|
const target = `${name}@${next}`;
|
|
910
|
-
const shouldPrompt = (val) => isUndefined(val) || dryRun;
|
|
911
|
+
const shouldPrompt = (val) => isUndefined(val) || dryRun && !isCI;
|
|
911
912
|
const res = await group({
|
|
912
913
|
publish: () => shouldPrompt(publish) ? confirm({ message: question(MSG.PROMPT.NPM_PUBLISH(target), "npm") }) : (() => {
|
|
913
914
|
logs.push(yellow`npm.publish`);
|
|
@@ -1132,7 +1133,7 @@ var Action = class {
|
|
|
1132
1133
|
console.log();
|
|
1133
1134
|
const { otp, package: defPkg, prepare, showChangelog, showRelease, ci, dryRun, onlyChangelog } = options;
|
|
1134
1135
|
intro(prepare ? MSG.INTRO_PREPARE(dryRun) : MSG.INTRO(dryRun));
|
|
1135
|
-
const { isMonorepo, packages, getPkgDir, skipNpm } = config;
|
|
1136
|
+
const { isMonorepo, packages, getPkgDir, skipGit, skipNpm, skipGithub } = config;
|
|
1136
1137
|
if (isMonorepo) {
|
|
1137
1138
|
if (ci) {
|
|
1138
1139
|
if (!defPkg) abortTask(MSG.ABORT.MONOREPO_CI_NO_PACKAGE);
|
|
@@ -1177,9 +1178,12 @@ var Action = class {
|
|
|
1177
1178
|
current: pkgVersion,
|
|
1178
1179
|
next: nextVersion,
|
|
1179
1180
|
toPreRelease: false,
|
|
1180
|
-
publishConfig: { ...publishConfig }
|
|
1181
|
+
publishConfig: { ...publishConfig },
|
|
1182
|
+
scope: isEmpty(selectedPkg) ? void 0 : selectedPkg
|
|
1181
1183
|
},
|
|
1182
|
-
|
|
1184
|
+
noGit: skipGit,
|
|
1185
|
+
noNpm: pkgPrivate || skipNpm,
|
|
1186
|
+
noGitHub: skipGit || skipGithub
|
|
1183
1187
|
});
|
|
1184
1188
|
const { messages } = await publint({ pkgDir });
|
|
1185
1189
|
for (const message of messages) {
|
|
@@ -1220,8 +1224,9 @@ var Action = class {
|
|
|
1220
1224
|
return resolveConfig(cwd);
|
|
1221
1225
|
}
|
|
1222
1226
|
async printChangelog(ctx, config) {
|
|
1223
|
-
const { showChangelog, selectedPkg, noGit, isIncrement } = ctx;
|
|
1227
|
+
const { showChangelog, selectedPkg, noGit, isIncrement, pkg: { scope } } = ctx;
|
|
1224
1228
|
const { isMonorepo, getPkgDir, changelog: { tagPrefix, releaseCount, includeHidden, transformTypes } } = config;
|
|
1229
|
+
const { github: { shortNotes } } = config;
|
|
1225
1230
|
if (noGit) return;
|
|
1226
1231
|
const match = isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*";
|
|
1227
1232
|
const { from, to } = isZero(releaseCount) ? {
|
|
@@ -1232,11 +1237,13 @@ var Action = class {
|
|
|
1232
1237
|
const changelog = await getChangelog({
|
|
1233
1238
|
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1234
1239
|
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1240
|
+
scope: isEmpty(nonNullable([scope])) ? void 0 : nonNullable([scope]),
|
|
1235
1241
|
releaseCount,
|
|
1236
1242
|
includeHidden,
|
|
1237
1243
|
transformTypes
|
|
1238
1244
|
});
|
|
1239
|
-
|
|
1245
|
+
const short = `Please refer to [CHANGELOG.md](${getPkgDir(selectedPkg)}/CHANGELOG.md) for details.`;
|
|
1246
|
+
Object.assign(ctx.github, { changelog: shortNotes ? short : changelog });
|
|
1240
1247
|
if (commits) {
|
|
1241
1248
|
msg("GIT", `Changelog(${from}...${to}):${eol(2)}` + changelog);
|
|
1242
1249
|
msg("GIT", `Commits(${from}...${to}):${eol(2)}` + commits);
|
|
@@ -1252,7 +1259,8 @@ var Action = class {
|
|
|
1252
1259
|
await gitCheck(ctx, config);
|
|
1253
1260
|
const { git: { remoteName, remoteUrl } } = ctx;
|
|
1254
1261
|
return success(MSG.CHECK.GIT.CHECKED(remoteName, remoteUrl), dryRun);
|
|
1255
|
-
}
|
|
1262
|
+
},
|
|
1263
|
+
enabled: !ctx.noGit
|
|
1256
1264
|
},
|
|
1257
1265
|
{
|
|
1258
1266
|
title: MSG.CHECK.NPM.CHECKING,
|
|
@@ -1266,10 +1274,10 @@ var Action = class {
|
|
|
1266
1274
|
{
|
|
1267
1275
|
title: MSG.CHECK.GITHUB.CHECKING,
|
|
1268
1276
|
task: async () => {
|
|
1269
|
-
|
|
1277
|
+
await githubCheck(ctx, config);
|
|
1270
1278
|
const { github: { owner, repo } } = ctx;
|
|
1271
1279
|
const repository = getGithubUrl(owner, repo);
|
|
1272
|
-
return success(MSG.CHECK.GITHUB.CHECKED(repository
|
|
1280
|
+
return success(MSG.CHECK.GITHUB.CHECKED(repository), dryRun);
|
|
1273
1281
|
},
|
|
1274
1282
|
enabled: !(ctx.noGitHub || onlyChangelog)
|
|
1275
1283
|
}
|
|
@@ -1291,7 +1299,7 @@ var Action = class {
|
|
|
1291
1299
|
});
|
|
1292
1300
|
if (res) msg("BUMP", res.reason);
|
|
1293
1301
|
const inferred = res?.releaseType;
|
|
1294
|
-
const nextVersion = (isCI ?
|
|
1302
|
+
const nextVersion = (isCI ? inc(current, inferred ?? "patch") : void 0) || await runVersionPrompts(ctx, config, inferred);
|
|
1295
1303
|
ctx.isIncrement = neq(current, nextVersion);
|
|
1296
1304
|
const { version: next, isPrerelease: toPreRelease, preId, preBase } = parseVersion(nextVersion);
|
|
1297
1305
|
Object.assign(ctx.pkg, {
|
|
@@ -1318,7 +1326,7 @@ var Action = class {
|
|
|
1318
1326
|
await this.printChangelog(ctx, config);
|
|
1319
1327
|
}
|
|
1320
1328
|
async changelogTask(ctx, config) {
|
|
1321
|
-
const { isIncrement, dryRun, selectedPkg, noGit, onlyChangelog } = ctx;
|
|
1329
|
+
const { isIncrement, dryRun, selectedPkg, noGit, onlyChangelog, pkg: { scope } } = ctx;
|
|
1322
1330
|
const { getPkgDir, changelog: { tagPrefix, releaseCount, includeHidden, transformTypes } } = config;
|
|
1323
1331
|
if (noGit) return;
|
|
1324
1332
|
await tasks([{
|
|
@@ -1327,6 +1335,7 @@ var Action = class {
|
|
|
1327
1335
|
await generateChangelog({
|
|
1328
1336
|
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1329
1337
|
tagPrefix: tagPrefix?.(selectedPkg),
|
|
1338
|
+
scope: isEmpty(nonNullable([scope])) ? void 0 : nonNullable([scope]),
|
|
1330
1339
|
releaseCount,
|
|
1331
1340
|
includeHidden,
|
|
1332
1341
|
transformTypes
|
|
@@ -1399,6 +1408,45 @@ var Action = class {
|
|
|
1399
1408
|
}
|
|
1400
1409
|
};
|
|
1401
1410
|
//#endregion
|
|
1411
|
+
//#region src/monorepo/packages.ts
|
|
1412
|
+
const scanWorkspacePackages = (cwd) => {
|
|
1413
|
+
const workspaceFile = join(cwd, "pnpm-workspace.yaml");
|
|
1414
|
+
if (!existsSync(workspaceFile)) return [];
|
|
1415
|
+
const { packages = [] } = parse(readFileSync(workspaceFile, "utf8"));
|
|
1416
|
+
return globSync(packages.map((pattern) => normalizePath(join(pattern, "package.json"))), {
|
|
1417
|
+
cwd,
|
|
1418
|
+
absolute: false
|
|
1419
|
+
});
|
|
1420
|
+
};
|
|
1421
|
+
const getPackages = (cwd) => {
|
|
1422
|
+
return scanWorkspacePackages(cwd).reduce(((acc, file) => {
|
|
1423
|
+
const workspace = dirname(dirname(file));
|
|
1424
|
+
(acc[workspace] ??= []).push(basename(dirname(file)));
|
|
1425
|
+
return acc;
|
|
1426
|
+
}), {});
|
|
1427
|
+
};
|
|
1428
|
+
const monoConfig = (packages = [], workspace = "packages") => {
|
|
1429
|
+
const subs = isEmpty(packages) ? "[]" : `[ ${[...packages].map((k) => `'${k}'`).join(", ")} ]`;
|
|
1430
|
+
return dedent(`
|
|
1431
|
+
import { defineConfig } from '@peiyanlu/release'
|
|
1432
|
+
${newline(1)}
|
|
1433
|
+
export default defineConfig({
|
|
1434
|
+
isMonorepo: true,
|
|
1435
|
+
packages: ${subs},
|
|
1436
|
+
getPkgDir: (pkg) => \`${workspace}/\${ pkg }\`,
|
|
1437
|
+
toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
|
|
1438
|
+
changelog: {
|
|
1439
|
+
tagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1440
|
+
},
|
|
1441
|
+
})
|
|
1442
|
+
`);
|
|
1443
|
+
};
|
|
1444
|
+
const generateMonoConfig = (cwd = process.cwd()) => {
|
|
1445
|
+
const entries = Object.entries(getPackages(cwd));
|
|
1446
|
+
const [workspace, packages] = entries.length === 1 ? entries[0] : [];
|
|
1447
|
+
return monoConfig(packages, workspace);
|
|
1448
|
+
};
|
|
1449
|
+
//#endregion
|
|
1402
1450
|
//#region src/cli.ts
|
|
1403
1451
|
program.name("release").description(description).version(version, "-v, --version", "Print the tool version and exit.").usage("[release-type] [options]").argument("[release-type]", "Version bump type: patch | minor | major").enablePositionalOptions().option("-n, --dry-run", "Run in dry mode", 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("--only-changelog", "Only update the changelog.", false).option("-r, --release-count <count>", "Release count for release.", (val) => Number(val)).option("--include-hidden", "Include hidden commit types in the changelog.").option("-C, --no-require-clean-working-tree", "Allow releasing with uncommitted changes.").option("--skip-git", "Skip all Git-related checks and operations.").option("--skip-npm", "Skip all npm-related checks and operations.").option("--skip-github", "Skip all GitHub-related checks and operations.").option("-m, --is-monorepo", "Mono-repo project").helpOption("-h, --help", "Display help information.").action(async (releaseType, options) => {
|
|
1404
1452
|
const { prepare } = options;
|
|
@@ -1417,16 +1465,12 @@ program.command("init").description("Create a release configuration file").optio
|
|
|
1417
1465
|
console.error(`${red`Error:`} ${yellow(infile)} already exists.\nUse ${green`--force`} to overwrite.`);
|
|
1418
1466
|
process.exit(1);
|
|
1419
1467
|
}
|
|
1420
|
-
const
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
tagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1427
|
-
},
|
|
1428
|
-
}`;
|
|
1429
|
-
const content = [`import { defineConfig } from '@peiyanlu/release'`, `export default defineConfig(${monorepo ? monoConfigToString() : "{}"})`].join(eol(3));
|
|
1468
|
+
const config = () => dedent(`
|
|
1469
|
+
import { defineConfig } from '@peiyanlu/release'
|
|
1470
|
+
${newline(1)}
|
|
1471
|
+
export default defineConfig({})
|
|
1472
|
+
`);
|
|
1473
|
+
const content = monorepo ? generateMonoConfig(process.cwd()) : config();
|
|
1430
1474
|
writeFileSync(configFile, content + newline(), "utf-8");
|
|
1431
1475
|
console.log(`Wrote to ${underline(dim(configFile))}`);
|
|
1432
1476
|
if (add) gitAddSync([infile]);
|
package/dist/index.d.mts
CHANGED
|
@@ -19,20 +19,20 @@ type HookConfig = { [key in ReleaseHookKey]: ReleaseHookValue; };
|
|
|
19
19
|
interface ReleaseConfig {
|
|
20
20
|
/**
|
|
21
21
|
* 生命周期钩子配置
|
|
22
|
-
* 用于在 Release 关键阶段注入自定义逻辑(如 before/after hooks
|
|
22
|
+
* 用于在 Release 关键阶段注入自定义逻辑(如 before/after hooks)
|
|
23
23
|
*/
|
|
24
24
|
hooks: HookConfig;
|
|
25
25
|
/** 是否是 monorepo */
|
|
26
26
|
isMonorepo: boolean;
|
|
27
|
-
/** 使用 monorepo 时发布的包列表,后续回调的 `pkg
|
|
27
|
+
/** 使用 monorepo 时发布的包列表,后续回调的 `pkg`,默认为 `[]` 作为单仓库 */
|
|
28
28
|
packages: string[];
|
|
29
|
-
/** 使用 monorepo 时使用 getPkgDir,例如 `packages/${pkg}
|
|
29
|
+
/** 使用 monorepo 时使用 getPkgDir,例如 `packages/${pkg}`,默认为 `.` 作为单仓库 */
|
|
30
30
|
getPkgDir: (pkg: string) => string;
|
|
31
31
|
/** 使用 monorepo 时使用 toTag,例如 `${pkg}@${version}`,默认为 `v${version}` 作为单仓库 */
|
|
32
32
|
toTag: (pkg: string, version: string) => string;
|
|
33
33
|
/**
|
|
34
34
|
* CHANGELOG 相关配置
|
|
35
|
-
* 影响生成的日志数量、日志标题(monorepo
|
|
35
|
+
* 影响生成的日志数量、日志标题(monorepo)
|
|
36
36
|
*/
|
|
37
37
|
changelog: {
|
|
38
38
|
/**
|
|
@@ -60,7 +60,7 @@ interface ReleaseConfig {
|
|
|
60
60
|
skipGithub: boolean;
|
|
61
61
|
/**
|
|
62
62
|
* Git 相关配置
|
|
63
|
-
* 影响提交、打 tag、推送等本地/远程 Git
|
|
63
|
+
* 影响提交、打 tag、推送等本地/远程 Git 行为
|
|
64
64
|
*/
|
|
65
65
|
git: {
|
|
66
66
|
/**
|
|
@@ -115,7 +115,7 @@ interface ReleaseConfig {
|
|
|
115
115
|
};
|
|
116
116
|
/**
|
|
117
117
|
* NPM 发布相关配置
|
|
118
|
-
* 影响 npm publish
|
|
118
|
+
* 影响 npm publish 的目标路径、参数与前置校验
|
|
119
119
|
*/
|
|
120
120
|
npm: {
|
|
121
121
|
/**
|
|
@@ -129,19 +129,19 @@ interface ReleaseConfig {
|
|
|
129
129
|
/**
|
|
130
130
|
* 是否跳过发布前检查(如 ping、whoami 等校验)
|
|
131
131
|
*
|
|
132
|
-
* 启用后可减少发布前的网络请求,但可能无法提前发现登录状态异常或 Registry
|
|
132
|
+
* 启用后可减少发布前的网络请求,但可能无法提前发现登录状态异常或 Registry 不可用等问题
|
|
133
133
|
*/
|
|
134
134
|
skipChecks: boolean;
|
|
135
135
|
/**
|
|
136
136
|
* 发布前的文件清理配置
|
|
137
137
|
*
|
|
138
|
-
*
|
|
138
|
+
* 用于控制复制到临时发布目录后的清理行为,不会修改源项目文件
|
|
139
139
|
*/
|
|
140
140
|
cleanup: {
|
|
141
141
|
/**
|
|
142
142
|
* 是否清理 package.json
|
|
143
143
|
*
|
|
144
|
-
* 包括移除开发相关字段、应用 publishConfig
|
|
144
|
+
* 包括移除开发相关字段、应用 publishConfig,并保留发布所需内容
|
|
145
145
|
*/
|
|
146
146
|
packageJson: boolean | {
|
|
147
147
|
/** 需要忽略的字段,支持 `a.b.c` */
|
|
@@ -152,7 +152,7 @@ interface ReleaseConfig {
|
|
|
152
152
|
/**
|
|
153
153
|
* 是否清理 README.md
|
|
154
154
|
*
|
|
155
|
-
*
|
|
155
|
+
* 仅保留主要内容,并移除或精简不需要随包发布的文档内容
|
|
156
156
|
*/
|
|
157
157
|
readme: boolean;
|
|
158
158
|
/**
|
|
@@ -164,7 +164,7 @@ interface ReleaseConfig {
|
|
|
164
164
|
/**
|
|
165
165
|
* GitHub Release 相关配置
|
|
166
166
|
*
|
|
167
|
-
* 控制是否创建 Release
|
|
167
|
+
* 控制是否创建 Release、发布方式以及上传资源
|
|
168
168
|
*/
|
|
169
169
|
github: {
|
|
170
170
|
/**
|
|
@@ -196,9 +196,13 @@ interface ReleaseConfig {
|
|
|
196
196
|
*/
|
|
197
197
|
assets: string[];
|
|
198
198
|
/**
|
|
199
|
-
*
|
|
199
|
+
* 是否跳过本地 GitHub 发布前检查
|
|
200
200
|
*/
|
|
201
201
|
skipChecks: boolean;
|
|
202
|
+
/**
|
|
203
|
+
* 是否使用简短的 Release Notes,内容为指向 CHANGELOG.md 的链接
|
|
204
|
+
*/
|
|
205
|
+
shortNotes: boolean;
|
|
202
206
|
};
|
|
203
207
|
}
|
|
204
208
|
//#endregion
|
|
@@ -230,6 +234,8 @@ interface ParsePresetOptions {
|
|
|
230
234
|
includeHidden?: boolean;
|
|
231
235
|
/** 对默认 commit types 进行转换 */
|
|
232
236
|
transformTypes?: (types: CommitType$1[]) => CommitType$1[];
|
|
237
|
+
/** commit scope */
|
|
238
|
+
scope?: string | string[];
|
|
233
239
|
}
|
|
234
240
|
type GOptions = Simplify<GenerateOptions & ParsePresetOptions>;
|
|
235
241
|
declare const createGenerator: (options: GOptions) => Promise<ConventionalChangelog>;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isFunction, isPlainObject, isUndefined, isZero } from "@peiyanlu/ts-utils";
|
|
1
|
+
import { isEmpty, 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";
|
|
@@ -154,6 +154,10 @@ function headerPartial(context) {
|
|
|
154
154
|
const fullTitle = words(linkCompare && !isLikelyHash(previousTag) ? link(version, compareUrl(context)) : version, title && `"${title}"`, date && `(${date})`);
|
|
155
155
|
return heading(2, isPatch ? small(fullTitle) : fullTitle);
|
|
156
156
|
}
|
|
157
|
+
function preamblePartial(contest) {
|
|
158
|
+
const { preamble, commitGroups, noteGroups } = contest;
|
|
159
|
+
return segments(preamble, isEmpty(preamble) && isEmpty(commitGroups) && isEmpty(noteGroups) && "Version bump without any changes.");
|
|
160
|
+
}
|
|
157
161
|
function template(context) {
|
|
158
162
|
const { headerPartial, preamblePartial, commitPartial, footerPartial, noteGroups, commitGroups } = context;
|
|
159
163
|
return segments(headerPartial(context), preamblePartial(context), each(noteGroups, (group) => segments(heading(3, words("⚠", group.title)), list(group.notes, (note) => {
|
|
@@ -163,24 +167,29 @@ function template(context) {
|
|
|
163
167
|
//#endregion
|
|
164
168
|
//#region src/git/changelog.ts
|
|
165
169
|
const parsePreset = (options = {}) => {
|
|
166
|
-
const { includeHidden, transformTypes } = options;
|
|
170
|
+
const { includeHidden, transformTypes, scope } = options;
|
|
167
171
|
let types = [...defaultTypes];
|
|
168
172
|
if (includeHidden) types.forEach((type) => {
|
|
169
173
|
if (type.effect === "hidden") type.effect = "changelog";
|
|
170
174
|
});
|
|
171
175
|
types = transformTypes?.(types) ?? types;
|
|
172
|
-
const preset = createPreset({
|
|
176
|
+
const preset = createPreset({
|
|
177
|
+
types,
|
|
178
|
+
scope
|
|
179
|
+
});
|
|
173
180
|
preset.writer ??= {};
|
|
181
|
+
preset.writer.preamblePartial = preamblePartial;
|
|
174
182
|
preset.writer.headerPartial = headerPartial;
|
|
175
183
|
preset.writer.template = template;
|
|
176
184
|
return preset;
|
|
177
185
|
};
|
|
178
186
|
const createGenerator = async (options) => {
|
|
179
|
-
const { getPkgDir, tagPrefix, releaseCount = 1, includeHidden, transformTypes } = options;
|
|
187
|
+
const { getPkgDir, tagPrefix, releaseCount = 1, includeHidden, transformTypes, scope } = options;
|
|
180
188
|
const pkgDir = getPkgDir();
|
|
181
189
|
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(parsePreset({
|
|
182
190
|
includeHidden,
|
|
183
|
-
transformTypes
|
|
191
|
+
transformTypes,
|
|
192
|
+
scope
|
|
184
193
|
})).options({ releaseCount }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
185
194
|
};
|
|
186
195
|
const getChangelog = async (options) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peiyanlu/release",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Release project tool.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"@conventional-changelog/template": "^1.2.1",
|
|
25
25
|
"@octokit/rest": "^22.0.1",
|
|
26
26
|
"@peiyanlu/cli-utils": "^0.0.15",
|
|
27
|
-
"@peiyanlu/node-utils": "^0.0.
|
|
27
|
+
"@peiyanlu/node-utils": "^0.0.4",
|
|
28
28
|
"@peiyanlu/ts-utils": "^0.0.12",
|
|
29
29
|
"ansis": "^4.3.1",
|
|
30
30
|
"commander": "^15.0.0",
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"open": "^11.0.0",
|
|
37
37
|
"publint": "^0.3.22",
|
|
38
38
|
"semver": "^7.8.5",
|
|
39
|
-
"tinyglobby": "^0.2.17"
|
|
39
|
+
"tinyglobby": "^0.2.17",
|
|
40
|
+
"yaml": "^2.9.0"
|
|
40
41
|
},
|
|
41
42
|
"author": {
|
|
42
43
|
"name": "YanluPei",
|