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