@peiyanlu/release 0.0.4 → 0.0.5

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 CHANGED
@@ -1,17 +1,17 @@
1
1
  # Release Project
2
2
 
3
3
  <p>
4
- <a href="https://www.npmjs.com/package/@peiyanlu/create-release" target="_blank">
5
- <img src="https://img.shields.io/badge/npm-@peiyanlu/create--release-blue.svg?logo=npm" alt="NPM Package" />
4
+ <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
5
+ <img src="https://img.shields.io/badge/npm-@peiyanlu/release-blue.svg?logo=npm" alt="NPM Package" />
6
6
  </a>
7
- <a href="https://www.npmjs.com/package/@peiyanlu/create-release" target="_blank">
8
- <img src="https://img.shields.io/npm/v/@peiyanlu/create-release.svg" alt="NPM Version" />
7
+ <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
8
+ <img src="https://img.shields.io/npm/v/@peiyanlu/release.svg" alt="NPM Version" />
9
9
  </a>
10
- <a href="https://www.npmjs.com/package/@peiyanlu/create-release" target="_blank">
11
- <img src="https://img.shields.io/npm/l/@peiyanlu/create-release.svg" alt="Package License" />
10
+ <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
11
+ <img src="https://img.shields.io/npm/l/@peiyanlu/release.svg" alt="Package License" />
12
12
  </a>
13
- <a href="https://www.npmjs.com/package/@peiyanlu/create-release" target="_blank">
14
- <img src="https://img.shields.io/npm/dm/@peiyanlu/create-release.svg" alt="NPM Downloads" />
13
+ <a href="https://www.npmjs.com/package/@peiyanlu/release" target="_blank">
14
+ <img src="https://img.shields.io/npm/dm/@peiyanlu/release.svg" alt="NPM Downloads" />
15
15
  </a>
16
16
  </p>
17
17
 
@@ -110,7 +110,7 @@ export default defineConfig({
110
110
  packages: [],
111
111
  getPkgDir: (pkg) => `.`,
112
112
  toTag: (pkg: string, version: string) => `v${ version }`,
113
- changelogTagPrefix: undefined,
113
+ tagPrefix: undefined,
114
114
 
115
115
  git: {
116
116
  commit: true,
@@ -145,7 +145,7 @@ export default defineConfig({
145
145
  packages: [ 'demo-a', 'demo-b' ],
146
146
  getPkgDir: (pkg) => `packages/${ pkg }`,
147
147
  toTag: (pkg: string, version: string) => `${ pkg }@${ version }`,
148
- changelogTagPrefix: (pkg: string) => `${ pkg }@`,
148
+ tagPrefix: (pkg: string) => `${ pkg }@`,
149
149
 
150
150
  git: {
151
151
  commit: true,
package/dist/cli.mjs CHANGED
@@ -1,20 +1,20 @@
1
1
  import path, { join, resolve } from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
- import { eol, execAsync, isGitRepo, parseGitHubRepo, readJsonFile, runGit, runNpm } 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, isWorkingDirClean, parseGitHubRepo, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, resolveChangelogRange, runGit } from "@peiyanlu/cli-utils";
4
4
  import { dim, green, red, rgb, underline, yellow } from "ansis";
5
5
  import { program } from "commander";
6
6
  import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
7
7
  import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
8
8
  import { publint } from "publint";
9
9
  import { formatMessage } from "publint/utils";
10
- import { clean, coerce, eq, gt, inc, lt, parse, prerelease, valid } from "semver";
10
+ import { coerce, eq, inc, lt, parse, prerelease, valid } from "semver";
11
11
  import { castArray, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
12
12
  import { spawnSync } from "node:child_process";
13
- import { inspect } from "node:util";
14
13
  import { Octokit } from "@octokit/rest";
15
14
  import { contentType } from "mime-types";
16
15
  import open from "open";
17
16
  import { glob } from "tinyglobby";
17
+ import { inspect } from "node:util";
18
18
  import { ConventionalChangelog } from "conventional-changelog";
19
19
  import createPreset from "conventional-changelog-conventionalcommits";
20
20
  import { writeFile } from "node:fs/promises";
@@ -153,9 +153,6 @@ const MSG = {
153
153
 
154
154
  //#endregion
155
155
  //#region src/github/release.ts
156
- const getGithubUrl = (owner, repo) => {
157
- return `https://github.com/${owner}/${repo}`;
158
- };
159
156
  const truncateBody = (body) => {
160
157
  if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
161
158
  return body;
@@ -270,140 +267,9 @@ const createRelease = async (ctx, config) => {
270
267
  await uploadAssets(ctx, config);
271
268
  }
272
269
  };
273
- const getGithubReleaseUrl = (ctx) => {
274
- const { github: { owner, repo }, git: { currentTag } } = ctx;
275
- return `${getGithubUrl(owner, repo)}/releases/tag/${normalizeTag(currentTag)}`;
276
- };
277
270
 
278
271
  //#endregion
279
272
  //#region src/git/commit.ts
280
- const getBranchName = async () => {
281
- let branch = await runGit(["branch", "--show-current"]);
282
- if (!branch) {
283
- branch = await runGit([
284
- "rev-parse",
285
- "--abbrev-ref",
286
- "HEAD"
287
- ]);
288
- if (branch === "HEAD") return void 0;
289
- }
290
- return branch;
291
- };
292
- const getRemoteForBranch = (branch) => {
293
- return runGit([
294
- "config",
295
- "--get",
296
- `branch.${branch}.remote`
297
- ]);
298
- };
299
- const getAllRemotes = async () => {
300
- return (await runGit(["remote"]))?.split("\n").filter(Boolean) ?? [];
301
- };
302
- const getStatus = () => {
303
- return runGit([
304
- "status",
305
- "--short",
306
- "--untracked-files=no"
307
- ], { trim: false });
308
- };
309
- const coloredChangeset = (log$1) => {
310
- const colorStatusChar = (ch) => {
311
- switch (ch) {
312
- case "M": return `\x1b[33m${ch}\x1b[0m`;
313
- case "A": return `\x1b[32m${ch}\x1b[0m`;
314
- case "D": return `\x1b[31m${ch}\x1b[0m`;
315
- default: return ch;
316
- }
317
- };
318
- const colorStatus = (status) => status.split("").map(colorStatusChar).join("");
319
- return log$1.split("\n").filter(Boolean).map((line) => {
320
- const status = line.slice(0, 2);
321
- const file = line.slice(3);
322
- return `${colorStatus(status)} ${dim(file)}`;
323
- }).join("\n");
324
- };
325
- const getRemote = async () => {
326
- const branch = await getBranchName();
327
- const remoteName = branch && await getRemoteForBranch(branch) || "origin";
328
- return {
329
- remoteName,
330
- remoteUrl: await runGit([
331
- "remote",
332
- "get-url",
333
- remoteName
334
- ]).catch((_) => runGit([
335
- "config",
336
- "--get",
337
- `remote.${remoteName}.url`
338
- ]))
339
- };
340
- };
341
- const getLatestTag = async (match = "*", exclude = "*-beta.*") => {
342
- return runGit([
343
- "describe",
344
- "--tags",
345
- "--abbrev=0",
346
- `--match=${match}`,
347
- `--exclude=${exclude}`
348
- ]);
349
- };
350
- const getPreviousTag = async (current) => {
351
- return runGit([
352
- "describe",
353
- "--tags",
354
- "--abbrev=0",
355
- `${await runGit([
356
- "rev-list",
357
- "--tags",
358
- current || "--skip=1",
359
- "--max-count=1"
360
- ])}^`
361
- ]);
362
- };
363
- const resolveChangelogRange = async (isIncrement = true) => {
364
- const latestTag = await getLatestTag();
365
- const previousTag = await getPreviousTag(latestTag);
366
- if (!latestTag) return {
367
- from: "",
368
- to: "HEAD"
369
- };
370
- if (!isIncrement && previousTag) return {
371
- from: previousTag,
372
- to: `${latestTag}^1`
373
- };
374
- return {
375
- from: latestTag,
376
- to: "HEAD"
377
- };
378
- };
379
- const getLog = async (from = "", to = "HEAD", scope) => {
380
- const cmd = ["log", `--pretty=format:* %s (%h)`];
381
- if (from) cmd.push(`${from}...${to}`);
382
- if (scope) cmd.push(...["--", scope]);
383
- return runGit(cmd, { trim: false });
384
- };
385
- const isWorkingDirClean = async () => {
386
- return (await runGit(["status", "-s"]))?.length === 0;
387
- };
388
- const hasUpstreamBranch = async () => {
389
- const branch = await runGit([
390
- "for-each-ref",
391
- "--format=%(upstream:short)",
392
- await runGit(["symbolic-ref", "HEAD"])
393
- ]);
394
- return Boolean(branch);
395
- };
396
- const getUpstreamArgs = async (remoteName) => {
397
- const hasUpstream = await hasUpstreamBranch();
398
- const branch = await getBranchName();
399
- if (!hasUpstream) return [
400
- "--set-upstream",
401
- remoteName,
402
- branch
403
- ];
404
- return [remoteName, branch];
405
- };
406
- const normalizeTag = (tag) => clean(tag) ?? tag;
407
273
  const gitCheck = async (ctx, config) => {
408
274
  const { pkg: { name } } = ctx;
409
275
  const { git: { requireRepository, requireRemote, requireWorkDirClean } } = config;
@@ -430,53 +296,23 @@ const commitAndTag = async (ctx, config) => {
430
296
  const { git: { commit, tag, push, addUntrackedFiles, commitArgs, tagArgs, pushArgs } } = config;
431
297
  if (!commit) return;
432
298
  const args = dryRun ? ["--dry-run"] : [];
433
- await runGit([
434
- "add",
435
- ".",
436
- addUntrackedFiles ? "--all" : "--update",
437
- ...args
438
- ]);
439
- await runGit([
440
- "commit",
441
- "--message",
442
- commitMessage,
443
- ...commitArgs,
444
- ...args
445
- ]);
299
+ addUntrackedFiles ? await gitAddAll() : await gitAddTracked(args);
300
+ await gitCommit(commitMessage, [...commitArgs, ...args]);
446
301
  if (!dryRun) Object.assign(ctx.git, { isCommitted: true });
447
302
  if (tag) {
448
- await runGit([
449
- "tag",
450
- "--annotate",
451
- "--message",
452
- tagMessage,
453
- currentTag,
454
- ...tagArgs,
455
- ...args
456
- ]);
303
+ await gitTagAnnotated(currentTag, tagMessage, [...tagArgs, ...args]);
457
304
  if (!dryRun) Object.assign(ctx.git, { isTagged: true });
458
305
  }
459
306
  if (push) {
460
307
  const remotes = await getAllRemotes();
461
308
  for (const remoteName$1 of remotes) {
462
- const upstreamArgs = await getUpstreamArgs(remoteName$1);
463
- await runGit([
464
- "push",
465
- remoteName$1,
466
- `refs/tags/${currentTag}`,
467
- ...args
468
- ]);
469
- await runGit([
470
- "push",
471
- ...upstreamArgs,
472
- ...pushArgs,
473
- ...args
474
- ]);
309
+ await pushTag(remoteName$1, currentTag, args);
310
+ await pushBranch(remoteName$1, await getCurrentBranch(), [...pushArgs, ...args]);
475
311
  }
476
312
  if (!dryRun) Object.assign(ctx.git, { isPushed: true });
477
313
  }
478
314
  };
479
- const gitRollback$1 = (ctx) => {
315
+ const gitRollback = (ctx) => {
480
316
  const { git: { currentTag, isCommitted, isTagged, isPushed } } = ctx;
481
317
  if (isPushed) return;
482
318
  spawnSync("git", ["restore", "."]);
@@ -492,6 +328,30 @@ const gitRollback$1 = (ctx) => {
492
328
  ]);
493
329
  };
494
330
 
331
+ //#endregion
332
+ //#region src/prompts.ts
333
+ const abortTask = (msg$1) => {
334
+ cancel(`${red("✕")} ${msg$1}`);
335
+ process.exit(0);
336
+ };
337
+ const abortSinglePrompt = (value) => {
338
+ if (isCancel(value)) abortTask(MSG.ABORT.CANCEL);
339
+ };
340
+ const abortGroupPrompt = (ctx) => {
341
+ ctx && gitRollback(ctx);
342
+ abortTask(MSG.ABORT.CANCEL);
343
+ };
344
+ const abortOnError = (err, ctx) => {
345
+ ctx && gitRollback(ctx);
346
+ const msg$1 = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
347
+ log.message(`\n${msg$1}\n`);
348
+ process.exit(1);
349
+ };
350
+ const taskEnd = (msg$1) => {
351
+ outro(msg$1);
352
+ process.exit(0);
353
+ };
354
+
495
355
  //#endregion
496
356
  //#region src/utils.ts
497
357
  const info = (msg$1) => {
@@ -524,20 +384,6 @@ const runLifeCycleHook = async (hooks, key, dryRun) => {
524
384
  log.success(success(hook.replace(/^echo$/, dim("echo")), dryRun));
525
385
  }));
526
386
  };
527
- const gitRollback = (ctx) => {
528
- const { git: { currentTag, isCommitted, isTagged } } = ctx;
529
- spawnSync("git", ["restore", "."]);
530
- if (isTagged) spawnSync("git", [
531
- "tag",
532
- "--delete",
533
- currentTag
534
- ]);
535
- spawnSync("git", [
536
- "reset",
537
- "--hard",
538
- isCommitted ? "HEAD~1" : "HEAD"
539
- ]);
540
- };
541
387
  const diff = (from, to, separator = ".") => {
542
388
  const a = from.split(separator);
543
389
  return to.split(separator).map((v, i) => v === a[i] ? dim(v) : green(v)).join(separator);
@@ -559,39 +405,6 @@ const formatTemplate = async (ctx, config) => {
559
405
  });
560
406
  Object.assign(ctx.github, { releaseName });
561
407
  };
562
- const getPackageInfo = (pkgName, getPkgDir = (pkg$1) => `packages/${pkg$1}`) => {
563
- const pkgDir = resolve(getPkgDir(pkgName));
564
- const pkgPath = resolve(pkgDir, "package.json");
565
- return {
566
- pkg: readJsonFile(pkgPath),
567
- pkgDir,
568
- pkgPath
569
- };
570
- };
571
-
572
- //#endregion
573
- //#region src/prompts.ts
574
- const abortTask = (msg$1) => {
575
- cancel(`${red("✕")} ${msg$1}`);
576
- process.exit(0);
577
- };
578
- const abortSinglePrompt = (value) => {
579
- if (isCancel(value)) abortTask(MSG.ABORT.CANCEL);
580
- };
581
- const abortGroupPrompt = (ctx) => {
582
- ctx && gitRollback(ctx);
583
- abortTask(MSG.ABORT.CANCEL);
584
- };
585
- const abortOnError = (err, ctx) => {
586
- ctx && gitRollback(ctx);
587
- const msg$1 = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
588
- log.message(`\n${msg$1}\n`);
589
- process.exit(1);
590
- };
591
- const taskEnd = (msg$1) => {
592
- outro(msg$1);
593
- process.exit(0);
594
- };
595
408
 
596
409
  //#endregion
597
410
  //#region src/version/bump.ts
@@ -814,7 +627,7 @@ const createDefaultConfig = (isCI) => {
814
627
  toTag(pkg$1, version) {
815
628
  return `v${version}`;
816
629
  },
817
- changelogTagPrefix: void 0,
630
+ tagPrefix: void 0,
818
631
  git: {
819
632
  commit: isCI,
820
633
  tag: isCI,
@@ -1107,30 +920,6 @@ const runNpmOptPrompts = async (ctx, config) => {
1107
920
 
1108
921
  //#endregion
1109
922
  //#region src/npm/publish.ts
1110
- const resolvePublishRegistry = (publishConfig = {}) => {
1111
- if (publishConfig.registry) return publishConfig.registry;
1112
- return Object.keys(publishConfig).filter((k) => k.endsWith("registry")).map((k) => publishConfig[k])[0];
1113
- };
1114
- const pingRegistry = async () => {
1115
- return void 0 !== await runNpm(["ping"]);
1116
- };
1117
- const getAuthenticatedUser = () => {
1118
- return runNpm(["whoami"]);
1119
- };
1120
- const getVersionByTag = (name, tag) => {
1121
- return runNpm([
1122
- "show",
1123
- `${name}@${tag}`,
1124
- "version"
1125
- ]);
1126
- };
1127
- const getPublishedVersion = (name) => {
1128
- return runNpm([
1129
- "info",
1130
- name,
1131
- "version"
1132
- ]);
1133
- };
1134
923
  const resolvePublishTag = async (pkgName, version) => {
1135
924
  const { toPreRelease, preId } = parseVersion(version);
1136
925
  if (!toPreRelease) {
@@ -1138,69 +927,20 @@ const resolvePublishTag = async (pkgName, version) => {
1138
927
  return active && lt(version, active) ? "previous" : "latest";
1139
928
  } else return preId || "prerelease";
1140
929
  };
1141
- const bumpPackageVersion = (version, args = [], cwd = ".") => {
1142
- return runNpm([
1143
- "version",
1144
- version,
1145
- "--no-git-tag-version",
1146
- "--workspaces=false",
1147
- "--allow-same-version",
1148
- ...args
1149
- ], { cwd });
1150
- };
1151
930
  const isOtpError = (err) => err instanceof Error && /one-time password|otp/i.test(err.message);
1152
- const publishPackage = (pkg$1 = ".", tag = "latest", args = []) => {
1153
- return runNpm([
1154
- "publish",
1155
- "--access",
1156
- "public",
1157
- "--tag",
1158
- tag,
1159
- "--workspaces=false",
1160
- ...args
1161
- ], { cwd: pkg$1 });
1162
- };
1163
- const getPackageUrl = (name, next) => {
1164
- return `https://www.npmjs.com/package/${name}/v/${next}`;
1165
- };
1166
- const isCollaborator = async (ctx) => {
1167
- const { pkg: { name, publishConfig }, npm: { username } } = ctx;
1168
- const registry = resolvePublishRegistry(publishConfig);
1169
- const registryArg = registry ? ["--registry", registry] : [];
1170
- let npmVersion = await runNpm(["--version"]);
1171
- let accessCommand;
1172
- if (gt(npmVersion, "9.0.0")) accessCommand = [
1173
- "access",
1174
- "list",
1175
- "collaborators",
1176
- "--json"
1177
- ];
1178
- else accessCommand = ["access", "ls-collaborators"];
1179
- const res = await runNpm([
1180
- ...accessCommand,
1181
- name,
1182
- ...registryArg
1183
- ]);
1184
- if (res) return JSON.parse(res ?? "{}")[username]?.includes("write");
1185
- return false;
1186
- };
1187
931
  const npmCheck = async (ctx, config) => {
1188
- const { pkg: { name, current, publishConfig: { registry } }, npm: { username } } = ctx;
932
+ const { pkg: { name, current, publishConfig: { registry } } } = ctx;
1189
933
  const { npm: { skipChecks } } = config;
1190
934
  const tag = await resolvePublishTag(name, current);
1191
935
  Object.assign(ctx.npm, { tag });
1192
936
  if (skipChecks) return "";
1193
- if (!await pingRegistry()) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
1194
- const getUser = async () => {
1195
- const username$1 = await getAuthenticatedUser();
1196
- if (!username$1) return false;
1197
- Object.assign(ctx.npm, { username: username$1 });
1198
- return true;
1199
- };
1200
- if (!await getUser()) throw new Error(MSG.ERROR.NPM_AUTH);
1201
- const latest = await getVersionByTag(name, tag);
937
+ const [pinged, username] = await Promise.all([pingRegistry(registry), getAuthenticatedUser(registry)]);
938
+ if (!pinged) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
939
+ if (!username) throw new Error(MSG.ERROR.NPM_AUTH);
940
+ Object.assign(ctx.npm, { username });
941
+ const latest = await getPublishedVersion(`${name}@${tag}`);
1202
942
  if (latest) {
1203
- if (!await isCollaborator(ctx)) throw new Error(MSG.ERROR.NPM_USER(username, name));
943
+ if (!await hasWriteAccess(name, username, registry)) throw new Error(MSG.ERROR.NPM_USER(username, name));
1204
944
  }
1205
945
  return latest ? `(${tag} → ${latest})` : `(${tag} → no published version)`;
1206
946
  };
@@ -1210,11 +950,15 @@ const publishNpm = async (ctx, config) => {
1210
950
  if (!publish) return;
1211
951
  const otpArgs = otp ? ["--otp", otp] : [];
1212
952
  const dryRunArg = dryRun ? ["--dry-run"] : [];
1213
- return publishPackage(getPkgDir(selectedPkg), tag, [
1214
- ...otpArgs,
1215
- ...pushArgs,
1216
- ...dryRunArg
1217
- ]);
953
+ return publishPackage({
954
+ tag,
955
+ args: [
956
+ ...otpArgs,
957
+ ...pushArgs,
958
+ ...dryRunArg
959
+ ],
960
+ cwd: getPkgDir(selectedPkg)
961
+ });
1218
962
  };
1219
963
 
1220
964
  //#endregion
@@ -1290,7 +1034,7 @@ var Action = class {
1290
1034
  await this.gitTask(ctx, config);
1291
1035
  await this.npmTask(ctx, config);
1292
1036
  await this.githubTask(ctx, config);
1293
- if (dryRun) gitRollback$1(ctx);
1037
+ if (dryRun) gitRollback(ctx);
1294
1038
  taskEnd(MSG.OUTRO(dryRun));
1295
1039
  }
1296
1040
  async checkTask(ctx, config) {
@@ -1314,7 +1058,7 @@ var Action = class {
1314
1058
  }
1315
1059
  async bumpTask(ctx, config) {
1316
1060
  const { pkg: { current }, dryRun, isCI, showRelease, showChangelog, selectedPkg } = ctx;
1317
- const { hooks, getPkgDir } = config;
1061
+ const { isMonorepo, hooks, getPkgDir, tagPrefix } = config;
1318
1062
  const need = (ctx$1) => {
1319
1063
  const { pkg: { next: next$1 }, isIncrement: isIncrement$1 } = ctx$1;
1320
1064
  return !next$1 && isIncrement$1;
@@ -1336,22 +1080,22 @@ var Action = class {
1336
1080
  }
1337
1081
  }]).catch((err) => abortOnError(err, ctx));
1338
1082
  await runLifeCycleHook(hooks, "after:bump", dryRun);
1339
- const { from, to } = await resolveChangelogRange(isIncrement);
1083
+ const { from, to } = await resolveChangelogRange(isIncrement, isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*");
1340
1084
  const logStr = await getLog(from, to, getPkgDir(selectedPkg));
1341
1085
  if (logStr) {
1342
- msg("GIT", `Changelog:${eol(2)}` + logStr);
1086
+ msg("GIT", `Changelog(${from}...${to}):${eol(2)}` + logStr);
1343
1087
  if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
1344
1088
  } else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
1345
1089
  }
1346
1090
  async changelogTask(ctx, config) {
1347
1091
  const { isIncrement, dryRun, selectedPkg } = ctx;
1348
- const { getPkgDir, changelogTagPrefix } = config;
1092
+ const { getPkgDir, tagPrefix } = config;
1349
1093
  await tasks([{
1350
1094
  title: MSG.TASK.CHANGELOG.START,
1351
1095
  task: async () => {
1352
1096
  const changelog = await generateChangelog({
1353
1097
  getPkgDir: () => getPkgDir(selectedPkg),
1354
- tagPrefix: changelogTagPrefix?.(selectedPkg)
1098
+ tagPrefix: tagPrefix?.(selectedPkg)
1355
1099
  });
1356
1100
  await runGit(["add", `${getPkgDir(selectedPkg)}/CHANGELOG.md`]);
1357
1101
  Object.assign(ctx.github, { changelog });
@@ -1408,8 +1152,9 @@ var Action = class {
1408
1152
  await tasks([{
1409
1153
  title: MSG.TASK.GITHUB.START,
1410
1154
  task: async () => {
1155
+ const { github: { owner, repo }, git: { currentTag } } = ctx;
1411
1156
  await createRelease(ctx, config);
1412
- const url = getGithubReleaseUrl(ctx);
1157
+ const url = getGithubReleaseUrl(owner, repo, currentTag);
1413
1158
  return success(MSG.TASK.GITHUB.END(url), dryRun);
1414
1159
  }
1415
1160
  }]).catch((err) => abortOnError(err, ctx));
@@ -1439,7 +1184,7 @@ program.command("init").description("Create a release configuration file").optio
1439
1184
  packages: [ 'demo-a', 'demo-b' ],
1440
1185
  getPkgDir: (pkg) => \`packages/\${ pkg }\`,
1441
1186
  toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
1442
- changelogTagPrefix: (pkg) => \`\${ pkg }@\`,
1187
+ tagPrefix: (pkg) => \`\${ pkg }@\`,
1443
1188
  }`;
1444
1189
  writeFileSync(configPath, `import { defineConfig } from '@peiyanlu/release'
1445
1190
 
package/dist/index.d.mts CHANGED
@@ -24,7 +24,7 @@ interface ReleaseConfig {
24
24
  /** 使用 monorepo 时使用 toTag,例如 `${pkg}@${version}`,默认为 `v${version}` 作为单仓库 */
25
25
  toTag: (pkg: string, version: string) => string;
26
26
  /** 作为 CHANGELOG 标题的 Tag 前缀,多包场景下需要 `${pkg}@` */
27
- changelogTagPrefix: ((pkg: string) => string | undefined) | undefined;
27
+ tagPrefix: ((pkg: string) => string | undefined) | undefined;
28
28
  /**
29
29
  * Git 相关配置
30
30
  * 影响提交、打 tag、推送等本地/远程 Git 行为。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@peiyanlu/release",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Release project tool.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,19 +14,21 @@
14
14
  "types": "./dist/index.d.mts",
15
15
  "default": "./dist/index.mjs"
16
16
  }
17
- }
17
+ },
18
+ "./package.json": "./package.json"
18
19
  },
19
20
  "scripts": {
20
21
  "dev": "tsdown -w",
21
22
  "build": "tsdown",
22
23
  "prepublish": "tsdown",
24
+ "release": "tsdown && node index.js",
23
25
  "test": "vitest run",
24
- "release": "tsdown && node index.js"
26
+ "test:cov": "vitest run --coverage"
25
27
  },
26
28
  "dependencies": {
27
29
  "@clack/prompts": "^0.11.0",
28
30
  "@octokit/rest": "^22.0.1",
29
- "@peiyanlu/cli-utils": "^0.0.4",
31
+ "@peiyanlu/cli-utils": "^0.0.6",
30
32
  "@peiyanlu/ts-utils": "^0.0.4",
31
33
  "ansis": "^4.2.0",
32
34
  "commander": "^14.0.2",
@@ -42,9 +44,10 @@
42
44
  "@types/mime-types": "^3.0.1",
43
45
  "@types/node": "^24.10.3",
44
46
  "@types/semver": "^7.7.1",
47
+ "@vitest/coverage-v8": "^4.0.15",
45
48
  "tsdown": "^0.18.4",
46
49
  "typescript": "^5.9.3",
47
- "vitest": "^4.0.15"
50
+ "vitest": "^4.0.18"
48
51
  },
49
52
  "author": {
50
53
  "name": "YanluPei",