@peiyanlu/release 0.0.4 → 0.0.6
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/LICENSE +1 -1
- package/README.md +10 -10
- package/dist/cli.mjs +383 -554
- package/dist/index.d.mts +49 -8
- package/dist/index.mjs +338 -1
- package/package.json +16 -13
package/dist/cli.mjs
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
import path, { join, resolve } from "node:path";
|
|
2
2
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
3
|
-
import { eol, execAsync, isGitRepo, parseGitHubRepo,
|
|
3
|
+
import { bumpPackageVersion, coloredChangeset, eol, execAsync, getAllRemotes, getAuthenticatedUser, getCurrentBranch, getGithubReleaseUrl, getGithubUrl, getLatestTag, getLog, getPackageInfo, getPackageUrl, getPublishedVersion, getRemote, getStatus, gitAddAll, gitAddTracked, gitCommit, gitTagAnnotated, hasWriteAccess, isGitRepo, isWorkingDirClean, parseGitHubRepo, pingRegistry, publishPackage, pushBranch, pushTag, readJsonFile, resolveChangelogRange } from "@peiyanlu/cli-utils";
|
|
4
4
|
import { dim, green, red, rgb, underline, yellow } from "ansis";
|
|
5
5
|
import { program } from "commander";
|
|
6
6
|
import { createReadStream, createWriteStream, existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
7
7
|
import { cancel, confirm, group, intro, isCancel, log, outro, select, tasks, text } from "@clack/prompts";
|
|
8
8
|
import { publint } from "publint";
|
|
9
9
|
import { formatMessage } from "publint/utils";
|
|
10
|
-
import {
|
|
10
|
+
import { coerce, inc, lt, neq, parse, prerelease, valid } from "semver";
|
|
11
11
|
import { castArray, isPlainObject, isUndefined } from "@peiyanlu/ts-utils";
|
|
12
12
|
import { spawnSync } from "node:child_process";
|
|
13
13
|
import { inspect } from "node:util";
|
|
14
|
-
import { Octokit } from "@octokit/rest";
|
|
15
|
-
import { contentType } from "mime-types";
|
|
16
|
-
import open from "open";
|
|
17
|
-
import { glob } from "tinyglobby";
|
|
18
14
|
import { ConventionalChangelog } from "conventional-changelog";
|
|
19
15
|
import createPreset from "conventional-changelog-conventionalcommits";
|
|
20
16
|
import { writeFile } from "node:fs/promises";
|
|
21
17
|
import { finished } from "node:stream/promises";
|
|
18
|
+
import { Octokit } from "@octokit/rest";
|
|
19
|
+
import { contentType } from "mime-types";
|
|
20
|
+
import open from "open";
|
|
21
|
+
import { glob } from "tinyglobby";
|
|
22
22
|
|
|
23
23
|
//#region node_modules/.pnpm/tsdown@0.18.4_publint@0.3.17_typescript@5.9.3/node_modules/tsdown/esm-shims.js
|
|
24
24
|
const getFilename = () => fileURLToPath(import.meta.url);
|
|
@@ -79,6 +79,7 @@ const t = () => green(`${Math.floor(process.uptime())}s`);
|
|
|
79
79
|
const MSG = {
|
|
80
80
|
INTRO: (dryRun) => `🚀 Starting release ${n(dryRun)}`,
|
|
81
81
|
OUTRO: (dryRun) => `🎉 Release finished successfully in ${t()} ${n(dryRun)}`,
|
|
82
|
+
OUTRO_PREPARE: (dryRun) => `🎉 PrepareRelease finished successfully in ${t()} ${n(dryRun)}`,
|
|
82
83
|
LOG: {
|
|
83
84
|
SHOW_VERSION: (version) => `🎉 Released ${green(version)}`,
|
|
84
85
|
SHOW_CHANGELOG: "🎉 Changelog collected",
|
|
@@ -93,6 +94,10 @@ const MSG = {
|
|
|
93
94
|
NPM: {
|
|
94
95
|
CHECKING: "Checking npm registry",
|
|
95
96
|
CHECKED: (registry, msg$1) => `NPM registry: ${underline(dim(registry))} ${underline(dim(msg$1))}`
|
|
97
|
+
},
|
|
98
|
+
GITHUB: {
|
|
99
|
+
CHECKING: "Checking github repository",
|
|
100
|
+
CHECKED: (repository, msg$1) => `GitHub repository: ${underline(dim(repository))} ${underline(dim(msg$1))}`
|
|
96
101
|
}
|
|
97
102
|
},
|
|
98
103
|
TASK: {
|
|
@@ -151,259 +156,8 @@ const MSG = {
|
|
|
151
156
|
}
|
|
152
157
|
};
|
|
153
158
|
|
|
154
|
-
//#endregion
|
|
155
|
-
//#region src/github/release.ts
|
|
156
|
-
const getGithubUrl = (owner, repo) => {
|
|
157
|
-
return `https://github.com/${owner}/${repo}`;
|
|
158
|
-
};
|
|
159
|
-
const truncateBody = (body) => {
|
|
160
|
-
if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
|
|
161
|
-
return body;
|
|
162
|
-
};
|
|
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
|
-
}));
|
|
183
|
-
};
|
|
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 });
|
|
195
|
-
};
|
|
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"
|
|
213
|
-
});
|
|
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
|
|
221
|
-
});
|
|
222
|
-
} catch (e) {
|
|
223
|
-
if (e?.status === 422) throw new Error(MSG.ERROR.GITHUB_TAG_EXIT(currentTag));
|
|
224
|
-
throw e;
|
|
225
|
-
}
|
|
226
|
-
};
|
|
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
|
-
});
|
|
234
|
-
};
|
|
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);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
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
|
-
}
|
|
272
|
-
};
|
|
273
|
-
const getGithubReleaseUrl = (ctx) => {
|
|
274
|
-
const { github: { owner, repo }, git: { currentTag } } = ctx;
|
|
275
|
-
return `${getGithubUrl(owner, repo)}/releases/tag/${normalizeTag(currentTag)}`;
|
|
276
|
-
};
|
|
277
|
-
|
|
278
159
|
//#endregion
|
|
279
160
|
//#region src/git/commit.ts
|
|
280
|
-
const getBranchName = async () => {
|
|
281
|
-
let branch = await runGit(["branch", "--show-current"]);
|
|
282
|
-
if (!branch) {
|
|
283
|
-
branch = await runGit([
|
|
284
|
-
"rev-parse",
|
|
285
|
-
"--abbrev-ref",
|
|
286
|
-
"HEAD"
|
|
287
|
-
]);
|
|
288
|
-
if (branch === "HEAD") return void 0;
|
|
289
|
-
}
|
|
290
|
-
return branch;
|
|
291
|
-
};
|
|
292
|
-
const getRemoteForBranch = (branch) => {
|
|
293
|
-
return runGit([
|
|
294
|
-
"config",
|
|
295
|
-
"--get",
|
|
296
|
-
`branch.${branch}.remote`
|
|
297
|
-
]);
|
|
298
|
-
};
|
|
299
|
-
const getAllRemotes = async () => {
|
|
300
|
-
return (await runGit(["remote"]))?.split("\n").filter(Boolean) ?? [];
|
|
301
|
-
};
|
|
302
|
-
const getStatus = () => {
|
|
303
|
-
return runGit([
|
|
304
|
-
"status",
|
|
305
|
-
"--short",
|
|
306
|
-
"--untracked-files=no"
|
|
307
|
-
], { trim: false });
|
|
308
|
-
};
|
|
309
|
-
const coloredChangeset = (log$1) => {
|
|
310
|
-
const colorStatusChar = (ch) => {
|
|
311
|
-
switch (ch) {
|
|
312
|
-
case "M": return `\x1b[33m${ch}\x1b[0m`;
|
|
313
|
-
case "A": return `\x1b[32m${ch}\x1b[0m`;
|
|
314
|
-
case "D": return `\x1b[31m${ch}\x1b[0m`;
|
|
315
|
-
default: return ch;
|
|
316
|
-
}
|
|
317
|
-
};
|
|
318
|
-
const colorStatus = (status) => status.split("").map(colorStatusChar).join("");
|
|
319
|
-
return log$1.split("\n").filter(Boolean).map((line) => {
|
|
320
|
-
const status = line.slice(0, 2);
|
|
321
|
-
const file = line.slice(3);
|
|
322
|
-
return `${colorStatus(status)} ${dim(file)}`;
|
|
323
|
-
}).join("\n");
|
|
324
|
-
};
|
|
325
|
-
const getRemote = async () => {
|
|
326
|
-
const branch = await getBranchName();
|
|
327
|
-
const remoteName = branch && await getRemoteForBranch(branch) || "origin";
|
|
328
|
-
return {
|
|
329
|
-
remoteName,
|
|
330
|
-
remoteUrl: await runGit([
|
|
331
|
-
"remote",
|
|
332
|
-
"get-url",
|
|
333
|
-
remoteName
|
|
334
|
-
]).catch((_) => runGit([
|
|
335
|
-
"config",
|
|
336
|
-
"--get",
|
|
337
|
-
`remote.${remoteName}.url`
|
|
338
|
-
]))
|
|
339
|
-
};
|
|
340
|
-
};
|
|
341
|
-
const getLatestTag = async (match = "*", exclude = "*-beta.*") => {
|
|
342
|
-
return runGit([
|
|
343
|
-
"describe",
|
|
344
|
-
"--tags",
|
|
345
|
-
"--abbrev=0",
|
|
346
|
-
`--match=${match}`,
|
|
347
|
-
`--exclude=${exclude}`
|
|
348
|
-
]);
|
|
349
|
-
};
|
|
350
|
-
const getPreviousTag = async (current) => {
|
|
351
|
-
return runGit([
|
|
352
|
-
"describe",
|
|
353
|
-
"--tags",
|
|
354
|
-
"--abbrev=0",
|
|
355
|
-
`${await runGit([
|
|
356
|
-
"rev-list",
|
|
357
|
-
"--tags",
|
|
358
|
-
current || "--skip=1",
|
|
359
|
-
"--max-count=1"
|
|
360
|
-
])}^`
|
|
361
|
-
]);
|
|
362
|
-
};
|
|
363
|
-
const resolveChangelogRange = async (isIncrement = true) => {
|
|
364
|
-
const latestTag = await getLatestTag();
|
|
365
|
-
const previousTag = await getPreviousTag(latestTag);
|
|
366
|
-
if (!latestTag) return {
|
|
367
|
-
from: "",
|
|
368
|
-
to: "HEAD"
|
|
369
|
-
};
|
|
370
|
-
if (!isIncrement && previousTag) return {
|
|
371
|
-
from: previousTag,
|
|
372
|
-
to: `${latestTag}^1`
|
|
373
|
-
};
|
|
374
|
-
return {
|
|
375
|
-
from: latestTag,
|
|
376
|
-
to: "HEAD"
|
|
377
|
-
};
|
|
378
|
-
};
|
|
379
|
-
const getLog = async (from = "", to = "HEAD", scope) => {
|
|
380
|
-
const cmd = ["log", `--pretty=format:* %s (%h)`];
|
|
381
|
-
if (from) cmd.push(`${from}...${to}`);
|
|
382
|
-
if (scope) cmd.push(...["--", scope]);
|
|
383
|
-
return runGit(cmd, { trim: false });
|
|
384
|
-
};
|
|
385
|
-
const isWorkingDirClean = async () => {
|
|
386
|
-
return (await runGit(["status", "-s"]))?.length === 0;
|
|
387
|
-
};
|
|
388
|
-
const hasUpstreamBranch = async () => {
|
|
389
|
-
const branch = await runGit([
|
|
390
|
-
"for-each-ref",
|
|
391
|
-
"--format=%(upstream:short)",
|
|
392
|
-
await runGit(["symbolic-ref", "HEAD"])
|
|
393
|
-
]);
|
|
394
|
-
return Boolean(branch);
|
|
395
|
-
};
|
|
396
|
-
const getUpstreamArgs = async (remoteName) => {
|
|
397
|
-
const hasUpstream = await hasUpstreamBranch();
|
|
398
|
-
const branch = await getBranchName();
|
|
399
|
-
if (!hasUpstream) return [
|
|
400
|
-
"--set-upstream",
|
|
401
|
-
remoteName,
|
|
402
|
-
branch
|
|
403
|
-
];
|
|
404
|
-
return [remoteName, branch];
|
|
405
|
-
};
|
|
406
|
-
const normalizeTag = (tag) => clean(tag) ?? tag;
|
|
407
161
|
const gitCheck = async (ctx, config) => {
|
|
408
162
|
const { pkg: { name } } = ctx;
|
|
409
163
|
const { git: { requireRepository, requireRemote, requireWorkDirClean } } = config;
|
|
@@ -423,60 +177,34 @@ const gitCheck = async (ctx, config) => {
|
|
|
423
177
|
remoteName,
|
|
424
178
|
remoteUrl
|
|
425
179
|
});
|
|
426
|
-
|
|
180
|
+
const [owner, repo] = parseGitHubRepo(remoteUrl);
|
|
181
|
+
Object.assign(ctx.github, {
|
|
182
|
+
owner,
|
|
183
|
+
repo
|
|
184
|
+
});
|
|
427
185
|
};
|
|
428
186
|
const commitAndTag = async (ctx, config) => {
|
|
429
187
|
const { git: { remoteName, currentTag, commitMessage, tagMessage }, dryRun } = ctx;
|
|
430
188
|
const { git: { commit, tag, push, addUntrackedFiles, commitArgs, tagArgs, pushArgs } } = config;
|
|
431
189
|
if (!commit) return;
|
|
432
190
|
const args = dryRun ? ["--dry-run"] : [];
|
|
433
|
-
await
|
|
434
|
-
|
|
435
|
-
".",
|
|
436
|
-
addUntrackedFiles ? "--all" : "--update",
|
|
437
|
-
...args
|
|
438
|
-
]);
|
|
439
|
-
await runGit([
|
|
440
|
-
"commit",
|
|
441
|
-
"--message",
|
|
442
|
-
commitMessage,
|
|
443
|
-
...commitArgs,
|
|
444
|
-
...args
|
|
445
|
-
]);
|
|
191
|
+
addUntrackedFiles ? await gitAddAll() : await gitAddTracked(args);
|
|
192
|
+
await gitCommit(commitMessage, [...commitArgs, ...args]);
|
|
446
193
|
if (!dryRun) Object.assign(ctx.git, { isCommitted: true });
|
|
447
194
|
if (tag) {
|
|
448
|
-
await
|
|
449
|
-
"tag",
|
|
450
|
-
"--annotate",
|
|
451
|
-
"--message",
|
|
452
|
-
tagMessage,
|
|
453
|
-
currentTag,
|
|
454
|
-
...tagArgs,
|
|
455
|
-
...args
|
|
456
|
-
]);
|
|
195
|
+
await gitTagAnnotated(currentTag, tagMessage, [...tagArgs, ...args]);
|
|
457
196
|
if (!dryRun) Object.assign(ctx.git, { isTagged: true });
|
|
458
197
|
}
|
|
459
198
|
if (push) {
|
|
460
199
|
const remotes = await getAllRemotes();
|
|
461
200
|
for (const remoteName$1 of remotes) {
|
|
462
|
-
|
|
463
|
-
await
|
|
464
|
-
"push",
|
|
465
|
-
remoteName$1,
|
|
466
|
-
`refs/tags/${currentTag}`,
|
|
467
|
-
...args
|
|
468
|
-
]);
|
|
469
|
-
await runGit([
|
|
470
|
-
"push",
|
|
471
|
-
...upstreamArgs,
|
|
472
|
-
...pushArgs,
|
|
473
|
-
...args
|
|
474
|
-
]);
|
|
201
|
+
await pushTag(remoteName$1, currentTag, args);
|
|
202
|
+
await pushBranch(remoteName$1, await getCurrentBranch(), [...pushArgs, ...args]);
|
|
475
203
|
}
|
|
476
204
|
if (!dryRun) Object.assign(ctx.git, { isPushed: true });
|
|
477
205
|
}
|
|
478
206
|
};
|
|
479
|
-
const gitRollback
|
|
207
|
+
const gitRollback = (ctx) => {
|
|
480
208
|
const { git: { currentTag, isCommitted, isTagged, isPushed } } = ctx;
|
|
481
209
|
if (isPushed) return;
|
|
482
210
|
spawnSync("git", ["restore", "."]);
|
|
@@ -492,6 +220,30 @@ const gitRollback$1 = (ctx) => {
|
|
|
492
220
|
]);
|
|
493
221
|
};
|
|
494
222
|
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/prompts.ts
|
|
225
|
+
const abortTask = (msg$1) => {
|
|
226
|
+
cancel(`${red("✕")} ${msg$1}`);
|
|
227
|
+
process.exit(0);
|
|
228
|
+
};
|
|
229
|
+
const abortSinglePrompt = (value) => {
|
|
230
|
+
if (isCancel(value)) abortTask(MSG.ABORT.CANCEL);
|
|
231
|
+
};
|
|
232
|
+
const abortGroupPrompt = (ctx) => {
|
|
233
|
+
ctx && gitRollback(ctx);
|
|
234
|
+
abortTask(MSG.ABORT.CANCEL);
|
|
235
|
+
};
|
|
236
|
+
const abortOnError = (err, ctx) => {
|
|
237
|
+
ctx && gitRollback(ctx);
|
|
238
|
+
const msg$1 = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
|
|
239
|
+
log.message(`\n${msg$1}\n`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
};
|
|
242
|
+
const taskEnd = (msg$1) => {
|
|
243
|
+
outro(msg$1);
|
|
244
|
+
process.exit(0);
|
|
245
|
+
};
|
|
246
|
+
|
|
495
247
|
//#endregion
|
|
496
248
|
//#region src/utils.ts
|
|
497
249
|
const info = (msg$1) => {
|
|
@@ -524,20 +276,6 @@ const runLifeCycleHook = async (hooks, key, dryRun) => {
|
|
|
524
276
|
log.success(success(hook.replace(/^echo$/, dim("echo")), dryRun));
|
|
525
277
|
}));
|
|
526
278
|
};
|
|
527
|
-
const gitRollback = (ctx) => {
|
|
528
|
-
const { git: { currentTag, isCommitted, isTagged } } = ctx;
|
|
529
|
-
spawnSync("git", ["restore", "."]);
|
|
530
|
-
if (isTagged) spawnSync("git", [
|
|
531
|
-
"tag",
|
|
532
|
-
"--delete",
|
|
533
|
-
currentTag
|
|
534
|
-
]);
|
|
535
|
-
spawnSync("git", [
|
|
536
|
-
"reset",
|
|
537
|
-
"--hard",
|
|
538
|
-
isCommitted ? "HEAD~1" : "HEAD"
|
|
539
|
-
]);
|
|
540
|
-
};
|
|
541
279
|
const diff = (from, to, separator = ".") => {
|
|
542
280
|
const a = from.split(separator);
|
|
543
281
|
return to.split(separator).map((v, i) => v === a[i] ? dim(v) : green(v)).join(separator);
|
|
@@ -559,39 +297,6 @@ const formatTemplate = async (ctx, config) => {
|
|
|
559
297
|
});
|
|
560
298
|
Object.assign(ctx.github, { releaseName });
|
|
561
299
|
};
|
|
562
|
-
const getPackageInfo = (pkgName, getPkgDir = (pkg$1) => `packages/${pkg$1}`) => {
|
|
563
|
-
const pkgDir = resolve(getPkgDir(pkgName));
|
|
564
|
-
const pkgPath = resolve(pkgDir, "package.json");
|
|
565
|
-
return {
|
|
566
|
-
pkg: readJsonFile(pkgPath),
|
|
567
|
-
pkgDir,
|
|
568
|
-
pkgPath
|
|
569
|
-
};
|
|
570
|
-
};
|
|
571
|
-
|
|
572
|
-
//#endregion
|
|
573
|
-
//#region src/prompts.ts
|
|
574
|
-
const abortTask = (msg$1) => {
|
|
575
|
-
cancel(`${red("✕")} ${msg$1}`);
|
|
576
|
-
process.exit(0);
|
|
577
|
-
};
|
|
578
|
-
const abortSinglePrompt = (value) => {
|
|
579
|
-
if (isCancel(value)) abortTask(MSG.ABORT.CANCEL);
|
|
580
|
-
};
|
|
581
|
-
const abortGroupPrompt = (ctx) => {
|
|
582
|
-
ctx && gitRollback(ctx);
|
|
583
|
-
abortTask(MSG.ABORT.CANCEL);
|
|
584
|
-
};
|
|
585
|
-
const abortOnError = (err, ctx) => {
|
|
586
|
-
ctx && gitRollback(ctx);
|
|
587
|
-
const msg$1 = err.message.replace(/^\[(github|npm|git)]/, (a) => red(a));
|
|
588
|
-
log.message(`\n${msg$1}\n`);
|
|
589
|
-
process.exit(1);
|
|
590
|
-
};
|
|
591
|
-
const taskEnd = (msg$1) => {
|
|
592
|
-
outro(msg$1);
|
|
593
|
-
process.exit(0);
|
|
594
|
-
};
|
|
595
300
|
|
|
596
301
|
//#endregion
|
|
597
302
|
//#region src/version/bump.ts
|
|
@@ -615,7 +320,10 @@ const parseVersion = (raw) => {
|
|
|
615
320
|
};
|
|
616
321
|
};
|
|
617
322
|
const getCIVersion = (current) => {
|
|
618
|
-
return inc(current, Release.PATCH)
|
|
323
|
+
return inc(current, Release.PATCH);
|
|
324
|
+
};
|
|
325
|
+
const isNeq = (current, next) => {
|
|
326
|
+
return neq(current, next);
|
|
619
327
|
};
|
|
620
328
|
|
|
621
329
|
//#endregion
|
|
@@ -624,6 +332,7 @@ let Release = /* @__PURE__ */ function(Release$1) {
|
|
|
624
332
|
Release$1["MAJOR"] = "major";
|
|
625
333
|
Release$1["MINOR"] = "minor";
|
|
626
334
|
Release$1["PATCH"] = "patch";
|
|
335
|
+
Release$1["RELEASE"] = "release";
|
|
627
336
|
return Release$1;
|
|
628
337
|
}({});
|
|
629
338
|
let PreRelease = /* @__PURE__ */ function(PreRelease$1) {
|
|
@@ -643,76 +352,45 @@ let PreId = /* @__PURE__ */ function(PreId$1) {
|
|
|
643
352
|
PreId$1["DEV"] = "dev";
|
|
644
353
|
return PreId$1;
|
|
645
354
|
}({});
|
|
355
|
+
const createVersionMap = (current) => ({
|
|
356
|
+
patch: inc(current, Release.PATCH),
|
|
357
|
+
minor: inc(current, Release.MINOR),
|
|
358
|
+
major: inc(current, Release.MAJOR),
|
|
359
|
+
release: inc(current, Release.RELEASE),
|
|
360
|
+
prerelease: inc(current, PreRelease.RELEASE),
|
|
361
|
+
prepatch: inc(current, PreRelease.PATCH, PreId.BETA),
|
|
362
|
+
preminor: inc(current, PreRelease.MINOR, PreId.BETA),
|
|
363
|
+
premajor: inc(current, PreRelease.MAJOR, PreId.BETA)
|
|
364
|
+
});
|
|
365
|
+
const option = (label, value, hint) => ({
|
|
366
|
+
label,
|
|
367
|
+
value,
|
|
368
|
+
hint: hint ?? value
|
|
369
|
+
});
|
|
646
370
|
const runVersionPrompts = async (ctx, config) => {
|
|
647
371
|
const { pkg: { current, fromPreRelease } } = ctx;
|
|
648
372
|
const selectVersion = async () => {
|
|
649
|
-
const
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
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
|
-
}
|
|
373
|
+
const versions = createVersionMap(current);
|
|
374
|
+
const options = [
|
|
375
|
+
...fromPreRelease ? [option("Pre-Release", "prerelease", versions.prerelease)] : [],
|
|
376
|
+
option("Patch", "patch", versions.patch),
|
|
377
|
+
option("Minor", "minor", versions.minor),
|
|
378
|
+
option("Major", "major", versions.major),
|
|
379
|
+
...!fromPreRelease ? [
|
|
380
|
+
option("Pre-Patch", "prepatch", versions.prepatch),
|
|
381
|
+
option("Pre-Minor", "preminor", versions.preminor),
|
|
382
|
+
option("Pre-Major", "premajor", versions.premajor)
|
|
383
|
+
] : [],
|
|
384
|
+
option("As-Is", "ignore", current),
|
|
385
|
+
option("Custom", "custom", "custom specified")
|
|
694
386
|
];
|
|
695
|
-
const
|
|
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({
|
|
387
|
+
const type = await select({
|
|
705
388
|
message: question(MSG.PROMPT.SELECT_VERSION, "version"),
|
|
706
|
-
options
|
|
707
|
-
|
|
708
|
-
...release,
|
|
709
|
-
...fromPreRelease ? [] : pre,
|
|
710
|
-
...custom
|
|
711
|
-
],
|
|
712
|
-
initialValue: fromPreRelease ? prerelease$1 : patch
|
|
389
|
+
options,
|
|
390
|
+
initialValue: fromPreRelease ? "prerelease" : "patch"
|
|
713
391
|
});
|
|
714
|
-
abortSinglePrompt(
|
|
715
|
-
return
|
|
392
|
+
abortSinglePrompt(type);
|
|
393
|
+
return type;
|
|
716
394
|
};
|
|
717
395
|
const inputVersion = async () => {
|
|
718
396
|
const version = await text({
|
|
@@ -725,10 +403,12 @@ const runVersionPrompts = async (ctx, config) => {
|
|
|
725
403
|
abortSinglePrompt(version);
|
|
726
404
|
return version;
|
|
727
405
|
};
|
|
728
|
-
const
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
406
|
+
const resolveNextVersion = async (type, current$1) => {
|
|
407
|
+
if (type === "ignore") return current$1;
|
|
408
|
+
if (type === "custom") return inputVersion();
|
|
409
|
+
return createVersionMap(current$1)[type];
|
|
410
|
+
};
|
|
411
|
+
return await resolveNextVersion(await selectVersion(), current);
|
|
732
412
|
};
|
|
733
413
|
|
|
734
414
|
//#endregion
|
|
@@ -814,7 +494,7 @@ const createDefaultConfig = (isCI) => {
|
|
|
814
494
|
toTag(pkg$1, version) {
|
|
815
495
|
return `v${version}`;
|
|
816
496
|
},
|
|
817
|
-
|
|
497
|
+
tagPrefix: void 0,
|
|
818
498
|
git: {
|
|
819
499
|
commit: isCI,
|
|
820
500
|
tag: isCI,
|
|
@@ -831,8 +511,7 @@ const createDefaultConfig = (isCI) => {
|
|
|
831
511
|
},
|
|
832
512
|
npm: {
|
|
833
513
|
publish: isCI,
|
|
834
|
-
|
|
835
|
-
pushArgs: [],
|
|
514
|
+
publishArgs: [],
|
|
836
515
|
skipChecks: false
|
|
837
516
|
},
|
|
838
517
|
github: {
|
|
@@ -945,11 +624,8 @@ const defaultTypes = [
|
|
|
945
624
|
|
|
946
625
|
//#endregion
|
|
947
626
|
//#region src/git/changelog.ts
|
|
948
|
-
const
|
|
627
|
+
const createGenerator = async ({ getPkgDir, tagPrefix }) => {
|
|
949
628
|
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
629
|
const preset = await createPreset({ types: defaultTypes.map((t$1) => ({
|
|
954
630
|
...t$1,
|
|
955
631
|
hidden: false
|
|
@@ -1002,17 +678,30 @@ const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
|
1002
678
|
{{> commit root=@root}}
|
|
1003
679
|
{{/each}}
|
|
1004
680
|
{{/each}}`.trim() + eol(2);
|
|
1005
|
-
|
|
1006
|
-
|
|
681
|
+
return new ConventionalChangelog().readPackage(`${pkgDir}/package.json`).config(preset).options({ releaseCount: 1 }).commits({ path: pkgDir }).tags({ prefix: tagPrefix });
|
|
682
|
+
};
|
|
683
|
+
const getChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
684
|
+
const generator = await createGenerator({
|
|
685
|
+
getPkgDir,
|
|
686
|
+
tagPrefix
|
|
687
|
+
});
|
|
1007
688
|
let changelog = "";
|
|
1008
|
-
for await (const chunk of generator.write())
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
689
|
+
for await (const chunk of generator.write()) changelog += chunk;
|
|
690
|
+
return changelog;
|
|
691
|
+
};
|
|
692
|
+
const generateChangelog = async ({ getPkgDir, tagPrefix }) => {
|
|
693
|
+
const infile = join(getPkgDir(), "CHANGELOG.md");
|
|
694
|
+
if (!existsSync(infile)) await writeFile(infile, "");
|
|
695
|
+
const originalChangelog = readFileSync(infile, "utf-8");
|
|
696
|
+
const generator = await createGenerator({
|
|
697
|
+
getPkgDir,
|
|
698
|
+
tagPrefix
|
|
699
|
+
});
|
|
700
|
+
const writeStream = createWriteStream(infile);
|
|
701
|
+
for await (const chunk of generator.write()) writeStream.write(chunk);
|
|
1012
702
|
writeStream.write(originalChangelog);
|
|
1013
703
|
writeStream.end();
|
|
1014
704
|
await finished(writeStream);
|
|
1015
|
-
return changelog;
|
|
1016
705
|
};
|
|
1017
706
|
|
|
1018
707
|
//#endregion
|
|
@@ -1074,6 +763,172 @@ const runGithubPrompts = async (ctx, config) => {
|
|
|
1074
763
|
return res;
|
|
1075
764
|
};
|
|
1076
765
|
|
|
766
|
+
//#endregion
|
|
767
|
+
//#region src/github/release.ts
|
|
768
|
+
const truncateBody = (body) => {
|
|
769
|
+
if (body && body.length >= 124e3) return body.substring(0, 124e3) + "...";
|
|
770
|
+
return body;
|
|
771
|
+
};
|
|
772
|
+
const getReleaseByTagSafe = async (octokit, options) => {
|
|
773
|
+
try {
|
|
774
|
+
const { data } = await octokit.repos.getReleaseByTag({ ...options });
|
|
775
|
+
return data;
|
|
776
|
+
} catch (e) {
|
|
777
|
+
if (e?.status === 404) return null;
|
|
778
|
+
throw e;
|
|
779
|
+
}
|
|
780
|
+
};
|
|
781
|
+
const createCRelease = async (octokit, options) => {
|
|
782
|
+
const { owner, repo, tag, name, body, draft, prerelease: prerelease$1, generateReleaseNotes, makeLatest, targetCommitish, discussionCategoryName } = options;
|
|
783
|
+
const { data } = await octokit.repos.createRelease({
|
|
784
|
+
owner,
|
|
785
|
+
repo,
|
|
786
|
+
tag_name: tag,
|
|
787
|
+
name,
|
|
788
|
+
body,
|
|
789
|
+
draft,
|
|
790
|
+
prerelease: prerelease$1,
|
|
791
|
+
generate_release_notes: generateReleaseNotes,
|
|
792
|
+
make_latest: makeLatest,
|
|
793
|
+
target_commitish: targetCommitish,
|
|
794
|
+
discussion_category_name: discussionCategoryName
|
|
795
|
+
});
|
|
796
|
+
return data;
|
|
797
|
+
};
|
|
798
|
+
const updateCRelease = async (octokit, options) => {
|
|
799
|
+
const { owner, repo, tag, name, body, draft, prerelease: prerelease$1, generateReleaseNotes, makeLatest, targetCommitish, discussionCategoryName, releaseId } = options;
|
|
800
|
+
const { data } = await octokit.repos.updateRelease({
|
|
801
|
+
owner,
|
|
802
|
+
repo,
|
|
803
|
+
release_id: releaseId,
|
|
804
|
+
tag_name: tag,
|
|
805
|
+
name,
|
|
806
|
+
body,
|
|
807
|
+
draft,
|
|
808
|
+
prerelease: prerelease$1,
|
|
809
|
+
generate_release_notes: generateReleaseNotes,
|
|
810
|
+
make_latest: makeLatest,
|
|
811
|
+
target_commitish: targetCommitish,
|
|
812
|
+
discussion_category_name: discussionCategoryName
|
|
813
|
+
});
|
|
814
|
+
return data;
|
|
815
|
+
};
|
|
816
|
+
const createCReleaseByTag = async (octokit, options) => {
|
|
817
|
+
const existing = await getReleaseByTagSafe(octokit, options);
|
|
818
|
+
if (!existing) return createCRelease(octokit, { ...options });
|
|
819
|
+
const { draft, id: releaseId } = existing;
|
|
820
|
+
if (!draft) throw new Error(`Release for tag "${options.tag}" already exists and is not a draft`);
|
|
821
|
+
return updateCRelease(octokit, {
|
|
822
|
+
releaseId,
|
|
823
|
+
...options
|
|
824
|
+
});
|
|
825
|
+
};
|
|
826
|
+
const uploadAssets = async (octokit, options) => {
|
|
827
|
+
const { owner, repo, name, uploadUrl, releaseId, assets } = options;
|
|
828
|
+
const files = await glob(assets);
|
|
829
|
+
await Promise.all(files.map((file) => {
|
|
830
|
+
return octokit.repos.uploadReleaseAsset({
|
|
831
|
+
owner,
|
|
832
|
+
repo,
|
|
833
|
+
name,
|
|
834
|
+
url: uploadUrl,
|
|
835
|
+
release_id: releaseId,
|
|
836
|
+
data: createReadStream(file),
|
|
837
|
+
headers: {
|
|
838
|
+
"content-type": contentType(file) || "application/octet-stream",
|
|
839
|
+
"content-length": statSync(file).size
|
|
840
|
+
}
|
|
841
|
+
});
|
|
842
|
+
}));
|
|
843
|
+
};
|
|
844
|
+
const createWebRelease = async (options) => {
|
|
845
|
+
const { owner, repo, tag, title, body, prerelease: prerelease$1 } = options;
|
|
846
|
+
const url = new URL(`${getGithubUrl(owner, repo)}/releases/new`);
|
|
847
|
+
Object.entries({
|
|
848
|
+
tag,
|
|
849
|
+
title,
|
|
850
|
+
body,
|
|
851
|
+
prerelease: prerelease$1
|
|
852
|
+
}).forEach(([key, value]) => {
|
|
853
|
+
url.searchParams.set(key, value);
|
|
854
|
+
});
|
|
855
|
+
const isWindows = process.platform === "win32";
|
|
856
|
+
await open(url.toString(), { wait: isWindows });
|
|
857
|
+
};
|
|
858
|
+
const githubCheck = async (ctx, config) => {
|
|
859
|
+
const { github: { owner, repo }, isCI } = ctx;
|
|
860
|
+
const { github: { tokenRef, skipChecks } } = config;
|
|
861
|
+
const token = tokenRef ? process.env[tokenRef] : process.env.GITHUB_TOKEN;
|
|
862
|
+
if (!token) {
|
|
863
|
+
Object.assign(ctx.github, { isWeb: true });
|
|
864
|
+
if (isCI && process.env.GITHUB_ACTIONS) throw new Error(MSG.ERROR.GITHUB_TOKEN(tokenRef));
|
|
865
|
+
return "";
|
|
866
|
+
}
|
|
867
|
+
Object.assign(ctx.github, { token });
|
|
868
|
+
if (skipChecks) return "";
|
|
869
|
+
let username = "";
|
|
870
|
+
if (process.env.GITHUB_ACTIONS) username = process.env.GITHUB_ACTOR;
|
|
871
|
+
else {
|
|
872
|
+
const octokit = new Octokit({ auth: token });
|
|
873
|
+
try {
|
|
874
|
+
const { data: { login } } = await octokit.users.getAuthenticated();
|
|
875
|
+
username = login;
|
|
876
|
+
} catch (e) {
|
|
877
|
+
throw new Error(MSG.ERROR.GITHUB_AUTH);
|
|
878
|
+
}
|
|
879
|
+
try {
|
|
880
|
+
await octokit.repos.checkCollaborator({
|
|
881
|
+
owner,
|
|
882
|
+
repo,
|
|
883
|
+
username
|
|
884
|
+
});
|
|
885
|
+
} catch (e) {
|
|
886
|
+
throw new Error(MSG.ERROR.GITHUB_USER);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
return username;
|
|
890
|
+
};
|
|
891
|
+
const createRelease = async (ctx, config) => {
|
|
892
|
+
const { pkg: { name, toPreRelease }, git: { currentTag }, isCI, dryRun } = ctx;
|
|
893
|
+
const { github: { isWeb, token, owner, repo, changelog, releaseName } } = ctx;
|
|
894
|
+
const { github: { release, assets, prerelease: prerelease$1, autoGenerate, draft } } = config;
|
|
895
|
+
if (!release || dryRun) return;
|
|
896
|
+
const octokit = new Octokit({ auth: token });
|
|
897
|
+
if (isWeb) await createWebRelease({
|
|
898
|
+
owner,
|
|
899
|
+
repo,
|
|
900
|
+
tag: currentTag,
|
|
901
|
+
title: releaseName,
|
|
902
|
+
body: truncateBody(changelog),
|
|
903
|
+
prerelease: String(toPreRelease || prerelease$1)
|
|
904
|
+
});
|
|
905
|
+
else if (isCI) {
|
|
906
|
+
const res = await createCReleaseByTag(octokit, {
|
|
907
|
+
owner,
|
|
908
|
+
repo,
|
|
909
|
+
tag: currentTag,
|
|
910
|
+
name: releaseName,
|
|
911
|
+
body: autoGenerate ? "" : truncateBody(changelog),
|
|
912
|
+
draft,
|
|
913
|
+
prerelease: toPreRelease || prerelease$1,
|
|
914
|
+
generateReleaseNotes: autoGenerate,
|
|
915
|
+
makeLatest: "true"
|
|
916
|
+
});
|
|
917
|
+
if (!res) return;
|
|
918
|
+
if (assets.length) {
|
|
919
|
+
const { id: releaseId, upload_url: uploadUrl } = res;
|
|
920
|
+
await uploadAssets(octokit, {
|
|
921
|
+
owner,
|
|
922
|
+
repo,
|
|
923
|
+
name,
|
|
924
|
+
uploadUrl,
|
|
925
|
+
releaseId,
|
|
926
|
+
assets
|
|
927
|
+
});
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
};
|
|
931
|
+
|
|
1077
932
|
//#endregion
|
|
1078
933
|
//#region src/npm/prompts.ts
|
|
1079
934
|
const runNpmPublishPrompts = async (ctx, config) => {
|
|
@@ -1107,120 +962,71 @@ const runNpmOptPrompts = async (ctx, config) => {
|
|
|
1107
962
|
|
|
1108
963
|
//#endregion
|
|
1109
964
|
//#region src/npm/publish.ts
|
|
1110
|
-
const resolvePublishRegistry = (publishConfig = {}) => {
|
|
1111
|
-
if (publishConfig.registry) return publishConfig.registry;
|
|
1112
|
-
return Object.keys(publishConfig).filter((k) => k.endsWith("registry")).map((k) => publishConfig[k])[0];
|
|
1113
|
-
};
|
|
1114
|
-
const pingRegistry = async () => {
|
|
1115
|
-
return void 0 !== await runNpm(["ping"]);
|
|
1116
|
-
};
|
|
1117
|
-
const getAuthenticatedUser = () => {
|
|
1118
|
-
return runNpm(["whoami"]);
|
|
1119
|
-
};
|
|
1120
|
-
const getVersionByTag = (name, tag) => {
|
|
1121
|
-
return runNpm([
|
|
1122
|
-
"show",
|
|
1123
|
-
`${name}@${tag}`,
|
|
1124
|
-
"version"
|
|
1125
|
-
]);
|
|
1126
|
-
};
|
|
1127
|
-
const getPublishedVersion = (name) => {
|
|
1128
|
-
return runNpm([
|
|
1129
|
-
"info",
|
|
1130
|
-
name,
|
|
1131
|
-
"version"
|
|
1132
|
-
]);
|
|
1133
|
-
};
|
|
1134
965
|
const resolvePublishTag = async (pkgName, version) => {
|
|
1135
966
|
const { toPreRelease, preId } = parseVersion(version);
|
|
1136
|
-
if (
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
};
|
|
1141
|
-
const bumpPackageVersion = (version, args = [], cwd = ".") => {
|
|
1142
|
-
return runNpm([
|
|
1143
|
-
"version",
|
|
1144
|
-
version,
|
|
1145
|
-
"--no-git-tag-version",
|
|
1146
|
-
"--workspaces=false",
|
|
1147
|
-
"--allow-same-version",
|
|
1148
|
-
...args
|
|
1149
|
-
], { cwd });
|
|
967
|
+
if (toPreRelease) return preId || "next";
|
|
968
|
+
const active = await getPublishedVersion(pkgName);
|
|
969
|
+
if (!active) return "latest";
|
|
970
|
+
return lt(version, active) ? "previous" : "latest";
|
|
1150
971
|
};
|
|
1151
972
|
const isOtpError = (err) => err instanceof Error && /one-time password|otp/i.test(err.message);
|
|
1152
|
-
const publishPackage = (pkg$1 = ".", tag = "latest", args = []) => {
|
|
1153
|
-
return runNpm([
|
|
1154
|
-
"publish",
|
|
1155
|
-
"--access",
|
|
1156
|
-
"public",
|
|
1157
|
-
"--tag",
|
|
1158
|
-
tag,
|
|
1159
|
-
"--workspaces=false",
|
|
1160
|
-
...args
|
|
1161
|
-
], { cwd: pkg$1 });
|
|
1162
|
-
};
|
|
1163
|
-
const getPackageUrl = (name, next) => {
|
|
1164
|
-
return `https://www.npmjs.com/package/${name}/v/${next}`;
|
|
1165
|
-
};
|
|
1166
|
-
const isCollaborator = async (ctx) => {
|
|
1167
|
-
const { pkg: { name, publishConfig }, npm: { username } } = ctx;
|
|
1168
|
-
const registry = resolvePublishRegistry(publishConfig);
|
|
1169
|
-
const registryArg = registry ? ["--registry", registry] : [];
|
|
1170
|
-
let npmVersion = await runNpm(["--version"]);
|
|
1171
|
-
let accessCommand;
|
|
1172
|
-
if (gt(npmVersion, "9.0.0")) accessCommand = [
|
|
1173
|
-
"access",
|
|
1174
|
-
"list",
|
|
1175
|
-
"collaborators",
|
|
1176
|
-
"--json"
|
|
1177
|
-
];
|
|
1178
|
-
else accessCommand = ["access", "ls-collaborators"];
|
|
1179
|
-
const res = await runNpm([
|
|
1180
|
-
...accessCommand,
|
|
1181
|
-
name,
|
|
1182
|
-
...registryArg
|
|
1183
|
-
]);
|
|
1184
|
-
if (res) return JSON.parse(res ?? "{}")[username]?.includes("write");
|
|
1185
|
-
return false;
|
|
1186
|
-
};
|
|
1187
973
|
const npmCheck = async (ctx, config) => {
|
|
1188
|
-
const { pkg: { name, current, publishConfig: { registry } }
|
|
974
|
+
const { pkg: { name, current, publishConfig: { registry } } } = ctx;
|
|
1189
975
|
const { npm: { skipChecks } } = config;
|
|
1190
976
|
const tag = await resolvePublishTag(name, current);
|
|
1191
977
|
Object.assign(ctx.npm, { tag });
|
|
1192
978
|
if (skipChecks) return "";
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
return true;
|
|
1199
|
-
};
|
|
1200
|
-
if (!await getUser()) throw new Error(MSG.ERROR.NPM_AUTH);
|
|
1201
|
-
const latest = await getVersionByTag(name, tag);
|
|
979
|
+
const [pinged, username] = await Promise.all([pingRegistry(registry), getAuthenticatedUser(registry)]);
|
|
980
|
+
if (!pinged) throw new Error(MSG.ERROR.NPM_REGISTRY(registry));
|
|
981
|
+
if (!username) throw new Error(MSG.ERROR.NPM_AUTH);
|
|
982
|
+
Object.assign(ctx.npm, { username });
|
|
983
|
+
const latest = await getPublishedVersion(`${name}@${tag}`);
|
|
1202
984
|
if (latest) {
|
|
1203
|
-
if (!await
|
|
985
|
+
if (!await hasWriteAccess(name, username, registry)) throw new Error(MSG.ERROR.NPM_USER(username, name));
|
|
1204
986
|
}
|
|
1205
987
|
return latest ? `(${tag} → ${latest})` : `(${tag} → no published version)`;
|
|
1206
988
|
};
|
|
1207
989
|
const publishNpm = async (ctx, config) => {
|
|
1208
990
|
const { dryRun, selectedPkg, npm: { otp, tag } } = ctx;
|
|
1209
|
-
const { npm: { publish,
|
|
991
|
+
const { npm: { publish, publishArgs }, getPkgDir } = config;
|
|
1210
992
|
if (!publish) return;
|
|
1211
993
|
const otpArgs = otp ? ["--otp", otp] : [];
|
|
1212
994
|
const dryRunArg = dryRun ? ["--dry-run"] : [];
|
|
1213
|
-
return publishPackage(
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
995
|
+
return publishPackage({
|
|
996
|
+
tag,
|
|
997
|
+
args: [
|
|
998
|
+
...otpArgs,
|
|
999
|
+
...publishArgs,
|
|
1000
|
+
...dryRunArg
|
|
1001
|
+
],
|
|
1002
|
+
cwd: getPkgDir(selectedPkg)
|
|
1003
|
+
});
|
|
1218
1004
|
};
|
|
1219
1005
|
|
|
1220
1006
|
//#endregion
|
|
1221
1007
|
//#region src/action.ts
|
|
1222
1008
|
var Action = class {
|
|
1223
|
-
async
|
|
1009
|
+
async handleRelease(cmdArgs, options) {
|
|
1010
|
+
const { ctx, config } = await this.createContext(cmdArgs, options);
|
|
1011
|
+
await this.checkTask(ctx, config);
|
|
1012
|
+
await this.bumpTask(ctx, config);
|
|
1013
|
+
await this.changelogTask(ctx, config);
|
|
1014
|
+
await this.gitTask(ctx, config);
|
|
1015
|
+
await this.npmTask(ctx, config);
|
|
1016
|
+
await this.githubTask(ctx, config);
|
|
1017
|
+
if (ctx.dryRun) gitRollback(ctx);
|
|
1018
|
+
taskEnd(MSG.OUTRO(ctx.dryRun));
|
|
1019
|
+
}
|
|
1020
|
+
async handlePrepareRelease(cmdArgs, options) {
|
|
1021
|
+
const { ctx, config } = await this.createContext(cmdArgs, options);
|
|
1022
|
+
await this.checkTask(ctx, config);
|
|
1023
|
+
await this.bumpTask(ctx, config);
|
|
1024
|
+
await this.changelogTask(ctx, config);
|
|
1025
|
+
await this.gitTask(ctx, config);
|
|
1026
|
+
if (ctx.dryRun) gitRollback(ctx);
|
|
1027
|
+
taskEnd(MSG.OUTRO_PREPARE(ctx.dryRun));
|
|
1028
|
+
}
|
|
1029
|
+
async createContext(cmdArgs, options) {
|
|
1224
1030
|
const { version: cVersion, name: cName } = readJsonFile(join(__dirname, "../package.json"));
|
|
1225
1031
|
const { otp, package: defPkg, ...others } = options;
|
|
1226
1032
|
const { showChangelog, showRelease, ci, dryRun } = Object.fromEntries(Object.entries(others).map(([k, v]) => [k, Boolean(v)]));
|
|
@@ -1284,43 +1090,54 @@ var Action = class {
|
|
|
1284
1090
|
const formated = formatMessage(message, pkg$1);
|
|
1285
1091
|
formated && msg("PKG", formated);
|
|
1286
1092
|
}
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
await this.npmTask(ctx, config);
|
|
1292
|
-
await this.githubTask(ctx, config);
|
|
1293
|
-
if (dryRun) gitRollback$1(ctx);
|
|
1294
|
-
taskEnd(MSG.OUTRO(dryRun));
|
|
1093
|
+
return {
|
|
1094
|
+
ctx,
|
|
1095
|
+
config
|
|
1096
|
+
};
|
|
1295
1097
|
}
|
|
1296
1098
|
async checkTask(ctx, config) {
|
|
1297
|
-
const {
|
|
1298
|
-
await tasks([
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1099
|
+
const { dryRun } = ctx;
|
|
1100
|
+
await tasks([
|
|
1101
|
+
{
|
|
1102
|
+
title: MSG.CHECK.GIT.CHECKING,
|
|
1103
|
+
task: async () => {
|
|
1104
|
+
await gitCheck(ctx, config);
|
|
1105
|
+
const { git: { remoteName, remoteUrl } } = ctx;
|
|
1106
|
+
return success(MSG.CHECK.GIT.CHECKED(remoteName, remoteUrl), dryRun);
|
|
1107
|
+
}
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
title: MSG.CHECK.NPM.CHECKING,
|
|
1111
|
+
task: async () => {
|
|
1112
|
+
const msg$1 = await npmCheck(ctx, config);
|
|
1113
|
+
const { pkg: { publishConfig: { registry } } } = ctx;
|
|
1114
|
+
return success(MSG.CHECK.NPM.CHECKED(registry, msg$1), dryRun);
|
|
1115
|
+
},
|
|
1116
|
+
enabled: !ctx.noNpm
|
|
1311
1117
|
},
|
|
1312
|
-
|
|
1313
|
-
|
|
1118
|
+
{
|
|
1119
|
+
title: MSG.CHECK.GITHUB.CHECKING,
|
|
1120
|
+
task: async () => {
|
|
1121
|
+
const msg$1 = await githubCheck(ctx, config);
|
|
1122
|
+
const { github: { owner, repo } } = ctx;
|
|
1123
|
+
const repository = getGithubUrl(owner, repo);
|
|
1124
|
+
return success(MSG.CHECK.GITHUB.CHECKED(repository, msg$1), dryRun);
|
|
1125
|
+
},
|
|
1126
|
+
enabled: !ctx.noGitHub
|
|
1127
|
+
}
|
|
1128
|
+
]).catch(abortOnError);
|
|
1314
1129
|
}
|
|
1315
1130
|
async bumpTask(ctx, config) {
|
|
1316
1131
|
const { pkg: { current }, dryRun, isCI, showRelease, showChangelog, selectedPkg } = ctx;
|
|
1317
|
-
const { hooks, getPkgDir } = config;
|
|
1132
|
+
const { isMonorepo, hooks, getPkgDir, tagPrefix } = config;
|
|
1318
1133
|
const need = (ctx$1) => {
|
|
1319
1134
|
const { pkg: { next: next$1 }, isIncrement: isIncrement$1 } = ctx$1;
|
|
1320
1135
|
return !next$1 && isIncrement$1;
|
|
1321
1136
|
};
|
|
1322
1137
|
if (need(ctx)) {
|
|
1323
|
-
const
|
|
1138
|
+
const next$1 = (isCI ? getCIVersion(current) : void 0) || await runVersionPrompts(ctx, config);
|
|
1139
|
+
ctx.isIncrement = isNeq(current, next$1);
|
|
1140
|
+
const parsed = parseVersion(next$1);
|
|
1324
1141
|
Object.assign(ctx.pkg, { ...parsed });
|
|
1325
1142
|
}
|
|
1326
1143
|
const { pkg: { next }, isIncrement } = ctx;
|
|
@@ -1336,25 +1153,30 @@ var Action = class {
|
|
|
1336
1153
|
}
|
|
1337
1154
|
}]).catch((err) => abortOnError(err, ctx));
|
|
1338
1155
|
await runLifeCycleHook(hooks, "after:bump", dryRun);
|
|
1339
|
-
const { from, to } = await resolveChangelogRange(isIncrement);
|
|
1340
|
-
const
|
|
1341
|
-
|
|
1342
|
-
|
|
1156
|
+
const { from, to } = await resolveChangelogRange(isIncrement, isMonorepo ? `${tagPrefix?.(selectedPkg)}*` : "*");
|
|
1157
|
+
const commits = await getLog(from, to, getPkgDir(selectedPkg));
|
|
1158
|
+
const changelog = await getChangelog({
|
|
1159
|
+
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1160
|
+
tagPrefix: tagPrefix?.(selectedPkg)
|
|
1161
|
+
});
|
|
1162
|
+
Object.assign(ctx.github, { changelog });
|
|
1163
|
+
if (commits) {
|
|
1164
|
+
msg("GIT", `Changelog(${from}...${to}):${eol(2)}` + changelog.trimEnd());
|
|
1165
|
+
msg("GIT", `Commits(${from}...${to}):${eol(2)}` + commits);
|
|
1343
1166
|
if (showChangelog) taskEnd(MSG.LOG.SHOW_CHANGELOG);
|
|
1344
1167
|
} else msg("GIT", MSG.LOG.CHANGELOG_EMPTY);
|
|
1345
1168
|
}
|
|
1346
1169
|
async changelogTask(ctx, config) {
|
|
1347
1170
|
const { isIncrement, dryRun, selectedPkg } = ctx;
|
|
1348
|
-
const { getPkgDir,
|
|
1171
|
+
const { getPkgDir, tagPrefix } = config;
|
|
1349
1172
|
await tasks([{
|
|
1350
1173
|
title: MSG.TASK.CHANGELOG.START,
|
|
1351
1174
|
task: async () => {
|
|
1352
|
-
|
|
1175
|
+
await generateChangelog({
|
|
1353
1176
|
getPkgDir: () => getPkgDir(selectedPkg),
|
|
1354
|
-
tagPrefix:
|
|
1177
|
+
tagPrefix: tagPrefix?.(selectedPkg)
|
|
1355
1178
|
});
|
|
1356
|
-
await
|
|
1357
|
-
Object.assign(ctx.github, { changelog });
|
|
1179
|
+
await gitAddAll();
|
|
1358
1180
|
return success(MSG.TASK.CHANGELOG.END, dryRun);
|
|
1359
1181
|
},
|
|
1360
1182
|
enabled: isIncrement
|
|
@@ -1408,8 +1230,9 @@ var Action = class {
|
|
|
1408
1230
|
await tasks([{
|
|
1409
1231
|
title: MSG.TASK.GITHUB.START,
|
|
1410
1232
|
task: async () => {
|
|
1233
|
+
const { github: { owner, repo }, git: { currentTag } } = ctx;
|
|
1411
1234
|
await createRelease(ctx, config);
|
|
1412
|
-
const url = getGithubReleaseUrl(
|
|
1235
|
+
const url = getGithubReleaseUrl(owner, repo, currentTag);
|
|
1413
1236
|
return success(MSG.TASK.GITHUB.END(url), dryRun);
|
|
1414
1237
|
}
|
|
1415
1238
|
}]).catch((err) => abortOnError(err, ctx));
|
|
@@ -1420,8 +1243,14 @@ var Action = class {
|
|
|
1420
1243
|
//#endregion
|
|
1421
1244
|
//#region src/cli.ts
|
|
1422
1245
|
const pkg = readJsonFile(join(__dirname, "..", "package.json"));
|
|
1423
|
-
|
|
1424
|
-
|
|
1246
|
+
function withCommonOptions(cmd) {
|
|
1247
|
+
return cmd.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", false).option("-p, --package <pkg>", "Specify package name (CI only).", "").option("--otp <code>", "One-time password for npm publish.", "").option("--ci", "Enable CI mode.", false).option("--show-changelog", "Print changelog and exit.", false).option("--show-release", "Print release version and exit.", false).helpOption("-h, --help", "Display help information.");
|
|
1248
|
+
}
|
|
1249
|
+
withCommonOptions(program.name("release").description(pkg.description)).action(async (releaseType, options) => {
|
|
1250
|
+
await new Action().handleRelease(releaseType, options);
|
|
1251
|
+
});
|
|
1252
|
+
withCommonOptions(program.command("prepare").description("Prepare a release (bump version, commit, tag)")).action(async (releaseType, options) => {
|
|
1253
|
+
await new Action().handlePrepareRelease(releaseType, options);
|
|
1425
1254
|
});
|
|
1426
1255
|
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
1256
|
const { force = false, monorepo = false } = options;
|
|
@@ -1439,7 +1268,7 @@ program.command("init").description("Create a release configuration file").optio
|
|
|
1439
1268
|
packages: [ 'demo-a', 'demo-b' ],
|
|
1440
1269
|
getPkgDir: (pkg) => \`packages/\${ pkg }\`,
|
|
1441
1270
|
toTag: (pkg, version) => \`\${ pkg }@\${ version }\`,
|
|
1442
|
-
|
|
1271
|
+
tagPrefix: (pkg) => \`\${ pkg }@\`,
|
|
1443
1272
|
}`;
|
|
1444
1273
|
writeFileSync(configPath, `import { defineConfig } from '@peiyanlu/release'
|
|
1445
1274
|
|