@peiyanlu/release 0.0.1

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