@peiyanlu/release 0.0.2 → 0.0.3

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 CHANGED
@@ -1,23 +1,24 @@
1
- import path, { dirname, join, resolve } from "node:path";
1
+ import path, { join, resolve } from "node:path";
2
2
  import { fileURLToPath, pathToFileURL } from "node:url";
3
3
  import { eol, execAsync, isGitRepo, parseGitHubRepo, readJsonFile, runGit, runNpm } from "@peiyanlu/cli-utils";
4
- import { blue, dim, gray, green, red, rgb, underline, yellow } from "ansis";
4
+ import { dim, gray, green, red, rgb, underline, yellow } from "ansis";
5
5
  import { program } from "commander";
6
6
  import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
7
7
  import { clean, coerce, gt, gte, inc, parse, prerelease, valid } from "semver";
8
+ import { castArray, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
8
9
  import { createReadStream, existsSync, statSync } from "node:fs";
9
- import { readFile, writeFile } from "node:fs/promises";
10
+ import { spawnSync } from "node:child_process";
11
+ import { inspect, styleText } from "node:util";
10
12
  import { Octokit } from "@octokit/rest";
11
13
  import { contentType } from "mime-types";
12
14
  import open from "open";
13
15
  import { glob } from "tinyglobby";
14
- import { inspect, styleText } from "node:util";
15
- import { castArray, isUndefined } from "@peiyanlu/ts-utils";
16
+ import { readFile, writeFile } from "node:fs/promises";
16
17
 
17
18
  //#region node_modules/.pnpm/tsdown@0.18.4_typescript@5.9.3/node_modules/tsdown/esm-shims.js
18
19
  const getFilename = () => fileURLToPath(import.meta.url);
19
20
  const getDirname = () => path.dirname(getFilename());
20
- const __dirname$1 = /* @__PURE__ */ getDirname();
21
+ const __dirname = /* @__PURE__ */ getDirname();
21
22
 
22
23
  //#endregion
23
24
  //#region src/config.ts
@@ -46,9 +47,7 @@ const resolveConfig = async (cwd, log$1) => {
46
47
  log$1?.(configFile);
47
48
  return loadConfig(configFile);
48
49
  };
49
- const toString = (self) => Object.prototype.toString.call(self);
50
- const isPlainObject = (v) => toString(v) === "[object Object]";
51
- const isFunction = (v) => toString(v) === "[object Function]";
50
+ const isFunction = (v) => typeof v === "function";
52
51
  const mergeConfig = (defaults, overrides) => {
53
52
  if (!overrides) return structuredClone(defaults);
54
53
  if (Object.keys(defaults).length === 0) return structuredClone(overrides);
@@ -67,15 +66,11 @@ const mergeConfig = (defaults, overrides) => {
67
66
 
68
67
  //#endregion
69
68
  //#region src/messages.ts
70
- const n = (b) => {
71
- return b ? "(dry run)" : "";
72
- };
73
- const t = () => {
74
- return green(Math.floor(process.uptime()));
75
- };
69
+ const n = (b) => b ? "(dry run)" : "";
70
+ const t = () => green(`${Math.floor(process.uptime())}s`);
76
71
  const MSG = {
77
72
  INTRO: (pkgName, dryRun) => `🚀 Starting release ${yellow(pkgName)} ${n(dryRun)}`,
78
- OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()}s ${n(dryRun)} `,
73
+ OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()} ${n(dryRun)}.`,
79
74
  LOG: {
80
75
  SHOW_VERSION: (version) => `🎉 Released ${green(version)}`,
81
76
  SHOW_CHANGELOG: "🎉 Changelog collected",
@@ -95,11 +90,15 @@ const MSG = {
95
90
  TASK: {
96
91
  VERSION: {
97
92
  START: "Bumping version",
98
- END: (version) => `Version bumped to ${green(version)}`
93
+ END: (to) => `Version bumped: ${underline(dim(to))}`
94
+ },
95
+ CHANGELOG: {
96
+ START: "Generating changelog",
97
+ END: `Changelog generated`
99
98
  },
100
99
  NPM: {
101
100
  START: "Publishing npm package",
102
- END: "NPM package published"
101
+ END: (url) => `NPM package published: ${underline(dim(url))}`
103
102
  },
104
103
  GIT: {
105
104
  START: "Creating git commit, tag & push",
@@ -107,7 +106,7 @@ const MSG = {
107
106
  },
108
107
  GITHUB: {
109
108
  START: "Creating GitHub release",
110
- END: "GitHub release created"
109
+ END: (url) => `GitHub release created: ${underline(dim(url))}`
111
110
  }
112
111
  },
113
112
  PROMPT: {
@@ -121,6 +120,7 @@ const MSG = {
121
120
  GIT_PUSH: "Push commits and tags to remote?"
122
121
  },
123
122
  ERROR: {
123
+ GIT_WORKDIR: `[git] Working dir must be clean`,
124
124
  GIT_REGISTRY: (name) => `[git] Repository not found for ${dim(name)}`,
125
125
  GIT_REMOTE: (name) => `[git] Remote "${dim(name)}" not found`,
126
126
  GITHUB_TAG_EXIT: (tag) => `[github] Release or tag "${tag}" already exists`,
@@ -134,322 +134,152 @@ const MSG = {
134
134
  };
135
135
 
136
136
  //#endregion
137
- //#region src/prompts.ts
138
- const abortTask = (msg$1) => {
139
- cancel(`${red("✕")} ${msg$1}`);
140
- process.exit(0);
141
- };
142
- const abortOnCancel = (value) => {
143
- if (isCancel(value)) abortTask("Operation cancelled.");
144
- };
145
- const abortOnError = (err) => {
146
- const msg$1 = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
147
- log.message(`\n${msg$1}\n`);
148
- process.exit(1);
149
- };
150
- const taskEnd = (msg$1) => {
151
- outro(msg$1);
152
- process.exit(0);
153
- };
154
- const msg = (prefix, msg$1) => {
155
- log.message(gray`${rgb(33, 91, 184)(`[${prefix}]`)} ${msg$1}`);
156
- };
157
- const prompt = (msg$1) => {
158
- return `${blue`?`} ${msg$1}`;
159
- };
160
-
161
- //#endregion
162
- //#region src/version/bump.ts
163
- const isPreRelease = (version) => {
164
- return Boolean(prerelease(version));
165
- };
166
- const isValid = (version) => {
167
- return Boolean(valid(version));
168
- };
169
- const parseVersion = (raw) => {
170
- const next = isValid(raw) ? raw : coerce(raw)?.toString();
171
- if (!next) return {};
172
- const { prerelease: prerelease$1 } = parse(next);
173
- const toPreRelease = Boolean(prerelease$1.length);
174
- const [preId, preBase] = prerelease$1.map(String);
175
- return {
176
- next,
177
- toPreRelease,
178
- preId,
179
- preBase
180
- };
181
- };
182
- const getCIVersion = (current) => {
183
- return inc(current, Release.PATCH);
137
+ //#region src/github/release.ts
138
+ const getGithubUrl = (owner, repo) => {
139
+ return `https://github.com/${owner}/${repo}`;
184
140
  };
185
-
186
- //#endregion
187
- //#region src/version/prompts.ts
188
- let Release = /* @__PURE__ */ function(Release$1) {
189
- Release$1["MAJOR"] = "major";
190
- Release$1["MINOR"] = "minor";
191
- Release$1["PATCH"] = "patch";
192
- return Release$1;
193
- }({});
194
- let PreRelease = /* @__PURE__ */ function(PreRelease$1) {
195
- PreRelease$1["MAJOR"] = "premajor";
196
- PreRelease$1["MINOR"] = "preminor";
197
- PreRelease$1["PATCH"] = "prepatch";
198
- PreRelease$1["RELEASE"] = "prerelease";
199
- return PreRelease$1;
200
- }({});
201
- let PreId = /* @__PURE__ */ function(PreId$1) {
202
- PreId$1["ALPHA"] = "alpha";
203
- PreId$1["BETA"] = "beta";
204
- PreId$1["RC"] = "rc";
205
- PreId$1["NET"] = "next";
206
- PreId$1["CANARY"] = "canary";
207
- PreId$1["NIGHT"] = "nightly";
208
- PreId$1["DEV"] = "dev";
209
- return PreId$1;
210
- }({});
211
- const diff = (from, to) => {
212
- const a = from.split(".");
213
- return to.split(".").map((v, i) => v === a[i] ? dim(v) : green(v)).join(".");
141
+ const truncateBody = (body) => {
142
+ if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
143
+ return body;
214
144
  };
215
- const runVersionPrompts = async (ctx, config) => {
216
- const { pkg: { current, fromPreRelease } } = ctx;
217
- const selectVersion = async () => {
218
- const patch = inc(current, Release.PATCH);
219
- const minor = inc(current, Release.MINOR);
220
- const major = inc(current, Release.MAJOR);
221
- const prerelease$1 = inc(current, PreRelease.RELEASE);
222
- const prepatch = inc(current, PreRelease.PATCH, PreId.BETA);
223
- const preminor = inc(current, PreRelease.MINOR, PreId.BETA);
224
- const premajor = inc(current, PreRelease.MAJOR, PreId.BETA);
225
- const pr = [{
226
- label: "Pre-Release",
227
- value: prerelease$1,
228
- hint: prerelease$1
229
- }];
230
- const pre = [
231
- {
232
- label: "Pre-Patch",
233
- value: prepatch,
234
- hint: prepatch
235
- },
236
- {
237
- label: "Pre-Minor",
238
- value: preminor,
239
- hint: preminor
240
- },
241
- {
242
- label: "Pre-Major",
243
- value: premajor,
244
- hint: premajor
245
- }
246
- ];
247
- const release = [
248
- {
249
- label: "Patch",
250
- value: patch,
251
- hint: patch
252
- },
253
- {
254
- label: "Minor",
255
- value: minor,
256
- hint: minor
257
- },
258
- {
259
- label: "Major",
260
- value: major,
261
- hint: major
262
- }
263
- ];
264
- const custom = [{
265
- label: "As-Is",
266
- value: "ignore",
267
- hint: current
268
- }, {
269
- label: "Custom",
270
- value: "custom",
271
- hint: "custom specified"
272
- }];
273
- const version = await select({
274
- message: prompt(MSG.PROMPT.SELECT_VERSION),
275
- options: [
276
- ...fromPreRelease ? pr : [],
277
- ...release,
278
- ...fromPreRelease ? [] : pre,
279
- ...custom
280
- ],
281
- initialValue: fromPreRelease ? prerelease$1 : patch
282
- });
283
- abortOnCancel(version);
284
- return version;
285
- };
286
- const inputVersion = async () => {
287
- const version = await text({
288
- message: prompt(MSG.PROMPT.INPUT_VERSION),
289
- initialValue: current,
290
- validate: (val) => {
291
- if (!isValid(val)) return "Invalid version";
145
+ const uploadAssets = async (ctx, config) => {
146
+ const { github: { assets } } = config;
147
+ const { github: { isReleased, owner, repo, uploadUrl, releaseId, token }, pkg: { name } } = ctx;
148
+ if (!assets?.length || !isReleased) return;
149
+ const octokit = new Octokit({ auth: token });
150
+ const files = await glob(assets);
151
+ await Promise.all(files.map((file) => {
152
+ return octokit.repos.uploadReleaseAsset({
153
+ owner,
154
+ repo,
155
+ name,
156
+ url: uploadUrl,
157
+ release_id: releaseId,
158
+ data: createReadStream(file),
159
+ headers: {
160
+ "content-type": contentType(file) || "application/octet-stream",
161
+ "content-length": statSync(file).size
292
162
  }
293
163
  });
294
- abortOnCancel(version);
295
- return version;
296
- };
297
- const type = await selectVersion();
298
- const next = type === "custom" ? await inputVersion() : type === "ignore" ? current : type;
299
- const parsed = parseVersion(next);
300
- Object.assign(ctx.pkg, { ...parsed });
301
- ctx.isIncrement = gte(next, current);
302
- log.message(gray`from ${dim(current)}${eol()} to ${diff(current, next)}`);
303
- return { next };
304
- };
305
-
306
- //#endregion
307
- //#region src/defaults.ts
308
- const createDefaultContext = () => {
309
- return {
310
- ci: false,
311
- release: false,
312
- dryRun: false,
313
- changelog: false,
314
- increment: "",
315
- isIncrement: false,
316
- configFileExists: false,
317
- pkg: {
318
- current: "",
319
- next: "",
320
- name: "",
321
- isPrivate: false,
322
- fromPreRelease: false,
323
- toPreRelease: false,
324
- publishConfig: {
325
- access: "public",
326
- registry: "https://registry.npmjs.org"
327
- },
328
- preId: PreId.BETA,
329
- preBase: "0"
330
- },
331
- npm: {
332
- username: "",
333
- otp: ""
334
- },
335
- git: {
336
- remoteName: "origin",
337
- remoteUrl: "",
338
- latestTag: "",
339
- previousTag: "",
340
- currentTag: "",
341
- isCommitted: false,
342
- isTagged: false
343
- },
344
- github: {
345
- username: "",
346
- repo: "",
347
- changelog: "",
348
- owner: "",
349
- isWeb: false,
350
- skipChecks: false,
351
- isReleased: false,
352
- releaseId: 0,
353
- releaseUrl: "",
354
- uploadUrl: "",
355
- discussionUrl: "",
356
- token: ""
357
- }
358
- };
164
+ }));
359
165
  };
360
- const createDefaultConfig = (isCI) => {
361
- return {
362
- hooks: {
363
- "before:bump": "",
364
- "after:bump": "",
365
- "before:publish": "",
366
- "after:publish": "",
367
- "before:push": "",
368
- "after:push": "",
369
- "before:release": "",
370
- "after:release": ""
371
- },
372
- git: {
373
- commit: isCI,
374
- tag: isCI,
375
- push: isCI,
376
- commitMessage: "chore(release): ${version}",
377
- tagMessage: "Release ${version}",
378
- tagName: "${version}"
379
- },
380
- npm: {
381
- publish: isCI,
382
- tag: "",
383
- allowSameVersion: false
384
- },
385
- github: {
386
- release: isCI,
387
- releaseName: "Release ${version}",
388
- prerelease: false,
389
- draft: false,
390
- tokenRef: "GITHUB_TOKEN",
391
- assets: []
392
- }
393
- };
166
+ const createWebRelease = async (ctx, config) => {
167
+ const { pkg: { next, toPreRelease }, github: { owner, repo, changelog, releaseName } } = ctx;
168
+ const { github: { prerelease: prerelease$1 } } = config;
169
+ const github = getGithubUrl(owner, repo);
170
+ const url = new URL(`${github}/releases/new`);
171
+ url.searchParams.set("tag", next);
172
+ url.searchParams.set("title", releaseName ?? `Release ${next}`);
173
+ url.searchParams.set("body", truncateBody(changelog));
174
+ url.searchParams.set("prelease", String(toPreRelease || prerelease$1));
175
+ const isWindows = process.platform === "win32";
176
+ await open(url.toString(), { wait: isWindows });
394
177
  };
395
-
396
- //#endregion
397
- //#region src/utils.ts
398
- const __filename = fileURLToPath(import.meta.url);
399
- const __dirname = dirname(__filename);
400
- const info = (msg$1) => {
401
- console.log(`${rgb(33, 91, 184)(`ℹ`)} ${gray(msg$1)}`);
178
+ const createCiRelease = async (ctx, config) => {
179
+ const { pkg: { next, toPreRelease }, github: { owner, repo, token, changelog, releaseName } } = ctx;
180
+ const { github: { prerelease: prerelease$1, draft, autoGenerate } } = config;
181
+ const octokit = new Octokit({ auth: token });
182
+ try {
183
+ const { data } = await octokit.repos.createRelease({
184
+ owner,
185
+ repo,
186
+ tag_name: next,
187
+ target_commitish: void 0,
188
+ name: releaseName ?? `Release ${next}`,
189
+ body: autoGenerate ? "" : truncateBody(changelog),
190
+ draft: draft ?? false,
191
+ prerelease: (toPreRelease || prerelease$1) ?? false,
192
+ discussion_category_name: void 0,
193
+ generate_release_notes: autoGenerate,
194
+ make_latest: "true"
195
+ });
196
+ const { html_url, upload_url, id, discussion_url } = data;
197
+ Object.assign(ctx.github, {
198
+ isReleased: true,
199
+ releaseId: id,
200
+ releaseUrl: html_url,
201
+ uploadUrl: upload_url,
202
+ discussionUrl: discussion_url
203
+ });
204
+ } catch (e) {
205
+ if (e?.status === 422) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(next));
206
+ throw e;
207
+ }
402
208
  };
403
- const success = (msg$1, dryRun) => {
404
- return `${(dryRun ? rgb(123, 115, 66) : green)("✔")} ${msg$1}`;
209
+ const parseGithubUrl = (ctx) => {
210
+ const { git: { remoteUrl } } = ctx;
211
+ const [owner, repo] = parseGitHubRepo(remoteUrl);
212
+ Object.assign(ctx.github, {
213
+ owner,
214
+ repo
215
+ });
405
216
  };
406
- const runLifeCycleHook = async (hooks, key, dryRun) => {
407
- const handler = hooks[key];
408
- if (typeof handler === "function") {
409
- if (!dryRun) await handler();
410
- log.success(success(`${dim`run`} ${inspect(handler)}`, dryRun));
411
- return;
217
+ const createRelease = async (ctx, config) => {
218
+ const { github: { owner, repo }, isCI } = ctx;
219
+ const { github: { tokenRef, release, skipChecks } } = config;
220
+ if (!release) return;
221
+ const token = tokenRef ? process.env[tokenRef] : process.env.GITHUB_TOKEN;
222
+ if (!token) {
223
+ Object.assign(ctx.github, { isWeb: true });
224
+ if (isCI && process.env.GITHUB_ACTIONS) throw new Error(MSG.ERROR.GITHUB_TOKEN(tokenRef));
225
+ }
226
+ Object.assign(ctx.github, { token });
227
+ const octokit = new Octokit({ auth: token });
228
+ if (token && !skipChecks) if (process.env.GITHUB_ACTIONS) Object.assign(ctx.github, { username: process.env.GITHUB_ACTOR });
229
+ else {
230
+ try {
231
+ const { data: { login: username } } = await octokit.users.getAuthenticated();
232
+ Object.assign(ctx.github, { username });
233
+ } catch (e) {
234
+ throw new Error(MSG.ERROR.GITHUB_AUTH);
235
+ }
236
+ try {
237
+ const { github: { username } } = ctx;
238
+ await octokit.repos.checkCollaborator({
239
+ owner,
240
+ repo,
241
+ username
242
+ });
243
+ } catch (e) {
244
+ throw new Error(MSG.ERROR.GITHUB_USER);
245
+ }
246
+ }
247
+ const { github: { isWeb }, dryRun } = ctx;
248
+ if (dryRun) return true;
249
+ if (isWeb) await createWebRelease(ctx, config);
250
+ else if (isCI) {
251
+ await createCiRelease(ctx, config);
252
+ await uploadAssets(ctx, config);
412
253
  }
413
- return Promise.allSettled(castArray(handler).filter(Boolean).map(async (hook) => {
414
- if (!dryRun) await execAsync(hook, { shell: true }).catch(console.error);
415
- log.success(success(hook.replace("echo", dim("echo")), dryRun));
416
- }));
417
254
  };
418
- const format = (str, ctx) => {
419
- const { pkg: { next } } = ctx;
420
- return str.replace("${version}", next);
255
+ const getGithubReleaseUrl = (ctx) => {
256
+ const { github: { owner, repo }, git: { currentTag } } = ctx;
257
+ return `${getGithubUrl(owner, repo)}/releases/tag/${normalizeTag(currentTag)}`;
421
258
  };
422
259
 
423
260
  //#endregion
424
261
  //#region src/git/commit.ts
425
- const runGitProxy = (...args) => {
426
- if (process.env["dryRun"]) log.message(`${dim("$")} ${gray(["git", ...args[0]].join(" "))}`);
427
- return runGit(...args);
428
- };
429
262
  const getBranchName = () => {
430
- return runGitProxy([
263
+ return runGit([
431
264
  "rev-parse",
432
265
  "--abbrev-ref",
433
266
  "HEAD"
434
267
  ]);
435
268
  };
436
269
  const getRemoteForBranch = (branch) => {
437
- return runGitProxy([
270
+ return runGit([
438
271
  "config",
439
272
  "--get",
440
273
  `branch.${branch}.remote`
441
274
  ]);
442
275
  };
443
276
  const getStatus = () => {
444
- return runGitProxy([
277
+ return runGit([
445
278
  "status",
446
279
  "--short",
447
280
  "--untracked-files=no"
448
281
  ], { trim: false });
449
282
  };
450
- const gitRestore = () => {
451
- return runGitProxy(["restore", "."]);
452
- };
453
283
  const coloredChangeset = (log$1) => {
454
284
  const colorStatusChar = (ch) => {
455
285
  switch (ch) {
@@ -471,11 +301,11 @@ const getRemote = async () => {
471
301
  const remoteName = branch && await getRemoteForBranch(branch) || "origin";
472
302
  return {
473
303
  remoteName,
474
- remoteUrl: await runGitProxy([
304
+ remoteUrl: await runGit([
475
305
  "remote",
476
306
  "get-url",
477
307
  remoteName
478
- ]).catch((_) => runGitProxy([
308
+ ]).catch((_) => runGit([
479
309
  "config",
480
310
  "--get",
481
311
  `remote.${remoteName}.url`
@@ -483,7 +313,7 @@ const getRemote = async () => {
483
313
  };
484
314
  };
485
315
  const getLatestTag = async (match = "*", exclude = "*-beta.*") => {
486
- return runGitProxy([
316
+ return runGit([
487
317
  "describe",
488
318
  "--tags",
489
319
  "--abbrev=0",
@@ -492,11 +322,11 @@ const getLatestTag = async (match = "*", exclude = "*-beta.*") => {
492
322
  ]);
493
323
  };
494
324
  const getPreviousTag = async (current) => {
495
- return runGitProxy([
325
+ return runGit([
496
326
  "describe",
497
327
  "--tags",
498
328
  "--abbrev=0",
499
- `${await runGitProxy([
329
+ `${await runGit([
500
330
  "rev-list",
501
331
  "--tags",
502
332
  current || "--skip=1",
@@ -524,98 +354,100 @@ const getLog = async (print, isIncrement) => {
524
354
  const cmd = ["log", `--pretty=format:${print ? "* %s (%h)" : "%s %h %H"}`];
525
355
  const { from, to } = await resolveChangelogRange(isIncrement);
526
356
  if (from) cmd.push(`${from}...${to}`);
527
- return runGitProxy(cmd, { trim: false });
357
+ return runGit(cmd, { trim: false });
528
358
  };
529
- const normalizeTag = (tag) => clean(tag) ?? tag;
530
- const resolveTag$1 = async (ctx, config) => {
531
- const { git: { tagName: tagTemplate } } = config;
532
- const latestTag = await getLatestTag() ?? "";
533
- const currentTag = format(tagTemplate || ((latestTag || "").match(/^v/) ? "v${version}" : "${version}"), ctx);
534
- Object.assign(ctx.git, {
535
- latestTag,
536
- currentTag
537
- });
538
- return true;
539
- };
540
- const gitCheck = async (ctx, config) => {
541
- const { pkg: { name } } = ctx;
542
- if (!await isGitRepo()) throw new Error(MSG.ERROR.GIT_REGISTRY(name));
543
- const { remoteName, remoteUrl } = await getRemote();
544
- if (!remoteUrl) throw new Error(MSG.ERROR.GIT_REMOTE(name));
545
- Object.assign(ctx.git, {
546
- remoteName,
547
- remoteUrl
548
- });
549
- parseGithubUrl(ctx);
359
+ const isWorkingDirClean = async () => {
360
+ return (await runGit(["status", "-s"]))?.length === 0;
550
361
  };
551
362
  const hasUpstreamBranch = async () => {
552
- const branch = await runGitProxy([
363
+ const branch = await runGit([
553
364
  "for-each-ref",
554
365
  "--format=%(upstream:short)",
555
- await runGitProxy(["symbolic-ref", "HEAD"])
366
+ await runGit(["symbolic-ref", "HEAD"])
556
367
  ]);
557
368
  return Boolean(branch);
558
369
  };
559
370
  const getUpstreamArgs = async (remoteName) => {
560
371
  const hasUpstream = await hasUpstreamBranch();
561
372
  const branch = await getBranchName();
562
- if (!hasUpstream) return remoteName ? [
563
- "--set-upstream",
564
- remoteName,
565
- branch
566
- ] : [
373
+ if (!hasUpstream) return [
567
374
  "--set-upstream",
568
- "origin",
375
+ remoteName || "origin",
569
376
  branch
570
377
  ];
571
378
  if (remoteName) return [remoteName];
572
379
  return [];
573
380
  };
381
+ const normalizeTag = (tag) => clean(tag) ?? tag;
382
+ const gitCheck = async (ctx, config) => {
383
+ const { pkg: { name } } = ctx;
384
+ const { git: { requireRepository, requireRemote, requireWorkDirClean } } = config;
385
+ if (!await isGitRepo()) if (requireRepository) throw new Error(MSG.ERROR.GIT_REGISTRY(name));
386
+ else {
387
+ ctx.noGit = true;
388
+ return;
389
+ }
390
+ if (requireWorkDirClean && !await isWorkingDirClean()) throw new Error(MSG.ERROR.GIT_WORKDIR);
391
+ const { remoteName, remoteUrl } = await getRemote();
392
+ if (!remoteUrl) if (requireRemote) throw new Error(MSG.ERROR.GIT_REMOTE(name));
393
+ else {
394
+ ctx.noGitHub = true;
395
+ return;
396
+ }
397
+ Object.assign(ctx.git, {
398
+ remoteName,
399
+ remoteUrl
400
+ });
401
+ parseGithubUrl(ctx);
402
+ };
574
403
  const commitAndTag = async (ctx, config) => {
575
- const { git: { currentTag, remoteName }, dryRun } = ctx;
576
- const { git: { commit, tag, push, tagMessage, commitMessage } } = config;
404
+ const { git: { remoteName, currentTag, commitMessage, tagMessage }, dryRun } = ctx;
405
+ const { git: { commit, tag, push, addUntrackedFiles, commitArgs, tagArgs, pushArgs } } = config;
577
406
  if (!commit) return;
578
407
  const args = dryRun ? ["--dry-run"] : [];
579
- await runGitProxy([
408
+ await runGit([
580
409
  "add",
581
410
  ".",
582
- "--all",
411
+ addUntrackedFiles ? "--all" : "--update",
583
412
  ...args
584
413
  ]);
585
- await runGitProxy([
414
+ await runGit([
586
415
  "commit",
587
416
  "--message",
588
- format(commitMessage, ctx),
417
+ commitMessage,
418
+ ...commitArgs,
589
419
  ...args
590
420
  ]);
591
421
  if (!dryRun) Object.assign(ctx.git, { isCommitted: true });
592
422
  if (tag) {
593
- await runGitProxy([
423
+ await runGit([
594
424
  "tag",
595
425
  "--annotate",
596
426
  "--message",
597
- format(tagMessage, ctx),
427
+ tagMessage,
598
428
  currentTag,
429
+ ...tagArgs,
599
430
  ...args
600
431
  ]);
601
432
  if (!dryRun) Object.assign(ctx.git, { isTagged: true });
602
433
  }
603
- if (push) await runGitProxy([
434
+ if (push) await runGit([
604
435
  "push",
605
436
  ...await getUpstreamArgs(remoteName),
606
437
  "--follow-tags",
438
+ ...pushArgs,
607
439
  ...args
608
440
  ]);
609
441
  };
610
- const gitRollback = async (ctx) => {
442
+ const gitRollback$1 = (ctx) => {
611
443
  const { git: { currentTag, isCommitted, isTagged } } = ctx;
612
- await gitRestore();
613
- if (isTagged) await runGitProxy([
444
+ spawnSync("git", ["restore", "."]);
445
+ if (isTagged) spawnSync("git", [
614
446
  "tag",
615
447
  "--delete",
616
448
  currentTag
617
449
  ]);
618
- await runGitProxy([
450
+ spawnSync("git", [
619
451
  "reset",
620
452
  "--hard",
621
453
  isCommitted ? "HEAD~1" : "HEAD"
@@ -623,123 +455,344 @@ const gitRollback = async (ctx) => {
623
455
  };
624
456
 
625
457
  //#endregion
626
- //#region src/github/release.ts
627
- const getGithubUrl = (owner, repo) => {
628
- return `https://github.com/${owner}/${repo}`;
458
+ //#region src/utils.ts
459
+ const info = (msg$1) => {
460
+ console.log(`${rgb(33, 91, 184)(`i`)} ${gray(msg$1)}`);
629
461
  };
630
- const truncateBody = (body) => {
631
- if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
632
- return body;
462
+ const msg = (prefix, msg$1) => {
463
+ log.message(gray`${rgb(33, 91, 184)(`[${prefix}]`)} ${msg$1}`);
633
464
  };
634
- const uploadAssets = async (ctx, config) => {
635
- const { github: { assets } } = config;
636
- const { github: { isReleased, owner, repo, uploadUrl, releaseId, token }, pkg: { name } } = ctx;
637
- if (!assets?.length || !isReleased) return;
638
- const octokit = new Octokit({ auth: token });
639
- const files = await glob(assets);
640
- await Promise.all(files.map((file) => {
641
- return octokit.repos.uploadReleaseAsset({
642
- owner,
643
- repo,
644
- name,
645
- url: uploadUrl,
646
- release_id: releaseId,
647
- data: createReadStream(file),
648
- headers: {
649
- "content-type": contentType(file) || "application/octet-stream",
650
- "content-length": statSync(file).size
465
+ const question = (msg$1, type) => {
466
+ return `${{
467
+ version: green,
468
+ git: rgb(220, 94, 62),
469
+ npm: rgb(186, 70, 61),
470
+ github: rgb(64, 110, 228)
471
+ }[type]`?`} ${msg$1}`;
472
+ };
473
+ const success = (msg$1, dryRun) => {
474
+ return `${(dryRun ? rgb(123, 115, 66) : green)("✔")} ${msg$1}`;
475
+ };
476
+ const runLifeCycleHook = async (hooks, key, dryRun) => {
477
+ const handler = hooks[key];
478
+ if (typeof handler === "function") {
479
+ if (!dryRun) await handler();
480
+ log.success(success(`${dim`run`} ${inspect(handler)}`, dryRun));
481
+ return;
482
+ }
483
+ return Promise.allSettled(castArray(handler).filter(Boolean).map(async (hook) => {
484
+ if (!dryRun) await execAsync(hook).catch(log.error);
485
+ log.success(success(hook.replace(/^echo$/, dim("echo")), dryRun));
486
+ }));
487
+ };
488
+ const gitRollback = (ctx) => {
489
+ const { git: { currentTag, isCommitted, isTagged } } = ctx;
490
+ spawnSync("git", ["restore", "."]);
491
+ if (isTagged) spawnSync("git", [
492
+ "tag",
493
+ "--delete",
494
+ currentTag
495
+ ]);
496
+ spawnSync("git", [
497
+ "reset",
498
+ "--hard",
499
+ isCommitted ? "HEAD~1" : "HEAD"
500
+ ]);
501
+ };
502
+ const diff = (from, to, separator = ".") => {
503
+ const a = from.split(separator);
504
+ return to.split(separator).map((v, i) => v === a[i] ? dim(v) : green(v)).join(separator);
505
+ };
506
+ const formatTemplate = async (ctx, config) => {
507
+ const { pkg: { next } } = ctx;
508
+ const { git: { commitMessage: cm, tagMessage: tm, tagName }, github: { releaseName: rm } } = config;
509
+ const f = (s, v) => s.replace("${version}", v);
510
+ const latestTag = await getLatestTag() ?? "";
511
+ const currentTag = f(tagName || (latestTag.match(/^v/) ? "v${version}" : "${version}"), next);
512
+ const commitMessage = f(cm, next);
513
+ const tagMessage = f(tm, next);
514
+ const releaseName = f(rm, next);
515
+ Object.assign(ctx.git, {
516
+ latestTag,
517
+ currentTag,
518
+ commitMessage,
519
+ tagMessage
520
+ });
521
+ Object.assign(ctx.github, { releaseName });
522
+ };
523
+
524
+ //#endregion
525
+ //#region src/prompts.ts
526
+ const abortTask = (msg$1) => {
527
+ cancel(`${red("✕")} ${msg$1}`);
528
+ process.exit(0);
529
+ };
530
+ const abortSinglePrompt = (value) => {
531
+ if (isCancel(value)) abortTask("Operation cancelled.");
532
+ };
533
+ const abortGroupPrompt = (ctx) => {
534
+ ctx && gitRollback(ctx);
535
+ abortTask("Operation cancelled.");
536
+ };
537
+ const abortOnError = (err, ctx) => {
538
+ ctx && gitRollback(ctx);
539
+ const msg$1 = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
540
+ log.message(`\n${msg$1}\n`);
541
+ process.exit(1);
542
+ };
543
+ const taskEnd = (msg$1) => {
544
+ outro(msg$1);
545
+ process.exit(0);
546
+ };
547
+
548
+ //#endregion
549
+ //#region src/version/bump.ts
550
+ const isPreRelease = (version) => {
551
+ return Boolean(prerelease(version));
552
+ };
553
+ const isValid = (version) => {
554
+ return Boolean(valid(version));
555
+ };
556
+ const parseVersion = (raw) => {
557
+ const next = isValid(raw) ? raw : coerce(raw)?.toString();
558
+ if (!next) return {};
559
+ const { prerelease: prerelease$1 } = parse(next);
560
+ const toPreRelease = Boolean(prerelease$1.length);
561
+ const [preId, preBase] = prerelease$1.map(String);
562
+ return {
563
+ next,
564
+ toPreRelease,
565
+ preId,
566
+ preBase
567
+ };
568
+ };
569
+ const getCIVersion = (current) => {
570
+ return inc(current, Release.PATCH) ?? "";
571
+ };
572
+
573
+ //#endregion
574
+ //#region src/version/prompts.ts
575
+ let Release = /* @__PURE__ */ function(Release$1) {
576
+ Release$1["MAJOR"] = "major";
577
+ Release$1["MINOR"] = "minor";
578
+ Release$1["PATCH"] = "patch";
579
+ return Release$1;
580
+ }({});
581
+ let PreRelease = /* @__PURE__ */ function(PreRelease$1) {
582
+ PreRelease$1["MAJOR"] = "premajor";
583
+ PreRelease$1["MINOR"] = "preminor";
584
+ PreRelease$1["PATCH"] = "prepatch";
585
+ PreRelease$1["RELEASE"] = "prerelease";
586
+ return PreRelease$1;
587
+ }({});
588
+ let PreId = /* @__PURE__ */ function(PreId$1) {
589
+ PreId$1["ALPHA"] = "alpha";
590
+ PreId$1["BETA"] = "beta";
591
+ PreId$1["RC"] = "rc";
592
+ PreId$1["NET"] = "next";
593
+ PreId$1["CANARY"] = "canary";
594
+ PreId$1["NIGHT"] = "nightly";
595
+ PreId$1["DEV"] = "dev";
596
+ return PreId$1;
597
+ }({});
598
+ const runVersionPrompts = async (ctx, config) => {
599
+ const { pkg: { current, fromPreRelease } } = ctx;
600
+ const selectVersion = async () => {
601
+ const patch = inc(current, Release.PATCH);
602
+ const minor = inc(current, Release.MINOR);
603
+ const major = inc(current, Release.MAJOR);
604
+ const prerelease$1 = inc(current, PreRelease.RELEASE);
605
+ const prepatch = inc(current, PreRelease.PATCH, PreId.BETA);
606
+ const preminor = inc(current, PreRelease.MINOR, PreId.BETA);
607
+ const premajor = inc(current, PreRelease.MAJOR, PreId.BETA);
608
+ const pr = [{
609
+ label: "Pre-Release",
610
+ value: prerelease$1,
611
+ hint: prerelease$1
612
+ }];
613
+ const pre = [
614
+ {
615
+ label: "Pre-Patch",
616
+ value: prepatch,
617
+ hint: prepatch
618
+ },
619
+ {
620
+ label: "Pre-Minor",
621
+ value: preminor,
622
+ hint: preminor
623
+ },
624
+ {
625
+ label: "Pre-Major",
626
+ value: premajor,
627
+ hint: premajor
651
628
  }
629
+ ];
630
+ const release = [
631
+ {
632
+ label: "Patch",
633
+ value: patch,
634
+ hint: patch
635
+ },
636
+ {
637
+ label: "Minor",
638
+ value: minor,
639
+ hint: minor
640
+ },
641
+ {
642
+ label: "Major",
643
+ value: major,
644
+ hint: major
645
+ }
646
+ ];
647
+ const custom = [{
648
+ label: "As-Is",
649
+ value: "ignore",
650
+ hint: current
651
+ }, {
652
+ label: "Custom",
653
+ value: "custom",
654
+ hint: "custom specified"
655
+ }];
656
+ const version = await select({
657
+ message: question(MSG.PROMPT.SELECT_VERSION, "version"),
658
+ options: [
659
+ ...fromPreRelease ? pr : [],
660
+ ...release,
661
+ ...fromPreRelease ? [] : pre,
662
+ ...custom
663
+ ],
664
+ initialValue: fromPreRelease ? prerelease$1 : patch
652
665
  });
653
- }));
654
- };
655
- const createWebRelease = async (ctx, config) => {
656
- const { pkg: { next, toPreRelease }, github: { owner, repo, changelog } } = ctx;
657
- const { github: { prerelease: prerelease$1, releaseName } } = config;
658
- const github = getGithubUrl(owner, repo);
659
- const url = new URL(`${github}/releases/new`);
660
- url.searchParams.set("tag", next);
661
- url.searchParams.set("title", format(releaseName, ctx) ?? `Release ${next}`);
662
- url.searchParams.set("body", truncateBody(changelog));
663
- url.searchParams.set("prelease", String(toPreRelease || prerelease$1));
664
- const isWindows = process.platform === "win32";
665
- await open(url.toString(), { wait: isWindows });
666
- };
667
- const createCiRelease = async (ctx, config) => {
668
- const { pkg: { next, toPreRelease }, github: { owner, repo, token, changelog } } = ctx;
669
- const { github: { prerelease: prerelease$1, releaseName, draft } } = config;
670
- const octokit = new Octokit({ auth: token });
671
- try {
672
- const { data } = await octokit.repos.createRelease({
673
- owner,
674
- repo,
675
- tag_name: next,
676
- name: format(releaseName, ctx) ?? `Release ${next}`,
677
- body: truncateBody(changelog),
678
- draft: draft ?? false,
679
- prerelease: (toPreRelease || prerelease$1) ?? false
680
- });
681
- const { html_url, upload_url, id, discussion_url } = data;
682
- Object.assign(ctx.github, {
683
- isReleased: true,
684
- releaseId: id,
685
- releaseUrl: html_url,
686
- uploadUrl: upload_url,
687
- discussionUrl: discussion_url
666
+ abortSinglePrompt(version);
667
+ return version;
668
+ };
669
+ const inputVersion = async () => {
670
+ const version = await text({
671
+ message: question(MSG.PROMPT.INPUT_VERSION, "version"),
672
+ initialValue: current,
673
+ validate: (val) => {
674
+ if (!isValid(val)) return "Invalid version";
675
+ }
688
676
  });
689
- } catch (e) {
690
- if (e?.status === 422) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(next));
691
- throw e;
692
- }
693
- };
694
- const parseGithubUrl = (ctx) => {
695
- const { git: { remoteUrl } } = ctx;
696
- const [owner, repo] = parseGitHubRepo(remoteUrl);
697
- Object.assign(ctx.github, {
698
- owner,
699
- repo
700
- });
677
+ abortSinglePrompt(version);
678
+ return version;
679
+ };
680
+ const type = await selectVersion();
681
+ const next = type === "custom" ? await inputVersion() : type === "ignore" ? current : type;
682
+ ctx.isIncrement = gte(next, current);
683
+ return { next };
701
684
  };
702
- const createRelease = async (ctx, config) => {
703
- const { github: { owner, repo, skipChecks }, ci } = ctx;
704
- const { github: { tokenRef, release } } = config;
705
- if (!release) return;
706
- const token = tokenRef ? process.env[tokenRef] : process.env.GITHUB_TOKEN;
707
- if (!token) {
708
- Object.assign(ctx.github, { isWeb: true });
709
- if (ci && process.env.GITHUB_ACTIONS) throw new Error(MSG.ERROR.GITHUB_TOKEN(tokenRef));
710
- }
711
- Object.assign(ctx.github, { token });
712
- const octokit = new Octokit({ auth: token });
713
- if (token && !skipChecks) if (process.env.GITHUB_ACTIONS) Object.assign(ctx.github, { username: process.env.GITHUB_ACTOR });
714
- else {
715
- try {
716
- const { data: { login: username } } = await octokit.users.getAuthenticated();
717
- Object.assign(ctx.github, { username });
718
- } catch (e) {
719
- throw new Error(MSG.ERROR.GITHUB_AUTH);
720
- }
721
- try {
722
- const { github: { username } } = ctx;
723
- await octokit.repos.checkCollaborator({
724
- owner,
725
- repo,
726
- username
727
- });
728
- } catch (e) {
729
- throw new Error(MSG.ERROR.GITHUB_USER);
685
+
686
+ //#endregion
687
+ //#region src/defaults.ts
688
+ const createDefaultContext = () => {
689
+ return {
690
+ isCI: false,
691
+ release: false,
692
+ showRelease: false,
693
+ showChangelog: false,
694
+ dryRun: false,
695
+ noGit: false,
696
+ noNpm: false,
697
+ noGitHub: false,
698
+ increment: "",
699
+ isIncrement: false,
700
+ configFileExists: false,
701
+ pkg: {
702
+ current: "",
703
+ next: "",
704
+ name: "",
705
+ isPrivate: false,
706
+ fromPreRelease: false,
707
+ toPreRelease: false,
708
+ publishConfig: {
709
+ access: "public",
710
+ registry: "https://registry.npmjs.org"
711
+ },
712
+ preId: PreId.BETA,
713
+ preBase: "0"
714
+ },
715
+ npm: {
716
+ username: "",
717
+ otp: "",
718
+ tag: "latest"
719
+ },
720
+ git: {
721
+ remoteName: "origin",
722
+ remoteUrl: "",
723
+ latestTag: "",
724
+ previousTag: "",
725
+ currentTag: "",
726
+ isCommitted: false,
727
+ isTagged: false,
728
+ commitMessage: "",
729
+ tagMessage: "",
730
+ tagName: ""
731
+ },
732
+ github: {
733
+ username: "",
734
+ repo: "",
735
+ changelog: "",
736
+ owner: "",
737
+ isWeb: false,
738
+ isReleased: false,
739
+ releaseId: 0,
740
+ releaseUrl: "",
741
+ uploadUrl: "",
742
+ discussionUrl: "",
743
+ token: "",
744
+ releaseName: ""
730
745
  }
731
- }
732
- const { github: { isWeb }, dryRun } = ctx;
733
- if (dryRun) return true;
734
- if (isWeb) await createWebRelease(ctx, config);
735
- else if (ci) {
736
- await createCiRelease(ctx, config);
737
- await uploadAssets(ctx, config);
738
- }
746
+ };
739
747
  };
740
- const getGithubReleaseUrl = (ctx) => {
741
- const { github: { owner, repo }, git: { currentTag } } = ctx;
742
- return `${getGithubUrl(owner, repo)}/releases/tag/${normalizeTag(currentTag)}`;
748
+ const createDefaultConfig = (isCI) => {
749
+ return {
750
+ hooks: {
751
+ "before:bump": "",
752
+ "after:bump": "",
753
+ "before:publish": "",
754
+ "after:publish": "",
755
+ "before:push": "",
756
+ "after:push": "",
757
+ "before:release": "",
758
+ "after:release": ""
759
+ },
760
+ git: {
761
+ commit: isCI,
762
+ tag: isCI,
763
+ push: isCI,
764
+ commitMessage: "chore(release): ${version}",
765
+ commitArgs: [],
766
+ tagMessage: "Release ${version}",
767
+ tagName: "${version}",
768
+ tagArgs: [],
769
+ pushArgs: [],
770
+ addUntrackedFiles: false,
771
+ requireRemote: true,
772
+ requireRepository: true,
773
+ requireWorkDirClean: true
774
+ },
775
+ npm: {
776
+ publish: isCI,
777
+ publishPath: ".",
778
+ pushArgs: [],
779
+ skipChecks: false
780
+ },
781
+ github: {
782
+ release: isCI,
783
+ releaseName: "Release ${version}",
784
+ autoGenerate: false,
785
+ prerelease: false,
786
+ draft: false,
787
+ tokenRef: "GITHUB_TOKEN",
788
+ assets: [],
789
+ skipChecks: false
790
+ },
791
+ changelog: {
792
+ infile: "CHANGELOG.md",
793
+ types: []
794
+ }
795
+ };
743
796
  };
744
797
 
745
798
  //#endregion
@@ -836,6 +889,13 @@ const defaultTypes = [
836
889
  description: "破坏性变更"
837
890
  }
838
891
  ];
892
+ const mergeTypes = (types) => {
893
+ types.forEach((item) => {
894
+ const index = defaultTypes.findIndex((it) => it.type === item.type);
895
+ if (index >= 0) defaultTypes.splice(index, 1, { ...item });
896
+ else defaultTypes.push({ ...item });
897
+ });
898
+ };
839
899
  const findType = (type) => {
840
900
  return defaultTypes.find((item) => item.type === type)?.section ?? type;
841
901
  };
@@ -870,23 +930,20 @@ const classify = (commits) => {
870
930
  return sections;
871
931
  };
872
932
  const renderChangelog = async (ctx, sections) => {
873
- const { pkg: { next: to }, github: { owner, repo }, git: { latestTag } } = ctx;
933
+ const { github: { owner, repo }, git: { latestTag, currentTag } } = ctx;
874
934
  const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
875
935
  const github = getGithubUrl(owner, repo);
876
- let md;
877
- if (!latestTag) md = `## ${to} (${date})${eol(2)}`;
878
- else md = `## [${to}](${github}/compare/${latestTag}...${to}) (${date})${eol(2)}`;
879
- for (const [type, items] of Object.entries(sections)) {
880
- if (!items.length) continue;
881
- md += `### ${findType(type)}${eol(2)}`;
882
- for (const [desc, hash, fullHash] of items) md += `* **${type}** ${desc} [${hash}](${github}/commit/${fullHash})${eol(1)}`;
883
- md += eol(2);
884
- }
885
- if (latestTag) {
886
- md += `**Full Changelog**: ${github}/compare/${latestTag}...${to}`;
887
- md += eol(3);
888
- }
889
- return md;
936
+ return [
937
+ latestTag ? `## [${currentTag}](${github}/compare/${latestTag}...${currentTag}) (${date})` : `## ${currentTag} (${date})`,
938
+ Object.entries(sections).map((entry) => {
939
+ const [type, items] = entry;
940
+ return [`### ${findType(type)}`, items.map((item) => {
941
+ const [desc, hash, fullHash] = item;
942
+ return `* **${type}** ${desc} [${hash}](${github}/commit/${fullHash})`;
943
+ }).join(eol(1))].join(eol(2));
944
+ }).join(eol(2)),
945
+ latestTag ? `**Full Changelog**: ${github}/compare/${latestTag}...${currentTag}` : ""
946
+ ].join(eol(3));
890
947
  };
891
948
  const generateChangelog = async (ctx) => {
892
949
  const { isIncrement } = ctx;
@@ -896,36 +953,46 @@ const generateChangelog = async (ctx) => {
896
953
  Object.assign(ctx.github, { changelog });
897
954
  return changelog;
898
955
  };
899
- const updateChangelog = async (newContent) => {
900
- const file = "CHANGELOG.md";
901
- const title = `# Changelog${eol(3)}`;
902
- await writeFile(file, `${(existsSync(file) && await readFile(file, "utf-8") || title).replace(/* @__PURE__ */ new RegExp(`^${title}`), `${title}${newContent}`).trim()}${eol()}`);
903
- await runGitProxy(["add", file]);
956
+ const updateChangelog = async (file, newContent) => {
957
+ const infile = file || "CHANGELOG.md";
958
+ const target = join(process.cwd(), infile);
959
+ const SHORT_TITLE = "# Changelog";
960
+ const exist = (existsSync(target) ? await readFile(target, "utf-8") : "").replace(/\r\n/g, "\n");
961
+ await writeFile(target, [
962
+ SHORT_TITLE,
963
+ newContent,
964
+ exist.startsWith(SHORT_TITLE) ? exist.slice(11) : exist
965
+ ].map((k) => k.trim()).join(eol(4)) + eol());
966
+ await runGit(["add", infile]);
967
+ };
968
+ const writeChangelog = async (ctx, config) => {
969
+ const { changelog: { infile, types } } = config;
970
+ mergeTypes(types);
971
+ await updateChangelog(infile, await generateChangelog(ctx));
904
972
  };
905
973
 
906
974
  //#endregion
907
975
  //#region src/git/prompts.ts
908
976
  const runGitPrompts = async (ctx, config) => {
909
- const { git: { currentTag }, configFileExists: cfe, dryRun } = ctx;
910
- const { git: { commit, tag, push, commitMessage } } = config;
977
+ const { git: { currentTag, commitMessage }, configFileExists: cfe, dryRun } = ctx;
978
+ const { git: { commit, tag, push } } = config;
911
979
  const logs = [];
912
- const cMsg = format(commitMessage, ctx);
913
980
  const shouldPrompt = (val) => isUndefined(val) || dryRun;
914
981
  const { log: _, ...reset } = await group({
915
982
  commit: () => {
916
- return shouldPrompt(commit) ? confirm({ message: prompt(MSG.PROMPT.GIT_COMMIT(cMsg)) }) : (() => {
983
+ return shouldPrompt(commit) ? confirm({ message: question(MSG.PROMPT.GIT_COMMIT(commitMessage), "git") }) : (() => {
917
984
  logs.push(yellow`git.commit`);
918
985
  return Promise.resolve(commit);
919
986
  })();
920
987
  },
921
988
  tag: () => {
922
- return shouldPrompt(tag) ? confirm({ message: prompt(MSG.PROMPT.GIT_TAG(currentTag)) }) : (() => {
989
+ return shouldPrompt(tag) ? confirm({ message: question(MSG.PROMPT.GIT_TAG(currentTag), "git") }) : (() => {
923
990
  logs.push(yellow`git.tag`);
924
991
  return Promise.resolve(tag);
925
992
  })();
926
993
  },
927
994
  push: () => {
928
- return shouldPrompt(push) ? confirm({ message: prompt(MSG.PROMPT.GIT_PUSH) }) : (() => {
995
+ return shouldPrompt(push) ? confirm({ message: question(MSG.PROMPT.GIT_PUSH, "git") }) : (() => {
929
996
  logs.push(yellow`git.push`);
930
997
  return Promise.resolve(push);
931
998
  })();
@@ -934,9 +1001,8 @@ const runGitPrompts = async (ctx, config) => {
934
1001
  if (logs.length) msg("GIT", `Using ${cfe ? "user" : "default"} config: ${logs.join(", ")}`);
935
1002
  return Promise.resolve();
936
1003
  }
937
- }, { onCancel: async ({ results }) => {
938
- await gitRollback(ctx);
939
- abortTask("Operation cancelled.");
1004
+ }, { onCancel: ({ results }) => {
1005
+ abortGroupPrompt(ctx);
940
1006
  } });
941
1007
  Object.assign(config.git, reset);
942
1008
  };
@@ -944,13 +1010,12 @@ const runGitPrompts = async (ctx, config) => {
944
1010
  //#endregion
945
1011
  //#region src/github/prompts.ts
946
1012
  const runGithubPrompts = async (ctx, config) => {
947
- const { configFileExists: cfe, dryRun } = ctx;
948
- const { github: { release, releaseName } } = config;
1013
+ const { configFileExists: cfe, dryRun, github: { releaseName } } = ctx;
1014
+ const { github: { release } } = config;
949
1015
  const logs = [];
950
- const title = format(releaseName, ctx);
951
1016
  const shouldPrompt = (val) => isUndefined(val) || dryRun;
952
1017
  const res = await group({
953
- release: () => shouldPrompt(release) ? confirm({ message: prompt(MSG.PROMPT.GITHUB_RELEASE(title)) }) : (() => {
1018
+ release: () => shouldPrompt(release) ? confirm({ message: question(MSG.PROMPT.GITHUB_RELEASE(releaseName), "github") }) : (() => {
954
1019
  logs.push(yellow`github.release`);
955
1020
  return Promise.resolve(release);
956
1021
  })(),
@@ -958,9 +1023,8 @@ const runGithubPrompts = async (ctx, config) => {
958
1023
  if (logs.length) msg("GITHUB", `Using ${cfe ? "user" : "default"} config: ${logs.join(", ")}`);
959
1024
  return Promise.resolve();
960
1025
  }
961
- }, { onCancel: async ({ results }) => {
962
- await gitRollback(ctx);
963
- abortTask("Operation cancelled.");
1026
+ }, { onCancel: ({ results }) => {
1027
+ abortGroupPrompt(ctx);
964
1028
  } });
965
1029
  Object.assign(config.github, { release: res.release });
966
1030
  return res;
@@ -975,7 +1039,7 @@ const runNpmPublishPrompts = async (ctx, config) => {
975
1039
  const target = `${name}@${next}`;
976
1040
  const shouldPrompt = (val) => isUndefined(val) || dryRun;
977
1041
  const res = await group({
978
- publish: () => shouldPrompt(publish) ? confirm({ message: prompt(MSG.PROMPT.NPM_PUBLISH(target)) }) : (() => {
1042
+ publish: () => shouldPrompt(publish) ? confirm({ message: question(MSG.PROMPT.NPM_PUBLISH(target), "npm") }) : (() => {
979
1043
  logs.push(yellow`npm.publish`);
980
1044
  return Promise.resolve(publish);
981
1045
  })(),
@@ -984,16 +1048,14 @@ const runNpmPublishPrompts = async (ctx, config) => {
984
1048
  return Promise.resolve();
985
1049
  }
986
1050
  }, { onCancel: async ({ results }) => {
987
- await gitRollback(ctx);
988
- abortTask("Operation cancelled.");
1051
+ abortGroupPrompt(ctx);
989
1052
  } });
990
1053
  Object.assign(config.npm, { publish: res.publish });
991
1054
  return res;
992
1055
  };
993
1056
  const runNpmOptPrompts = async (ctx, config) => {
994
- const res = await group({ otp: () => text({ message: MSG.PROMPT.NPM_OTP }) }, { onCancel: async ({ results }) => {
995
- await gitRollback(ctx);
996
- abortTask("Operation cancelled.");
1057
+ const res = await group({ otp: () => text({ message: MSG.PROMPT.NPM_OTP }) }, { onCancel: ({ results }) => {
1058
+ abortGroupPrompt(ctx);
997
1059
  } });
998
1060
  Object.assign(ctx.npm, { otp: res.otp });
999
1061
  return res;
@@ -1001,43 +1063,27 @@ const runNpmOptPrompts = async (ctx, config) => {
1001
1063
 
1002
1064
  //#endregion
1003
1065
  //#region src/npm/publish.ts
1004
- const runNpmProxy = (...args) => {
1005
- if (process.env["dryRun"]) log.message(`${dim("$")} ${gray(["npm", ...args[0]].join(" "))}`);
1006
- return runNpm(...args);
1007
- };
1008
- const DEFAULT_TAG = "latest";
1009
- const DEFAULT_TAG_PRERELEASE = "next";
1010
1066
  const getRegistry = (publishConfig = {}) => {
1011
1067
  return (publishConfig.registry ? [publishConfig.registry] : Object.keys(publishConfig).filter((key) => key.endsWith("registry")).map((key) => publishConfig[key]))[0];
1012
1068
  };
1013
1069
  const isRegistryUp = async (registry) => {
1014
- return void 0 !== await runNpmProxy(["ping", ...registry ? [`--registry ${registry}`] : []]);
1070
+ return void 0 !== await runNpm(["ping", ...registry ? ["--registry", registry] : []]);
1015
1071
  };
1016
1072
  const isAuthenticated = (registry) => {
1017
- return runNpmProxy(["whoami", ...registry ? [`--registry ${registry}`] : []]);
1073
+ return runNpm(["whoami", ...registry ? ["--registry", registry] : []]);
1018
1074
  };
1019
1075
  const getLatestRegistryVersion = (name, tag, registry) => {
1020
- const registryArg = registry ? [`--registry ${registry}`] : [];
1021
- return runNpmProxy([
1076
+ const registryArg = registry ? ["--registry", registry] : [];
1077
+ return runNpm([
1022
1078
  "show",
1023
1079
  `${name}@${tag}`,
1024
1080
  "version",
1025
1081
  ...registryArg
1026
1082
  ]);
1027
1083
  };
1028
- const getRegistryTags = async (name, registry) => {
1029
- const res = await runNpmProxy([
1030
- "view",
1031
- name,
1032
- "dist-tags",
1033
- "--json",
1034
- ...registry ? [`--registry ${registry}`] : []
1035
- ]);
1036
- return Object.keys(JSON.parse(res || "{}"));
1037
- };
1038
1084
  const bump = (version, allowSameVersion, args = []) => {
1039
1085
  allowSameVersion && args.push(`--allow-same-version`);
1040
- return runNpmProxy([
1086
+ return runNpm([
1041
1087
  "version",
1042
1088
  version,
1043
1089
  "--no-git-tag-version",
@@ -1045,21 +1091,16 @@ const bump = (version, allowSameVersion, args = []) => {
1045
1091
  ...args
1046
1092
  ]);
1047
1093
  };
1048
- const guessPreReleaseTag = async (name) => {
1049
- return (await getRegistryTags(name)).filter((tag) => DEFAULT_TAG !== tag)[0] ?? DEFAULT_TAG_PRERELEASE;
1050
- };
1051
- const resolveTag = async (ctx, config) => {
1052
- const { npm: { tag } } = config;
1053
- const { pkg: { name, current } } = ctx;
1054
- const { toPreRelease, preId } = parseVersion(current);
1055
- if (!toPreRelease) return DEFAULT_TAG;
1056
- else return tag || preId || await guessPreReleaseTag(name);
1094
+ const resolveTag = async (version) => {
1095
+ const { toPreRelease, preId } = parseVersion(version);
1096
+ if (!toPreRelease) return "latest";
1097
+ else return preId || "next";
1057
1098
  };
1058
1099
  const isCollaborator = async (ctx) => {
1059
1100
  const { pkg: { name, publishConfig }, npm: { username } } = ctx;
1060
1101
  const registry = getRegistry(publishConfig);
1061
- const registryArg = registry ? [`--registry ${registry}`] : [];
1062
- let npmVersion = await runNpmProxy(["--version"]);
1102
+ const registryArg = registry ? ["--registry", registry] : [];
1103
+ let npmVersion = await runNpm(["--version"]);
1063
1104
  let accessCommand;
1064
1105
  if (gt(npmVersion, "9.0.0")) accessCommand = [
1065
1106
  "access",
@@ -1068,7 +1109,7 @@ const isCollaborator = async (ctx) => {
1068
1109
  "--json"
1069
1110
  ];
1070
1111
  else accessCommand = ["access", "ls-collaborators"];
1071
- const res = await runNpmProxy([
1112
+ const res = await runNpm([
1072
1113
  ...accessCommand,
1073
1114
  name,
1074
1115
  ...registryArg
@@ -1084,40 +1125,53 @@ const getPackageUrl = (ctx) => {
1084
1125
  return `https://www.npmjs.com/package/${name}/v/${next}`;
1085
1126
  };
1086
1127
  const npmCheck = async (ctx, config) => {
1087
- const { pkg: { name, publishConfig }, npm: { username } } = ctx;
1128
+ const { pkg: { name, current, publishConfig }, npm: { username } } = ctx;
1129
+ const { npm: { skipChecks } } = config;
1088
1130
  const registry = getRegistry(publishConfig);
1089
- const tag = await resolveTag(ctx, config);
1131
+ const tag = await resolveTag(current);
1132
+ Object.assign(ctx.npm, { tag });
1133
+ if (skipChecks) return "";
1134
+ if (!await isRegistryUp()) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
1090
1135
  const getUser = async () => {
1091
1136
  const username$1 = await isAuthenticated(registry);
1092
1137
  if (!username$1) return false;
1093
1138
  Object.assign(ctx.npm, { username: username$1 });
1094
1139
  return true;
1095
1140
  };
1096
- if (!await isRegistryUp()) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
1097
1141
  if (!await getUser()) throw new Error(MSG.ERROR.NPM_AUTH);
1098
1142
  const latestPublish = await getLatestRegistryVersion(name, tag, registry);
1099
1143
  if (latestPublish) {
1100
1144
  if (!await isCollaborator(ctx)) throw new Error(MSG.ERROR.NPM_USER(username, name));
1101
1145
  }
1102
- return dim(latestPublish ? `(latest is ${latestPublish})` : "(new publish)");
1146
+ return underline(dim(latestPublish ? `(latest is ${latestPublish})` : "(new publish)"));
1103
1147
  };
1104
1148
  const isOtpError = (err) => /one-time password|otp/i.test(err.message);
1105
1149
  const publishNpm = async (ctx, config) => {
1106
- const { dryRun, npm: { otp } } = ctx;
1107
- const { npm: { publish, tag } } = config;
1150
+ const { dryRun, npm: { otp, tag }, pkg: { publishConfig } } = ctx;
1151
+ const { npm: { publish, publishPath, pushArgs } } = config;
1152
+ const registry = getRegistry(publishConfig);
1108
1153
  if (!publish) return;
1109
- const args = ["publish"];
1110
- if (tag) args.push("--tag", tag);
1111
- if (otp) args.push("--otp", otp);
1112
- if (dryRun) args.push("--dry-run");
1113
- return runNpmProxy(args);
1154
+ const otpArgs = otp ? ["--otp", otp] : [];
1155
+ const dryRunArg = dryRun ? ["--dry-run"] : [];
1156
+ const registryArg = registry ? ["--registry", registry] : [];
1157
+ return runNpm([
1158
+ "publish",
1159
+ publishPath,
1160
+ "--tag",
1161
+ tag,
1162
+ "--workspaces=false",
1163
+ ...otpArgs,
1164
+ ...pushArgs,
1165
+ ...registryArg,
1166
+ ...dryRunArg
1167
+ ]);
1114
1168
  };
1115
1169
 
1116
1170
  //#endregion
1117
1171
  //#region src/action.ts
1118
1172
  var Action = class {
1119
1173
  async handle(cmdArgs, options) {
1120
- const { version: cVersion, name: cName } = readJsonFile(join(__dirname$1, "../package.json"));
1174
+ const { version: cVersion, name: cName } = readJsonFile(join(__dirname, "../package.json"));
1121
1175
  info(`${cName} ${dim(`v${cVersion}`)} `);
1122
1176
  const { otp, ...others } = options;
1123
1177
  const { showChangelog, showRelease, ci, dryRun } = Object.fromEntries(Object.entries(others).map(([k, v]) => [k, Boolean(v)]));
@@ -1131,10 +1185,12 @@ var Action = class {
1131
1185
  const ctx = mergeConfig(createDefaultContext(), {
1132
1186
  configFileExists: Object.keys(local).length > 0,
1133
1187
  dryRun,
1188
+ showRelease,
1189
+ showChangelog,
1134
1190
  npm: { otp: String(otp) },
1135
1191
  increment: cmdArgs,
1136
1192
  isIncrement: true,
1137
- ci: [
1193
+ isCI: [
1138
1194
  ci,
1139
1195
  showChangelog,
1140
1196
  showRelease,
@@ -1146,14 +1202,25 @@ var Action = class {
1146
1202
  isPrivate: pkgPrivate,
1147
1203
  fromPreRelease: isPreRelease(pkgVersion),
1148
1204
  current: pkgVersion,
1149
- next: "",
1205
+ next: nextVersion,
1150
1206
  toPreRelease: false,
1151
1207
  publishConfig: { ...publishConfig }
1152
- }
1208
+ },
1209
+ noNpm: pkgPrivate
1153
1210
  });
1154
1211
  console.log();
1155
1212
  intro(MSG.INTRO(pkgName, dryRun));
1156
- const { hooks } = config;
1213
+ await this.checkTask(ctx, config);
1214
+ await this.bumpTask(ctx, config);
1215
+ await this.changelogTask(ctx, config);
1216
+ await this.gitTask(ctx, config);
1217
+ await this.npmTask(ctx, config);
1218
+ await this.githubTask(ctx, config);
1219
+ if (dryRun) gitRollback$1(ctx);
1220
+ taskEnd(MSG.OUTRO(dryRun));
1221
+ }
1222
+ async checkTask(ctx, config) {
1223
+ const { noNpm, dryRun } = ctx;
1157
1224
  await tasks([{
1158
1225
  title: MSG.CHECK.GIT.CHECKING,
1159
1226
  task: async () => {
@@ -1167,73 +1234,58 @@ var Action = class {
1167
1234
  const msg$1 = await npmCheck(ctx, config);
1168
1235
  const { pkg: { publishConfig: { registry } } } = ctx;
1169
1236
  return success(MSG.CHECK.NPM.CHECKED(registry, msg$1), dryRun);
1170
- }
1237
+ },
1238
+ enabled: !noNpm
1171
1239
  }]).catch(abortOnError);
1172
- {
1173
- const { pkg: { current, next }, isIncrement, ci: ci$1 } = ctx;
1174
- if (ci$1) {
1175
- if (!next && isIncrement) {
1176
- const next$1 = nextVersion || getCIVersion(current);
1177
- if (next$1) {
1178
- const parsed = parseVersion(next$1);
1179
- Object.assign(ctx.pkg, { ...parsed });
1180
- }
1181
- }
1182
- if (showRelease) {
1183
- const { pkg: { next: next$1 } } = ctx;
1184
- taskEnd(MSG.LOG.SHOW_VERSION(next$1));
1185
- }
1186
- }
1187
- }
1188
- {
1189
- const logStr = await getLog(true, ctx.isIncrement);
1190
- if (logStr) {
1191
- msg("GIT", `Changelog:${eol(2)}` + dim(logStr));
1192
- if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
1193
- } else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
1194
- }
1195
- {
1196
- const { pkg: { next }, isIncrement } = ctx;
1197
- if (!next && isIncrement) await runVersionPrompts(ctx, config);
1198
- await runLifeCycleHook(hooks, "before:bump", dryRun);
1199
- await tasks([{
1200
- title: MSG.TASK.VERSION.START,
1201
- task: async () => {
1202
- const { pkg: { next: next$1 }, isIncrement: isIncrement$1 } = ctx;
1203
- await bump(next$1, isIncrement$1);
1204
- await resolveTag$1(ctx, config);
1205
- await updateChangelog(await generateChangelog(ctx));
1206
- return success(MSG.TASK.VERSION.END(next$1), dryRun);
1207
- }
1208
- }]).catch(abortOnError);
1209
- await runLifeCycleHook(hooks, "after:bump", dryRun);
1210
- }
1211
- {
1212
- const changeset = await getStatus();
1213
- if (changeset) msg("GIT", `Changes:${eol(2)}` + coloredChangeset(changeset));
1214
- else msg("GIT", MSG.LOG.CHANGES_EMPTY);
1240
+ }
1241
+ async bumpTask(ctx, config) {
1242
+ const { pkg: { current }, dryRun, isCI, showRelease, showChangelog } = ctx;
1243
+ const { hooks } = config;
1244
+ const need = (ctx$1) => {
1245
+ const { pkg: { next: next$1 }, isIncrement: isIncrement$1 } = ctx$1;
1246
+ return !next$1 && isIncrement$1;
1247
+ };
1248
+ if (need(ctx)) {
1249
+ const parsed = parseVersion(isCI && getCIVersion(current) || (await runVersionPrompts(ctx, config)).next);
1250
+ Object.assign(ctx.pkg, { ...parsed });
1215
1251
  }
1216
- {
1217
- const { pkg: { isPrivate }, ci: ci$1 } = ctx;
1218
- if (!isPrivate) {
1219
- await runNpmPublishPrompts(ctx, config);
1220
- await runLifeCycleHook(hooks, "before:publish", dryRun);
1221
- await tasks([{
1222
- title: MSG.TASK.NPM.START,
1223
- task: async () => {
1224
- await publishNpm(ctx, config).catch(async (err) => {
1225
- if (isOtpError(err) && !ci$1) {
1226
- await runNpmOptPrompts(ctx, config);
1227
- await publishNpm(ctx, config);
1228
- } else throw err;
1229
- });
1230
- const url = getPackageUrl(ctx);
1231
- return success(MSG.TASK.NPM.END + `: ${url}`, dryRun);
1232
- }
1233
- }]).catch(abortOnError);
1234
- await runLifeCycleHook(hooks, "after:publish", dryRun);
1252
+ const { pkg: { next }, isIncrement } = ctx;
1253
+ if (showRelease) taskEnd(MSG.LOG.SHOW_VERSION(next));
1254
+ await runLifeCycleHook(hooks, "before:bump", dryRun);
1255
+ await tasks([{
1256
+ title: MSG.TASK.VERSION.START,
1257
+ task: async () => {
1258
+ await bump(next, isIncrement);
1259
+ await formatTemplate(ctx, config);
1260
+ const to = `(${current}...${diff(current, next)})`;
1261
+ return success(MSG.TASK.VERSION.END(to), dryRun);
1235
1262
  }
1236
- }
1263
+ }]).catch((err) => abortOnError(err, ctx));
1264
+ await runLifeCycleHook(hooks, "after:bump", dryRun);
1265
+ const logStr = (await getLog(true, isIncrement))?.split("\n").filter((line) => /^(\*\s+)?(\w+):\s(.+)\s\((\w{7})\)$/.test(line)).join("\n");
1266
+ if (logStr) {
1267
+ msg("GIT", `Changelog:${eol(2)}` + dim(logStr));
1268
+ if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
1269
+ } else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
1270
+ const changeset = await getStatus();
1271
+ if (changeset) msg("GIT", `Changes:${eol(2)}` + coloredChangeset(changeset));
1272
+ else msg("GIT", MSG.LOG.CHANGES_EMPTY);
1273
+ }
1274
+ async changelogTask(ctx, config) {
1275
+ const { isIncrement, dryRun } = ctx;
1276
+ await tasks([{
1277
+ title: MSG.TASK.CHANGELOG.START,
1278
+ task: async () => {
1279
+ await writeChangelog(ctx, config);
1280
+ return success(MSG.TASK.CHANGELOG.END, dryRun);
1281
+ },
1282
+ enabled: isIncrement
1283
+ }]).catch((err) => abortOnError(err, ctx));
1284
+ }
1285
+ async gitTask(ctx, config) {
1286
+ const { noGit, dryRun } = ctx;
1287
+ const { hooks } = config;
1288
+ if (noGit) return;
1237
1289
  await runGitPrompts(ctx, config);
1238
1290
  await runLifeCycleHook(hooks, "before:push", dryRun);
1239
1291
  await tasks([{
@@ -1242,8 +1294,34 @@ var Action = class {
1242
1294
  await commitAndTag(ctx, config);
1243
1295
  return success(MSG.TASK.GIT.END, dryRun);
1244
1296
  }
1245
- }]).catch(abortOnError);
1297
+ }]).catch((err) => abortOnError(err, ctx));
1246
1298
  await runLifeCycleHook(hooks, "after:push", dryRun);
1299
+ }
1300
+ async npmTask(ctx, config) {
1301
+ const { noNpm, dryRun, isCI } = ctx;
1302
+ const { hooks } = config;
1303
+ if (noNpm) return;
1304
+ await runNpmPublishPrompts(ctx, config);
1305
+ await runLifeCycleHook(hooks, "before:publish", dryRun);
1306
+ await tasks([{
1307
+ title: MSG.TASK.NPM.START,
1308
+ task: async () => {
1309
+ await publishNpm(ctx, config).catch(async (err) => {
1310
+ if (isOtpError(err) && !isCI) {
1311
+ await runNpmOptPrompts(ctx, config);
1312
+ await publishNpm(ctx, config);
1313
+ } else throw err;
1314
+ });
1315
+ const url = getPackageUrl(ctx);
1316
+ return success(MSG.TASK.NPM.END(url), dryRun);
1317
+ }
1318
+ }]).catch((err) => abortOnError(err, ctx));
1319
+ await runLifeCycleHook(hooks, "after:publish", dryRun);
1320
+ }
1321
+ async githubTask(ctx, config) {
1322
+ const { noGitHub, dryRun } = ctx;
1323
+ const { hooks } = config;
1324
+ if (noGitHub) return;
1247
1325
  await runGithubPrompts(ctx, config);
1248
1326
  await runLifeCycleHook(hooks, "before:release", dryRun);
1249
1327
  await tasks([{
@@ -1251,18 +1329,16 @@ var Action = class {
1251
1329
  task: async () => {
1252
1330
  await createRelease(ctx, config);
1253
1331
  const url = getGithubReleaseUrl(ctx);
1254
- return success(MSG.TASK.GITHUB.END + `: ${url}`, dryRun);
1332
+ return success(MSG.TASK.GITHUB.END(url), dryRun);
1255
1333
  }
1256
- }]).catch(abortOnError);
1334
+ }]).catch((err) => abortOnError(err, ctx));
1257
1335
  await runLifeCycleHook(hooks, "after:release", dryRun);
1258
- if (dryRun) await gitRollback(ctx);
1259
- taskEnd(MSG.OUTRO(dryRun));
1260
1336
  }
1261
1337
  };
1262
1338
 
1263
1339
  //#endregion
1264
1340
  //#region src/cli.ts
1265
- const pkg = readJsonFile(join(__dirname$1, "..", "package.json"));
1341
+ const pkg = readJsonFile(join(__dirname, "..", "package.json"));
1266
1342
  program.name(pkg.name).description(pkg.description).version(pkg.version, "-v, --version", "Output the current version.").usage("[ARGUMENT] [OPTION]...").argument("[argument]", "patch|minor|major").option("-n, --dry-run", "Report actions that would be performed without writing out results.", false).option("--ci", "CI mode", false).option("--otp", "npm publish otp", "").option("--show-changelog", "Skip npm publish", false).option("--show-release", "Skip github release", false).action(async (argName, options) => {
1267
1343
  await new Action().handle(argName, options);
1268
1344
  }).helpOption("-h, --help", "Output usage information.").parse(process.argv);