@deot/dev-releaser 2.9.9 → 2.9.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +520 -591
- package/dist/index.js +496 -571
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -1,574 +1,499 @@
|
|
|
1
|
-
import chalk from
|
|
2
|
-
import { Locals, Logger, Shell, Utils } from
|
|
3
|
-
import * as path from
|
|
4
|
-
import { createRequire } from
|
|
5
|
-
import fs from
|
|
6
|
-
import { CommitParser } from
|
|
7
|
-
import semver from
|
|
8
|
-
import { confirm, input } from
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import { Locals, Logger, Shell, Utils } from "@deot/dev-shared";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import fs from "fs-extra";
|
|
6
|
+
import { CommitParser } from "conventional-commits-parser";
|
|
7
|
+
import semver from "semver";
|
|
8
|
+
import { confirm, input } from "@inquirer/prompts";
|
|
9
|
+
//#region packages/releaser/src/release.ts
|
|
10
|
+
var require$ = createRequire(process.cwd());
|
|
11
|
+
var HASH = "-hash-";
|
|
12
|
+
var SUFFIX = "🐒💨🙊";
|
|
13
|
+
var reBreaking = new RegExp(`(${{ noteKeywords: ["BREAKING CHANGE", "Breaking Change"] }.noteKeywords.join(")|(")})`);
|
|
14
|
+
var commitParser = new CommitParser();
|
|
15
|
+
var Release = class {
|
|
16
|
+
packageDir;
|
|
17
|
+
packageName;
|
|
18
|
+
packageFolderName;
|
|
19
|
+
packageOptions;
|
|
20
|
+
packageRelation;
|
|
21
|
+
config;
|
|
22
|
+
changeLog;
|
|
23
|
+
version;
|
|
24
|
+
commits;
|
|
25
|
+
commandOptions;
|
|
26
|
+
constructor(config, commandOptions) {
|
|
27
|
+
const { packageDir, packageRelation } = Locals.impl();
|
|
28
|
+
if (typeof config === "string") {
|
|
29
|
+
const packageFolderName = config;
|
|
30
|
+
config = {
|
|
31
|
+
dir: path.resolve(packageDir, packageFolderName),
|
|
32
|
+
name: packageFolderName
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
this.packageDir = config.dir;
|
|
36
|
+
this.packageName = Locals.getPackageName(config.name);
|
|
37
|
+
this.packageFolderName = config.name;
|
|
38
|
+
this.packageOptions = require$(`${this.packageDir}/package.json`);
|
|
39
|
+
this.packageRelation = packageRelation[this.packageName] || [];
|
|
40
|
+
this.config = config;
|
|
41
|
+
this.commits = [];
|
|
42
|
+
this.changeLog = "";
|
|
43
|
+
this.version = "";
|
|
44
|
+
this.commandOptions = commandOptions;
|
|
45
|
+
}
|
|
46
|
+
async parseCommits() {
|
|
47
|
+
const { workspace } = Locals.impl();
|
|
48
|
+
const { packageFolderName, commandOptions } = this;
|
|
49
|
+
const [latestTag] = await this.getTags();
|
|
50
|
+
Logger.log(chalk.yellow(`Last Release Tag`) + `: ${latestTag || "<none>"}`);
|
|
51
|
+
const params = [
|
|
52
|
+
"--no-pager",
|
|
53
|
+
"log",
|
|
54
|
+
`${latestTag}..HEAD`,
|
|
55
|
+
`--format=%B%n${HASH}%n%H${SUFFIX}`
|
|
56
|
+
];
|
|
57
|
+
let { stdout } = await Shell.exec("git", params);
|
|
58
|
+
let skipGetLog = false;
|
|
59
|
+
if (latestTag) {
|
|
60
|
+
const log1 = await Shell.exec("git", ["rev-parse", latestTag]);
|
|
61
|
+
const log2 = await Shell.exec("git", [
|
|
62
|
+
"--no-pager",
|
|
63
|
+
"log",
|
|
64
|
+
"-1",
|
|
65
|
+
"--format=%H"
|
|
66
|
+
]);
|
|
67
|
+
if (log1.stdout === log2.stdout) skipGetLog = true;
|
|
68
|
+
}
|
|
69
|
+
if (!skipGetLog && !stdout) {
|
|
70
|
+
if (latestTag) params.splice(2, 1, `${latestTag}`);
|
|
71
|
+
else params.splice(2, 1, "HEAD");
|
|
72
|
+
({stdout} = await Shell.exec("git", params));
|
|
73
|
+
}
|
|
74
|
+
const allowTypes = [
|
|
75
|
+
"feat",
|
|
76
|
+
`fix`,
|
|
77
|
+
`break change`,
|
|
78
|
+
`style`,
|
|
79
|
+
`perf`,
|
|
80
|
+
`types`,
|
|
81
|
+
`refactor`,
|
|
82
|
+
`chore`
|
|
83
|
+
];
|
|
84
|
+
const rePlugin = new RegExp(`^(${allowTypes.join("|")})${workspace ? `\\(([,\\w-]+(,))?${packageFolderName}((,)[,\\w-]+)?\\)` : "(\\(.+\\))?"}: .*`, "i");
|
|
85
|
+
const reAll = workspace && new RegExp(`^(${allowTypes.join("|")})\\(\\*\\): .*`, "i");
|
|
86
|
+
const allCommits = commandOptions.commits || stdout.split(SUFFIX);
|
|
87
|
+
const commits = allCommits.filter((commit) => {
|
|
88
|
+
const chunk = commit.trim();
|
|
89
|
+
return chunk && (rePlugin.test(chunk) || reAll && reAll.test(chunk));
|
|
90
|
+
}).map((commit) => {
|
|
91
|
+
const node = commitParser.parse(commit);
|
|
92
|
+
const body = node.body || node.footer;
|
|
93
|
+
if (!node.type) node.type = commitParser.parse(node.header?.replace(/\(.+\)!?:/, ":") || "").type;
|
|
94
|
+
if (!node.hash) node.hash = commit.split(HASH).pop()?.trim();
|
|
95
|
+
node.breaking = reBreaking.test(body) || /!:/.test(node.header);
|
|
96
|
+
node.effect = false;
|
|
97
|
+
node.custom = false;
|
|
98
|
+
return node;
|
|
99
|
+
});
|
|
100
|
+
if (!commits.length) Logger.log(chalk.red(`No Commits Found.`));
|
|
101
|
+
else Logger.log(chalk.yellow(`Found `) + chalk.bold(`${allCommits.length}`) + ` Commits, ` + chalk.bold(`${commits.length}`) + " Commits Valid");
|
|
102
|
+
const { skipUpdatePackage } = commandOptions;
|
|
103
|
+
if (commits.length && skipUpdatePackage) {
|
|
104
|
+
let skip = false;
|
|
105
|
+
if (typeof skipUpdatePackage === "boolean" && skipUpdatePackage) skip = await confirm({
|
|
106
|
+
message: `Skip Update(${this.packageName}@${this.packageOptions.version}):`,
|
|
107
|
+
default: true
|
|
108
|
+
});
|
|
109
|
+
else if (typeof skipUpdatePackage === "string" && (skipUpdatePackage === "*" || skipUpdatePackage.split(",").includes(this.packageName))) skip = true;
|
|
110
|
+
if (skip) {
|
|
111
|
+
Logger.log(chalk.red(`Skipping Update\n`));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
await this.updateVersion();
|
|
116
|
+
await this.updateCommits(commits);
|
|
117
|
+
const { forceUpdatePackage } = commandOptions;
|
|
118
|
+
if (!commits.length && forceUpdatePackage) {
|
|
119
|
+
let force = false;
|
|
120
|
+
if (typeof forceUpdatePackage === "boolean" && forceUpdatePackage) force = await confirm({
|
|
121
|
+
message: `Force Update(${this.packageName}@${this.packageOptions.version}):`,
|
|
122
|
+
default: true
|
|
123
|
+
});
|
|
124
|
+
else if (typeof forceUpdatePackage === "string" && (forceUpdatePackage === "*" || forceUpdatePackage.split(",").includes(this.packageName))) force = true;
|
|
125
|
+
if (force) {
|
|
126
|
+
const versionChanged = `\`${this.packageOptions.version}\` -> \`${this.version}\``;
|
|
127
|
+
this.commits = [{
|
|
128
|
+
type: "chore",
|
|
129
|
+
header: `chore(${this.packageFolderName || "release"}): force-publish ${versionChanged}`,
|
|
130
|
+
hash: "",
|
|
131
|
+
effect: false,
|
|
132
|
+
breaking: false,
|
|
133
|
+
custom: true
|
|
134
|
+
}];
|
|
135
|
+
this.changeLog = `### Force Update Package\n\n- ${versionChanged}`.trim();
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async getTags() {
|
|
140
|
+
const { packageName } = this;
|
|
141
|
+
const params = [
|
|
142
|
+
"tag",
|
|
143
|
+
"--list",
|
|
144
|
+
`'${packageName}@*'`,
|
|
145
|
+
"--sort",
|
|
146
|
+
"-v:refname"
|
|
147
|
+
];
|
|
148
|
+
const { stdout } = await Shell.exec("git", params);
|
|
149
|
+
return stdout.split("\n");
|
|
150
|
+
}
|
|
151
|
+
rebuildChangeLog(commits) {
|
|
152
|
+
const { packageDir } = this;
|
|
153
|
+
const { homepage, workspace } = Locals.impl();
|
|
154
|
+
const logPath = path.resolve(packageDir, "./CHANGELOG.md");
|
|
155
|
+
const logFile = fs.existsSync(logPath) ? fs.readFileSync(logPath, "utf-8") : "";
|
|
156
|
+
const notes = {
|
|
157
|
+
breaking: [],
|
|
158
|
+
features: [],
|
|
159
|
+
fixes: [],
|
|
160
|
+
updates: []
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* (close #1) -> issues
|
|
164
|
+
* close (#1) -> issues
|
|
165
|
+
* (#1) -> pull request
|
|
166
|
+
*
|
|
167
|
+
* JS支持后行断言
|
|
168
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Assertions
|
|
169
|
+
*/
|
|
170
|
+
const closeRegxp = /\(?(closes? )\(?#((\d+))\)/gi;
|
|
171
|
+
const pullRegxp = /(?<!closes? )\((#(\d+))\)/gi;
|
|
172
|
+
for (const commit of commits) {
|
|
173
|
+
const { effect, breaking, hash, header, type } = commit;
|
|
174
|
+
/**
|
|
175
|
+
* 确保当log一样时,commit hash判断log是不一致的
|
|
176
|
+
* close: commit下自行提交(log可能会相同)需添加ref
|
|
177
|
+
* pr: 由github pr生成一条提交记录(唯一的, 无需添加ref)
|
|
178
|
+
*/
|
|
179
|
+
const ref = !hash || pullRegxp.test(header) ? "" : ` ([${hash?.substring(0, 7)}](${homepage}/commit/${hash}))`;
|
|
180
|
+
let message = header?.trim();
|
|
181
|
+
if (workspace && !effect) message = message.replace(/\(.+\)!?:/, ":");
|
|
182
|
+
message = message.replace(pullRegxp, `[$1](${homepage}/pull/$2)`).replace(closeRegxp, `[$1$2](${homepage}/issues/$2)`) + ref;
|
|
183
|
+
if (breaking) notes.breaking.push(message);
|
|
184
|
+
else if (type === "fix") notes.fixes.push(message);
|
|
185
|
+
else if (type === "feat") notes.features.push(message);
|
|
186
|
+
else notes.updates.push(message);
|
|
187
|
+
}
|
|
188
|
+
Object.keys(notes).forEach((i) => {
|
|
189
|
+
notes[i] = notes[i].filter((j) => {
|
|
190
|
+
return !logFile.includes(j);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
const parts = [
|
|
194
|
+
notes.breaking.length ? `### Breaking Changes\n\n- ${notes.breaking.join("\n- ")}`.trim() : "",
|
|
195
|
+
notes.fixes.length ? `### Bugfixes\n\n- ${notes.fixes.join("\n- ")}`.trim() : "",
|
|
196
|
+
notes.features.length ? `### Features\n\n- ${notes.features.join("\n- ")}`.trim() : "",
|
|
197
|
+
notes.updates.length ? `### Updates\n\n- ${notes.updates.join("\n- ")}`.trim() : ""
|
|
198
|
+
].filter(Boolean);
|
|
199
|
+
const newLog = parts.join("\n\n");
|
|
200
|
+
return !parts.length || logFile.includes(newLog) ? "" : newLog;
|
|
201
|
+
}
|
|
202
|
+
async updateVersion() {
|
|
203
|
+
const { packageOptions, commits, commandOptions } = this;
|
|
204
|
+
const { version } = packageOptions;
|
|
205
|
+
let newVersion = "";
|
|
206
|
+
if (commandOptions.customVersion) {
|
|
207
|
+
newVersion = commandOptions.customVersion;
|
|
208
|
+
if (!/\d+.\d+.\d+/.test(newVersion) || version === newVersion) newVersion = await input({
|
|
209
|
+
message: `Custom Update Version(${this.packageName}@${version}):`,
|
|
210
|
+
default: "",
|
|
211
|
+
validate: (answer) => {
|
|
212
|
+
if (!/\d+.\d+.\d+/.test(answer)) return "Version Should Be Like x.x.x";
|
|
213
|
+
if (answer === version) return "Version Should Be Diff Than Before";
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
} else {
|
|
218
|
+
const intersection = [
|
|
219
|
+
commandOptions.major && "major",
|
|
220
|
+
commandOptions.minor && "minor",
|
|
221
|
+
commandOptions.patch && "patch"
|
|
222
|
+
].filter((i) => !!i);
|
|
223
|
+
if (intersection.length) newVersion = semver.inc(version, intersection[0]) || "";
|
|
224
|
+
else {
|
|
225
|
+
const types = new Set(commits.map(({ type }) => type));
|
|
226
|
+
const level = commits.some((commit) => !!commit.breaking) ? "major" : types.has("feat") ? "minor" : "patch";
|
|
227
|
+
newVersion = semver.inc(version, level) || "";
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
this.version = newVersion;
|
|
231
|
+
}
|
|
232
|
+
isChanged() {
|
|
233
|
+
return !!this.commits.length;
|
|
234
|
+
}
|
|
235
|
+
async updateCommits(commits, source) {
|
|
236
|
+
if (!commits.length) return;
|
|
237
|
+
const { packageName } = this;
|
|
238
|
+
const olds = this.commits.map((i) => JSON.stringify({
|
|
239
|
+
...i,
|
|
240
|
+
effect: false
|
|
241
|
+
}));
|
|
242
|
+
const newCommits = commits.filter((i) => {
|
|
243
|
+
return !olds.includes(JSON.stringify({
|
|
244
|
+
...i,
|
|
245
|
+
effect: false
|
|
246
|
+
}));
|
|
247
|
+
}).map((j) => {
|
|
248
|
+
return {
|
|
249
|
+
...j,
|
|
250
|
+
effect: !!source
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
if (newCommits.length && this.commits.length) this.commits = this.commits.filter((i) => !i.custom);
|
|
254
|
+
const commits$ = this.commits.concat(newCommits);
|
|
255
|
+
if (source) Logger.log(chalk.magenta(`MERGE COMMITS: `) + chalk.bold(`${commits.length}`) + " Commits. merge " + chalk.yellow(source) + " into " + chalk.green(packageName));
|
|
256
|
+
else Logger.log(``);
|
|
257
|
+
const changeLog = this.rebuildChangeLog(commits$);
|
|
258
|
+
if (changeLog) {
|
|
259
|
+
this.commits = commits$;
|
|
260
|
+
this.changeLog = changeLog;
|
|
261
|
+
} else if (commits.length) Logger.log(chalk.red(`${commits.length} Commits Already Exists.`));
|
|
262
|
+
}
|
|
263
|
+
async updatePackageOptions(relationVerisons = {}) {
|
|
264
|
+
if (!this.isChanged()) return;
|
|
265
|
+
const { packageDir, packageOptions, commandOptions } = this;
|
|
266
|
+
const { dependencies, devDependencies } = packageOptions;
|
|
267
|
+
const newVersion = this.version;
|
|
268
|
+
Logger.log(chalk.yellow(`New Version: `) + `${newVersion}`);
|
|
269
|
+
packageOptions.version = newVersion;
|
|
270
|
+
if (Object.keys(this.packageRelation).length) for (const packageName$ in relationVerisons) {
|
|
271
|
+
const newVersion$ = relationVerisons[packageName$];
|
|
272
|
+
if (dependencies?.[packageName$]) dependencies[packageName$] = newVersion$;
|
|
273
|
+
if (devDependencies?.[packageName$]) devDependencies[packageName$] = newVersion$;
|
|
274
|
+
}
|
|
275
|
+
if (commandOptions.dryRun) {
|
|
276
|
+
Logger.log(chalk.yellow(`Skipping package.json Update`));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
Logger.log(chalk.yellow(`Updating `) + "package.json");
|
|
280
|
+
fs.outputFileSync(`${packageDir}/package.json`, JSON.stringify(packageOptions, null, 2));
|
|
281
|
+
}
|
|
282
|
+
async updateChangelog() {
|
|
283
|
+
if (!this.isChanged()) return;
|
|
284
|
+
const { packageName, packageDir, packageOptions, commandOptions } = this;
|
|
285
|
+
const title = `# ${packageName} ChangeLog`;
|
|
286
|
+
const [date] = (/* @__PURE__ */ new Date()).toISOString().split("T");
|
|
287
|
+
const logPath = path.resolve(packageDir, "./CHANGELOG.md");
|
|
288
|
+
const logFile = fs.existsSync(logPath) ? fs.readFileSync(logPath, "utf-8") : "";
|
|
289
|
+
const oldNotes = logFile.startsWith(title) ? logFile.slice(title.length).trim() : logFile;
|
|
290
|
+
const newLog = [
|
|
291
|
+
`## v${packageOptions.version}`,
|
|
292
|
+
`_${date}_`,
|
|
293
|
+
this.changeLog
|
|
294
|
+
].filter(Boolean).join("\n\n");
|
|
295
|
+
if (commandOptions.dryRun) {
|
|
296
|
+
Logger.log(chalk.yellow(`New ChangeLog:`) + `\n${newLog}`);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
Logger.log(chalk.yellow(`Updating `) + `CHANGELOG.md`);
|
|
300
|
+
let content = [
|
|
301
|
+
title,
|
|
302
|
+
newLog,
|
|
303
|
+
oldNotes
|
|
304
|
+
].filter(Boolean).join("\n\n");
|
|
305
|
+
if (!content.endsWith("\n")) content += "\n";
|
|
306
|
+
fs.writeFileSync(logPath, content, "utf-8");
|
|
307
|
+
}
|
|
308
|
+
async test() {
|
|
309
|
+
if (!this.isChanged()) return;
|
|
310
|
+
const { commandOptions } = this;
|
|
311
|
+
if (commandOptions.dryRun) {
|
|
312
|
+
Logger.log(chalk.yellow("Skipping Test"));
|
|
313
|
+
return;
|
|
314
|
+
} else Logger.log(chalk.yellow("Test..."));
|
|
315
|
+
await Shell.exec(`npm run test -- --package-name ${this.packageName}${commandOptions.coverage ? "" : " --no-coverage"}`);
|
|
316
|
+
}
|
|
317
|
+
async build() {
|
|
318
|
+
if (!this.isChanged()) return;
|
|
319
|
+
const { commandOptions } = this;
|
|
320
|
+
if (commandOptions.dryRun) {
|
|
321
|
+
Logger.log(chalk.yellow("Skipping Build"));
|
|
322
|
+
return;
|
|
323
|
+
} else Logger.log(chalk.yellow("Build..."));
|
|
324
|
+
await Shell.exec(`npm run build -- --package-name ${this.packageName}`);
|
|
325
|
+
}
|
|
326
|
+
async publish() {
|
|
327
|
+
const { commandOptions } = this;
|
|
328
|
+
if (!this.isChanged() || !commandOptions.publish) return;
|
|
329
|
+
const { packageDir, packageName } = this;
|
|
330
|
+
Logger.log(chalk.magenta(`PUBLISH: `) + packageName);
|
|
331
|
+
if (commandOptions.dryRun) {
|
|
332
|
+
Logger.log(chalk.yellow(`Skipping Publish`));
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
Logger.log(chalk.cyan(`\n Publishing to NPM`));
|
|
336
|
+
await Shell.spawn("npm", [
|
|
337
|
+
"publish",
|
|
338
|
+
"--no-git-checks",
|
|
339
|
+
"--access",
|
|
340
|
+
"public"
|
|
341
|
+
], { cwd: packageDir });
|
|
342
|
+
}
|
|
343
|
+
async tag() {
|
|
344
|
+
const { commandOptions } = this;
|
|
345
|
+
if (!this.isChanged() || !commandOptions.tag) return;
|
|
346
|
+
const { packageDir, packageName, packageOptions } = this;
|
|
347
|
+
Logger.log(chalk.magenta(`TAG: `) + packageName);
|
|
348
|
+
if (commandOptions.dryRun) {
|
|
349
|
+
Logger.log(chalk.yellow(`Skipping Git Tag`));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const tagName = `${packageName}@${packageOptions.version}`;
|
|
353
|
+
Logger.log(chalk.blue(`\n Tagging`) + chalk.grey(`${tagName}`));
|
|
354
|
+
await Shell.spawn("git", ["tag", tagName], { cwd: packageDir });
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* 清理tags,仅保留最后一个tag. 相当于tags仅用于记录commit开始的位置
|
|
358
|
+
* git push以后执行。即时删除失败了,也不会产生不必要的影响
|
|
359
|
+
*/
|
|
360
|
+
async cleanTagsAndKeepLastTag() {
|
|
361
|
+
const { commandOptions } = this;
|
|
362
|
+
if (!commandOptions.keepLastTag) return;
|
|
363
|
+
let tags = await this.getTags();
|
|
364
|
+
tags = tags.slice(1).filter((i) => !!i).reverse();
|
|
365
|
+
if (!tags.length) return;
|
|
366
|
+
const { packageName } = this;
|
|
367
|
+
Logger.log(chalk.magenta(`CLEAN TAGS: `) + packageName);
|
|
368
|
+
if (commandOptions.dryRun) {
|
|
369
|
+
Logger.log(chalk.yellow(`Skipping Tags Clean`));
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
await tags.reduce((preProcess, tag) => {
|
|
373
|
+
preProcess = preProcess.then(() => Shell.spawn("git", [
|
|
374
|
+
"push",
|
|
375
|
+
"origin",
|
|
376
|
+
"--delete",
|
|
377
|
+
tag
|
|
378
|
+
])).then(() => Shell.spawn("git", [
|
|
379
|
+
"tag",
|
|
380
|
+
"--delete",
|
|
381
|
+
tag
|
|
382
|
+
]));
|
|
383
|
+
return preProcess;
|
|
384
|
+
}, Promise.resolve());
|
|
385
|
+
}
|
|
386
|
+
async process() {
|
|
387
|
+
const { workspace } = Locals.impl();
|
|
388
|
+
const { packageName, packageDir, packageFolderName } = this;
|
|
389
|
+
if (!packageDir || !fs.pathExists(packageDir)) throw new RangeError(`Could not find directory for package: ${packageFolderName}`);
|
|
390
|
+
Logger.log(chalk.cyan(`Releasing ${packageName}`) + " from " + chalk.grey(`${workspace}/${packageFolderName}`));
|
|
391
|
+
await this.parseCommits();
|
|
392
|
+
return this;
|
|
393
|
+
}
|
|
16
394
|
};
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class Release {
|
|
20
|
-
packageDir;
|
|
21
|
-
packageName;
|
|
22
|
-
packageFolderName;
|
|
23
|
-
packageOptions;
|
|
24
|
-
packageRelation;
|
|
25
|
-
config;
|
|
26
|
-
changeLog;
|
|
27
|
-
version;
|
|
28
|
-
commits;
|
|
29
|
-
commandOptions;
|
|
30
|
-
constructor(config, commandOptions) {
|
|
31
|
-
const { packageDir, packageRelation } = Locals.impl();
|
|
32
|
-
if (typeof config === "string") {
|
|
33
|
-
const packageFolderName = config;
|
|
34
|
-
const packageDir$ = path.resolve(packageDir, packageFolderName);
|
|
35
|
-
config = {
|
|
36
|
-
dir: packageDir$,
|
|
37
|
-
name: packageFolderName
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
this.packageDir = config.dir;
|
|
41
|
-
this.packageName = Locals.getPackageName(config.name);
|
|
42
|
-
this.packageFolderName = config.name;
|
|
43
|
-
this.packageOptions = require$(`${this.packageDir}/package.json`);
|
|
44
|
-
this.packageRelation = packageRelation[this.packageName] || [];
|
|
45
|
-
this.config = config;
|
|
46
|
-
this.commits = [];
|
|
47
|
-
this.changeLog = "";
|
|
48
|
-
this.version = "";
|
|
49
|
-
this.commandOptions = commandOptions;
|
|
50
|
-
}
|
|
51
|
-
async parseCommits() {
|
|
52
|
-
const { workspace } = Locals.impl();
|
|
53
|
-
const { packageFolderName, commandOptions } = this;
|
|
54
|
-
const [latestTag] = await this.getTags();
|
|
55
|
-
Logger.log(chalk.yellow(`Last Release Tag`) + `: ${latestTag || "<none>"}`);
|
|
56
|
-
const params = ["--no-pager", "log", `${latestTag}..HEAD`, `--format=%B%n${HASH}%n%H${SUFFIX}`];
|
|
57
|
-
let {
|
|
58
|
-
stdout
|
|
59
|
-
} = await Shell.exec("git", params);
|
|
60
|
-
let skipGetLog = false;
|
|
61
|
-
if (latestTag) {
|
|
62
|
-
const log1 = await Shell.exec("git", ["rev-parse", latestTag]);
|
|
63
|
-
const log2 = await Shell.exec("git", ["--no-pager", "log", "-1", "--format=%H"]);
|
|
64
|
-
if (log1.stdout === log2.stdout) {
|
|
65
|
-
skipGetLog = true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
if (!skipGetLog && !stdout) {
|
|
69
|
-
if (latestTag) {
|
|
70
|
-
params.splice(2, 1, `${latestTag}`);
|
|
71
|
-
} else {
|
|
72
|
-
params.splice(2, 1, "HEAD");
|
|
73
|
-
}
|
|
74
|
-
({ stdout } = await Shell.exec("git", params));
|
|
75
|
-
}
|
|
76
|
-
const allowTypes = ["feat", `fix`, `break change`, `style`, `perf`, `types`, `refactor`, `chore`];
|
|
77
|
-
const rePlugin = new RegExp(`^(${allowTypes.join("|")})${workspace ? `\\(([,\\w-]+(,))?${packageFolderName}((,)[,\\w-]+)?\\)` : "(\\(.+\\))?"}: .*`, "i");
|
|
78
|
-
const reAll = workspace && new RegExp(`^(${allowTypes.join("|")})\\(\\*\\): .*`, "i");
|
|
79
|
-
const allCommits = commandOptions.commits || stdout.split(SUFFIX);
|
|
80
|
-
const commits = allCommits.filter((commit) => {
|
|
81
|
-
const chunk = commit.trim();
|
|
82
|
-
return chunk && (rePlugin.test(chunk) || reAll && reAll.test(chunk));
|
|
83
|
-
}).map((commit) => {
|
|
84
|
-
const node = commitParser.parse(commit);
|
|
85
|
-
const body = node.body || node.footer;
|
|
86
|
-
if (!node.type) node.type = commitParser.parse(node.header?.replace(/\(.+\)!?:/, ":") || "").type;
|
|
87
|
-
if (!node.hash) node.hash = commit.split(HASH).pop()?.trim();
|
|
88
|
-
node.breaking = reBreaking.test(body) || /!:/.test(node.header);
|
|
89
|
-
node.effect = false;
|
|
90
|
-
node.custom = false;
|
|
91
|
-
return node;
|
|
92
|
-
});
|
|
93
|
-
if (!commits.length) {
|
|
94
|
-
Logger.log(chalk.red(`No Commits Found.`));
|
|
95
|
-
} else {
|
|
96
|
-
Logger.log(
|
|
97
|
-
chalk.yellow(`Found `) + chalk.bold(`${allCommits.length}`) + ` Commits, ` + chalk.bold(`${commits.length}`) + " Commits Valid"
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
const { skipUpdatePackage } = commandOptions;
|
|
101
|
-
if (commits.length && skipUpdatePackage) {
|
|
102
|
-
let skip = false;
|
|
103
|
-
if (typeof skipUpdatePackage === "boolean" && skipUpdatePackage) {
|
|
104
|
-
skip = await confirm({
|
|
105
|
-
message: `Skip Update(${this.packageName}@${this.packageOptions.version}):`,
|
|
106
|
-
default: true
|
|
107
|
-
});
|
|
108
|
-
} else if (typeof skipUpdatePackage === "string" && (skipUpdatePackage === "*" || skipUpdatePackage.split(",").includes(this.packageName))) {
|
|
109
|
-
skip = true;
|
|
110
|
-
}
|
|
111
|
-
if (skip) {
|
|
112
|
-
Logger.log(chalk.red(`Skipping Update
|
|
113
|
-
`));
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
await this.updateVersion();
|
|
118
|
-
await this.updateCommits(commits);
|
|
119
|
-
const { forceUpdatePackage } = commandOptions;
|
|
120
|
-
if (!commits.length && forceUpdatePackage) {
|
|
121
|
-
let force = false;
|
|
122
|
-
if (typeof forceUpdatePackage === "boolean" && forceUpdatePackage) {
|
|
123
|
-
force = await confirm({
|
|
124
|
-
message: `Force Update(${this.packageName}@${this.packageOptions.version}):`,
|
|
125
|
-
default: true
|
|
126
|
-
});
|
|
127
|
-
} else if (typeof forceUpdatePackage === "string" && (forceUpdatePackage === "*" || forceUpdatePackage.split(",").includes(this.packageName))) {
|
|
128
|
-
force = true;
|
|
129
|
-
}
|
|
130
|
-
if (force) {
|
|
131
|
-
const oldVersion = this.packageOptions.version;
|
|
132
|
-
const versionChanged = `\`${oldVersion}\` -> \`${this.version}\``;
|
|
133
|
-
this.commits = [
|
|
134
|
-
{
|
|
135
|
-
type: "chore",
|
|
136
|
-
header: `chore(${this.packageFolderName || "release"}): force-publish ${versionChanged}`,
|
|
137
|
-
hash: "",
|
|
138
|
-
effect: false,
|
|
139
|
-
breaking: false,
|
|
140
|
-
custom: true
|
|
141
|
-
}
|
|
142
|
-
];
|
|
143
|
-
this.changeLog = `### Force Update Package
|
|
144
|
-
|
|
145
|
-
- ${versionChanged}`.trim();
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
async getTags() {
|
|
150
|
-
const { packageName } = this;
|
|
151
|
-
const params = ["tag", "--list", `'${packageName}@*'`, "--sort", "-v:refname"];
|
|
152
|
-
const { stdout } = await Shell.exec("git", params);
|
|
153
|
-
return stdout.split("\n");
|
|
154
|
-
}
|
|
155
|
-
rebuildChangeLog(commits) {
|
|
156
|
-
const { packageDir } = this;
|
|
157
|
-
const { homepage, workspace } = Locals.impl();
|
|
158
|
-
const logPath = path.resolve(packageDir, "./CHANGELOG.md");
|
|
159
|
-
const logFile = fs.existsSync(logPath) ? fs.readFileSync(logPath, "utf-8") : "";
|
|
160
|
-
const notes = {
|
|
161
|
-
breaking: [],
|
|
162
|
-
features: [],
|
|
163
|
-
fixes: [],
|
|
164
|
-
updates: []
|
|
165
|
-
};
|
|
166
|
-
const closeRegxp = /\(?(closes? )\(?#((\d+))\)/ig;
|
|
167
|
-
const pullRegxp = /(?<!closes? )\((#(\d+))\)/ig;
|
|
168
|
-
for (const commit of commits) {
|
|
169
|
-
const { effect, breaking, hash, header, type } = commit;
|
|
170
|
-
const ref = !hash || pullRegxp.test(header) ? "" : ` ([${hash?.substring(0, 7)}](${homepage}/commit/${hash}))`;
|
|
171
|
-
let message = header?.trim();
|
|
172
|
-
if (workspace && !effect) {
|
|
173
|
-
message = message.replace(/\(.+\)!?:/, ":");
|
|
174
|
-
}
|
|
175
|
-
message = message.replace(pullRegxp, `[$1](${homepage}/pull/$2)`).replace(closeRegxp, `[$1$2](${homepage}/issues/$2)`) + ref;
|
|
176
|
-
if (breaking) {
|
|
177
|
-
notes.breaking.push(message);
|
|
178
|
-
} else if (type === "fix") {
|
|
179
|
-
notes.fixes.push(message);
|
|
180
|
-
} else if (type === "feat") {
|
|
181
|
-
notes.features.push(message);
|
|
182
|
-
} else {
|
|
183
|
-
notes.updates.push(message);
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
Object.keys(notes).forEach((i) => {
|
|
187
|
-
notes[i] = notes[i].filter((j) => {
|
|
188
|
-
return !logFile.includes(j);
|
|
189
|
-
});
|
|
190
|
-
});
|
|
191
|
-
const parts = [
|
|
192
|
-
notes.breaking.length ? `### Breaking Changes
|
|
193
|
-
|
|
194
|
-
- ${notes.breaking.join("\n- ")}`.trim() : "",
|
|
195
|
-
notes.fixes.length ? `### Bugfixes
|
|
196
|
-
|
|
197
|
-
- ${notes.fixes.join("\n- ")}`.trim() : "",
|
|
198
|
-
notes.features.length ? `### Features
|
|
199
|
-
|
|
200
|
-
- ${notes.features.join("\n- ")}`.trim() : "",
|
|
201
|
-
notes.updates.length ? `### Updates
|
|
202
|
-
|
|
203
|
-
- ${notes.updates.join("\n- ")}`.trim() : ""
|
|
204
|
-
].filter(Boolean);
|
|
205
|
-
const newLog = parts.join("\n\n");
|
|
206
|
-
return !parts.length || logFile.includes(newLog) ? "" : newLog;
|
|
207
|
-
}
|
|
208
|
-
async updateVersion() {
|
|
209
|
-
const { packageOptions, commits, commandOptions } = this;
|
|
210
|
-
const { version } = packageOptions;
|
|
211
|
-
let newVersion = "";
|
|
212
|
-
if (commandOptions.customVersion) {
|
|
213
|
-
newVersion = commandOptions.customVersion;
|
|
214
|
-
if (!/\d+.\d+.\d+/.test(newVersion) || version === newVersion) {
|
|
215
|
-
newVersion = await input(
|
|
216
|
-
{
|
|
217
|
-
message: `Custom Update Version(${this.packageName}@${version}):`,
|
|
218
|
-
default: "",
|
|
219
|
-
validate: (answer) => {
|
|
220
|
-
if (!/\d+.\d+.\d+/.test(answer)) {
|
|
221
|
-
return "Version Should Be Like x.x.x";
|
|
222
|
-
}
|
|
223
|
-
if (answer === version) {
|
|
224
|
-
return "Version Should Be Diff Than Before";
|
|
225
|
-
}
|
|
226
|
-
return true;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
const intersection = [
|
|
233
|
-
commandOptions.major && "major",
|
|
234
|
-
commandOptions.minor && "minor",
|
|
235
|
-
commandOptions.patch && "patch"
|
|
236
|
-
].filter((i) => !!i);
|
|
237
|
-
if (intersection.length) {
|
|
238
|
-
newVersion = semver.inc(version, intersection[0]) || "";
|
|
239
|
-
} else {
|
|
240
|
-
const types = new Set(commits.map(({
|
|
241
|
-
type
|
|
242
|
-
}) => type));
|
|
243
|
-
const breaking = commits.some((commit) => !!commit.breaking);
|
|
244
|
-
const level = breaking ? "major" : types.has("feat") ? "minor" : "patch";
|
|
245
|
-
newVersion = semver.inc(version, level) || "";
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
this.version = newVersion;
|
|
249
|
-
}
|
|
250
|
-
isChanged() {
|
|
251
|
-
return !!this.commits.length;
|
|
252
|
-
}
|
|
253
|
-
async updateCommits(commits, source) {
|
|
254
|
-
if (!commits.length) return;
|
|
255
|
-
const { packageName } = this;
|
|
256
|
-
const olds = this.commits.map((i) => JSON.stringify({ ...i, effect: false }));
|
|
257
|
-
const newCommits = commits.filter((i) => {
|
|
258
|
-
return !olds.includes(JSON.stringify({ ...i, effect: false }));
|
|
259
|
-
}).map((j) => {
|
|
260
|
-
return {
|
|
261
|
-
...j,
|
|
262
|
-
effect: !!source
|
|
263
|
-
};
|
|
264
|
-
});
|
|
265
|
-
if (newCommits.length && this.commits.length) {
|
|
266
|
-
this.commits = this.commits.filter((i) => !i.custom);
|
|
267
|
-
}
|
|
268
|
-
const commits$ = this.commits.concat(newCommits);
|
|
269
|
-
if (source) {
|
|
270
|
-
Logger.log(
|
|
271
|
-
chalk.magenta(`MERGE COMMITS: `) + chalk.bold(`${commits.length}`) + ` Commits. merge ` + chalk.yellow(source) + " into " + chalk.green(packageName)
|
|
272
|
-
);
|
|
273
|
-
} else {
|
|
274
|
-
Logger.log(``);
|
|
275
|
-
}
|
|
276
|
-
const changeLog = this.rebuildChangeLog(commits$);
|
|
277
|
-
if (changeLog) {
|
|
278
|
-
this.commits = commits$;
|
|
279
|
-
this.changeLog = changeLog;
|
|
280
|
-
} else if (commits.length) {
|
|
281
|
-
Logger.log(chalk.red(`${commits.length} Commits Already Exists.`));
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
async updatePackageOptions(relationVerisons = {}) {
|
|
285
|
-
if (!this.isChanged()) return;
|
|
286
|
-
const { packageDir, packageOptions, commandOptions } = this;
|
|
287
|
-
const { dependencies, devDependencies } = packageOptions;
|
|
288
|
-
const newVersion = this.version;
|
|
289
|
-
Logger.log(chalk.yellow(`New Version: `) + `${newVersion}`);
|
|
290
|
-
packageOptions.version = newVersion;
|
|
291
|
-
if (Object.keys(this.packageRelation).length) {
|
|
292
|
-
for (const packageName$ in relationVerisons) {
|
|
293
|
-
const newVersion$ = relationVerisons[packageName$];
|
|
294
|
-
if (dependencies?.[packageName$]) {
|
|
295
|
-
dependencies[packageName$] = newVersion$;
|
|
296
|
-
}
|
|
297
|
-
if (devDependencies?.[packageName$]) {
|
|
298
|
-
devDependencies[packageName$] = newVersion$;
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
if (commandOptions.dryRun) {
|
|
303
|
-
Logger.log(chalk.yellow(`Skipping package.json Update`));
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
Logger.log(chalk.yellow(`Updating `) + "package.json");
|
|
307
|
-
fs.outputFileSync(`${packageDir}/package.json`, JSON.stringify(packageOptions, null, 2));
|
|
308
|
-
}
|
|
309
|
-
async updateChangelog() {
|
|
310
|
-
if (!this.isChanged()) return;
|
|
311
|
-
const { packageName, packageDir, packageOptions, commandOptions } = this;
|
|
312
|
-
const title = `# ${packageName} ChangeLog`;
|
|
313
|
-
const [date] = (/* @__PURE__ */ new Date()).toISOString().split("T");
|
|
314
|
-
const logPath = path.resolve(packageDir, "./CHANGELOG.md");
|
|
315
|
-
const logFile = fs.existsSync(logPath) ? fs.readFileSync(logPath, "utf-8") : "";
|
|
316
|
-
const oldNotes = logFile.startsWith(title) ? logFile.slice(title.length).trim() : logFile;
|
|
317
|
-
const parts = [
|
|
318
|
-
`## v${packageOptions.version}`,
|
|
319
|
-
`_${date}_`,
|
|
320
|
-
this.changeLog
|
|
321
|
-
].filter(Boolean);
|
|
322
|
-
const newLog = parts.join("\n\n");
|
|
323
|
-
if (commandOptions.dryRun) {
|
|
324
|
-
Logger.log(chalk.yellow(`New ChangeLog:`) + `
|
|
325
|
-
${newLog}`);
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
Logger.log(chalk.yellow(`Updating `) + `CHANGELOG.md`);
|
|
329
|
-
let content = [title, newLog, oldNotes].filter(Boolean).join("\n\n");
|
|
330
|
-
if (!content.endsWith("\n")) content += "\n";
|
|
331
|
-
fs.writeFileSync(logPath, content, "utf-8");
|
|
332
|
-
}
|
|
333
|
-
async test() {
|
|
334
|
-
if (!this.isChanged()) return;
|
|
335
|
-
const { commandOptions } = this;
|
|
336
|
-
if (commandOptions.dryRun) {
|
|
337
|
-
Logger.log(chalk.yellow("Skipping Test"));
|
|
338
|
-
return;
|
|
339
|
-
} else {
|
|
340
|
-
Logger.log(chalk.yellow("Test..."));
|
|
341
|
-
}
|
|
342
|
-
await Shell.exec(`npm run test -- --package-name ${this.packageName}${commandOptions.coverage ? "" : " --no-coverage"}`);
|
|
343
|
-
}
|
|
344
|
-
async build() {
|
|
345
|
-
if (!this.isChanged()) return;
|
|
346
|
-
const { commandOptions } = this;
|
|
347
|
-
if (commandOptions.dryRun) {
|
|
348
|
-
Logger.log(chalk.yellow("Skipping Build"));
|
|
349
|
-
return;
|
|
350
|
-
} else {
|
|
351
|
-
Logger.log(chalk.yellow("Build..."));
|
|
352
|
-
}
|
|
353
|
-
await Shell.exec(`npm run build -- --package-name ${this.packageName}`);
|
|
354
|
-
}
|
|
355
|
-
async publish() {
|
|
356
|
-
const { commandOptions } = this;
|
|
357
|
-
if (!this.isChanged() || !commandOptions.publish) return;
|
|
358
|
-
const { packageDir, packageName } = this;
|
|
359
|
-
Logger.log(chalk.magenta(`PUBLISH: `) + packageName);
|
|
360
|
-
if (commandOptions.dryRun) {
|
|
361
|
-
Logger.log(chalk.yellow(`Skipping Publish`));
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
Logger.log(chalk.cyan(`
|
|
365
|
-
Publishing to NPM`));
|
|
366
|
-
await Shell.spawn(
|
|
367
|
-
"npm",
|
|
368
|
-
["publish", "--no-git-checks", "--access", "public"],
|
|
369
|
-
{
|
|
370
|
-
cwd: packageDir
|
|
371
|
-
}
|
|
372
|
-
);
|
|
373
|
-
}
|
|
374
|
-
async tag() {
|
|
375
|
-
const { commandOptions } = this;
|
|
376
|
-
if (!this.isChanged() || !commandOptions.tag) return;
|
|
377
|
-
const { packageDir, packageName, packageOptions } = this;
|
|
378
|
-
Logger.log(chalk.magenta(`TAG: `) + packageName);
|
|
379
|
-
if (commandOptions.dryRun) {
|
|
380
|
-
Logger.log(chalk.yellow(`Skipping Git Tag`));
|
|
381
|
-
return;
|
|
382
|
-
}
|
|
383
|
-
const tagName = `${packageName}@${packageOptions.version}`;
|
|
384
|
-
Logger.log(chalk.blue(`
|
|
385
|
-
Tagging`) + chalk.grey(`${tagName}`));
|
|
386
|
-
await Shell.spawn(
|
|
387
|
-
"git",
|
|
388
|
-
["tag", tagName],
|
|
389
|
-
{
|
|
390
|
-
cwd: packageDir
|
|
391
|
-
}
|
|
392
|
-
);
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* 清理tags,仅保留最后一个tag. 相当于tags仅用于记录commit开始的位置
|
|
396
|
-
* git push以后执行。即时删除失败了,也不会产生不必要的影响
|
|
397
|
-
*/
|
|
398
|
-
async cleanTagsAndKeepLastTag() {
|
|
399
|
-
const { commandOptions } = this;
|
|
400
|
-
if (!commandOptions.keepLastTag) return;
|
|
401
|
-
let tags = await this.getTags();
|
|
402
|
-
tags = tags.slice(1).filter((i) => !!i).reverse();
|
|
403
|
-
if (!tags.length) return;
|
|
404
|
-
const { packageName } = this;
|
|
405
|
-
Logger.log(chalk.magenta(`CLEAN TAGS: `) + packageName);
|
|
406
|
-
if (commandOptions.dryRun) {
|
|
407
|
-
Logger.log(chalk.yellow(`Skipping Tags Clean`));
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
await tags.reduce(
|
|
411
|
-
(preProcess, tag) => {
|
|
412
|
-
preProcess = preProcess.then(() => Shell.spawn("git", ["push", "origin", "--delete", tag])).then(() => Shell.spawn("git", ["tag", "--delete", tag]));
|
|
413
|
-
return preProcess;
|
|
414
|
-
},
|
|
415
|
-
Promise.resolve()
|
|
416
|
-
);
|
|
417
|
-
}
|
|
418
|
-
async process() {
|
|
419
|
-
const { workspace } = Locals.impl();
|
|
420
|
-
const { packageName, packageDir, packageFolderName } = this;
|
|
421
|
-
if (!packageDir || !fs.pathExists(packageDir)) {
|
|
422
|
-
throw new RangeError(`Could not find directory for package: ${packageFolderName}`);
|
|
423
|
-
}
|
|
424
|
-
Logger.log(chalk.cyan(`Releasing ${packageName}`) + " from " + chalk.grey(`${workspace}/${packageFolderName}`));
|
|
425
|
-
await this.parseCommits();
|
|
426
|
-
return this;
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
const release = (options, commandOptions) => {
|
|
430
|
-
return new Release(options, commandOptions);
|
|
395
|
+
var release = (options, commandOptions) => {
|
|
396
|
+
return new Release(options, commandOptions);
|
|
431
397
|
};
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
Logger.log(chalk.magenta(`PUSH: `) + "Nothing Chanaged.");
|
|
534
|
-
} else if (!options.push) {
|
|
535
|
-
Logger.log(chalk.magenta(`PUSH: `) + "Push Disabled.");
|
|
536
|
-
} else if (options.dryRun) {
|
|
537
|
-
Logger.log(chalk.magenta(`PUSH: `) + "Skipping Git Push");
|
|
538
|
-
} else {
|
|
539
|
-
Logger.log(chalk.yellow("Git Pull/Push..."));
|
|
540
|
-
await Shell.spawn("git", ["pull", "--rebase"]);
|
|
541
|
-
await Shell.spawn("git", ["push"]);
|
|
542
|
-
await Shell.spawn("git", ["push", "--tags"]);
|
|
543
|
-
}
|
|
544
|
-
Logger.log(chalk.blue(`
|
|
545
|
-
---------------------
|
|
546
|
-
`));
|
|
547
|
-
await inputs.reduce(
|
|
548
|
-
(preProcess, packageFolderName) => {
|
|
549
|
-
const instance = instances[packageFolderName];
|
|
550
|
-
preProcess = preProcess.then(() => instance.cleanTagsAndKeepLastTag());
|
|
551
|
-
return preProcess;
|
|
552
|
-
},
|
|
553
|
-
Promise.resolve()
|
|
554
|
-
);
|
|
555
|
-
Logger.log(chalk.magenta(`FINISH: `) + chalk.green(`Release Successed.`));
|
|
556
|
-
if (options.dryRun) {
|
|
557
|
-
Logger.log(
|
|
558
|
-
chalk.green("NO DRY RUN WAY: ") + chalk.grey(`npm run release -- --no-dry-run
|
|
559
|
-
`)
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
}, {
|
|
563
|
-
onError: (e) => {
|
|
564
|
-
if (typeof e === "number" && e === 1) {
|
|
565
|
-
Logger.error("发布失败");
|
|
566
|
-
} else {
|
|
567
|
-
Logger.error(e);
|
|
568
|
-
}
|
|
569
|
-
Logger.log(chalk.magenta(`FINISH: `) + chalk.red(`Release Failed.`));
|
|
570
|
-
process.exit(1);
|
|
571
|
-
}
|
|
572
|
-
});
|
|
573
|
-
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region packages/releaser/src/index.ts
|
|
400
|
+
var run = (options) => Utils.autoCatch(async () => {
|
|
401
|
+
options = {
|
|
402
|
+
coverage: true,
|
|
403
|
+
dryRun: true,
|
|
404
|
+
tag: true,
|
|
405
|
+
publish: true,
|
|
406
|
+
commit: true,
|
|
407
|
+
push: true,
|
|
408
|
+
keepLastTag: false,
|
|
409
|
+
...options
|
|
410
|
+
};
|
|
411
|
+
const locals = Locals.impl();
|
|
412
|
+
options.forceUpdateName = Locals.getRealPackageName(options.forceUpdateName);
|
|
413
|
+
options.skipUpdateName = Locals.getRealPackageName(options.skipUpdateName);
|
|
414
|
+
if (options.dryRun) Logger.log(chalk.magenta(`DRY RUN: `) + "No files will be modified.", options);
|
|
415
|
+
let inputs = [];
|
|
416
|
+
if (locals.workspace) inputs = locals.normalizePackageFolderNames;
|
|
417
|
+
else inputs = [""];
|
|
418
|
+
const instances = {};
|
|
419
|
+
await inputs.reduce((preProcess, packageFolderName) => {
|
|
420
|
+
preProcess = preProcess.then(() => release(packageFolderName, options).process()).then((instance) => {
|
|
421
|
+
instances[packageFolderName] = instance;
|
|
422
|
+
});
|
|
423
|
+
return preProcess;
|
|
424
|
+
}, Promise.resolve());
|
|
425
|
+
Logger.log(chalk.blue(`---------------------\n`));
|
|
426
|
+
let message = `chore(release): publish\n\n`;
|
|
427
|
+
const relationVerisons = {};
|
|
428
|
+
await inputs.reduce((preProcess, packageFolderName) => {
|
|
429
|
+
const instance = instances[packageFolderName];
|
|
430
|
+
instance.packageRelation.forEach((i) => {
|
|
431
|
+
const instance$ = instances[Locals.getPackageFolderName(i)];
|
|
432
|
+
if (instance$.commits.length > 0) instance.updateCommits(instance$.commits, instance$.packageName);
|
|
433
|
+
});
|
|
434
|
+
if (instance.commits.length) preProcess = preProcess.then(() => Logger.log(chalk.magenta(`CHANGED: `) + instance.packageName)).then(() => instance.test()).then(() => instance.build()).then(() => instance.updatePackageOptions(relationVerisons)).then(() => instance.updateChangelog()).then(() => {
|
|
435
|
+
message += `- ${instance.packageName}@${instance.packageOptions.version}\n`;
|
|
436
|
+
relationVerisons[instance.packageName] = `^${instance.packageOptions.version}`;
|
|
437
|
+
});
|
|
438
|
+
return preProcess;
|
|
439
|
+
}, Promise.resolve());
|
|
440
|
+
Logger.log(chalk.blue(`\n---------------------\n`));
|
|
441
|
+
const isChanged = Object.keys(relationVerisons).length;
|
|
442
|
+
if (!isChanged) Logger.log(chalk.magenta(`COMMIT: `) + "Nothing Chanaged Found.");
|
|
443
|
+
else if (!options.commit) Logger.log(chalk.magenta(`COMMIT: `) + "Disabled.");
|
|
444
|
+
else if (options.dryRun) Logger.log(chalk.magenta(`COMMIT: `) + chalk.yellow(`Skipping Git Commit`) + `\n${message}`);
|
|
445
|
+
else {
|
|
446
|
+
Logger.log(chalk.magenta(`CHANGED: `) + `pnpm-lock.yaml`);
|
|
447
|
+
await Shell.spawn("npx", [
|
|
448
|
+
"pnpm",
|
|
449
|
+
"install",
|
|
450
|
+
"--lockfile-only"
|
|
451
|
+
]);
|
|
452
|
+
Logger.log(chalk.magenta(`COMMIT: `) + `CHANGELOG.md, package.json, pnpm-lock.yaml`);
|
|
453
|
+
await Shell.spawn("git", ["add", process.cwd()]);
|
|
454
|
+
await Shell.spawn("git", [
|
|
455
|
+
"commit",
|
|
456
|
+
"--m",
|
|
457
|
+
`'${message}'`
|
|
458
|
+
]);
|
|
459
|
+
}
|
|
460
|
+
if ((options.keepLastTag || options.push) && !options.dryRun) {
|
|
461
|
+
Logger.log(chalk.yellow("Git Fetch..."));
|
|
462
|
+
await Shell.spawn("git", [
|
|
463
|
+
"fetch",
|
|
464
|
+
"--prune",
|
|
465
|
+
"--prune-tags"
|
|
466
|
+
]);
|
|
467
|
+
}
|
|
468
|
+
Logger.log(chalk.blue(`\n---------------------\n`));
|
|
469
|
+
await inputs.reduce((preProcess, packageFolderName) => {
|
|
470
|
+
const instance = instances[packageFolderName];
|
|
471
|
+
preProcess = preProcess.then(() => instance.publish()).then(() => instance.tag());
|
|
472
|
+
return preProcess;
|
|
473
|
+
}, Promise.resolve());
|
|
474
|
+
Logger.log(chalk.blue(`\n---------------------\n`));
|
|
475
|
+
if (!isChanged) Logger.log(chalk.magenta(`PUSH: `) + "Nothing Chanaged.");
|
|
476
|
+
else if (!options.push) Logger.log(chalk.magenta(`PUSH: `) + "Push Disabled.");
|
|
477
|
+
else if (options.dryRun) Logger.log(chalk.magenta(`PUSH: `) + "Skipping Git Push");
|
|
478
|
+
else {
|
|
479
|
+
Logger.log(chalk.yellow("Git Pull/Push..."));
|
|
480
|
+
await Shell.spawn("git", ["pull", "--rebase"]);
|
|
481
|
+
await Shell.spawn("git", ["push"]);
|
|
482
|
+
await Shell.spawn("git", ["push", "--tags"]);
|
|
483
|
+
}
|
|
484
|
+
Logger.log(chalk.blue(`\n---------------------\n`));
|
|
485
|
+
await inputs.reduce((preProcess, packageFolderName) => {
|
|
486
|
+
const instance = instances[packageFolderName];
|
|
487
|
+
preProcess = preProcess.then(() => instance.cleanTagsAndKeepLastTag());
|
|
488
|
+
return preProcess;
|
|
489
|
+
}, Promise.resolve());
|
|
490
|
+
Logger.log(chalk.magenta(`FINISH: `) + chalk.green(`Release Successed.`));
|
|
491
|
+
if (options.dryRun) Logger.log(chalk.green("NO DRY RUN WAY: ") + chalk.grey(`npm run release -- --no-dry-run\n`));
|
|
492
|
+
}, { onError: (e) => {
|
|
493
|
+
if (typeof e === "number" && e === 1) Logger.error("发布失败");
|
|
494
|
+
else Logger.error(e);
|
|
495
|
+
Logger.log(chalk.magenta(`FINISH: `) + chalk.red(`Release Failed.`));
|
|
496
|
+
process.exit(1);
|
|
497
|
+
} });
|
|
498
|
+
//#endregion
|
|
574
499
|
export { run };
|