@ruan-cat/utils 4.22.0 → 4.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/chunk-BPIJ4Z5J.js +233 -0
- package/dist/cli/chunk-BPIJ4Z5J.js.map +1 -0
- package/dist/cli/index.js +11 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/relizy-runner.js +8 -0
- package/dist/cli/relizy-runner.js.map +1 -0
- package/dist/node-esm/index.d.ts +61 -1
- package/dist/node-esm/index.js +247 -12
- package/dist/node-esm/index.js.map +1 -1
- package/package.json +4 -2
- package/src/cli/index.ts +10 -0
- package/src/cli/relizy-runner.ts +11 -0
- package/src/node-esm/index.ts +1 -0
- package/src/node-esm/scripts/relizy-runner/index.test.ts +281 -0
- package/src/node-esm/scripts/relizy-runner/index.ts +354 -0
package/package.json
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruan-cat/utils",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.23.0",
|
|
4
4
|
"description": "阮喵喵工具集合。默认提供js文件,也直接提供ts文件。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
7
7
|
"types": "./src/index.ts",
|
|
8
8
|
"bin": {
|
|
9
9
|
"ruan-cat-utils": "./dist/cli/index.js",
|
|
10
|
-
"move-vercel-output-to-root": "./dist/cli/move-vercel-output-to-root.js"
|
|
10
|
+
"move-vercel-output-to-root": "./dist/cli/move-vercel-output-to-root.js",
|
|
11
|
+
"relizy-runner": "./dist/cli/relizy-runner.js"
|
|
11
12
|
},
|
|
12
13
|
"homepage": "https://utils.ruancat6312.top",
|
|
13
14
|
"bugs": {
|
|
@@ -73,6 +74,7 @@
|
|
|
73
74
|
"axios": "^1.13.6",
|
|
74
75
|
"consola": "^3.4.2",
|
|
75
76
|
"lodash-es": "^4.17.23",
|
|
77
|
+
"pkg-types": "^2.3.0",
|
|
76
78
|
"pnpm-workspace-yaml": "^1.6.0",
|
|
77
79
|
"tinyglobby": "^0.2.15"
|
|
78
80
|
},
|
package/src/cli/index.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* 支持的子命令:
|
|
12
12
|
* move-vercel-output-to-root 将子包的 .vercel/output 搬运到 monorepo 根目录
|
|
13
|
+
* relizy-runner relizy 发版兼容层(Windows GNU 工具补齐 + 基线 tag 检查)
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import consola from "consola";
|
|
@@ -17,6 +18,7 @@ import {
|
|
|
17
18
|
getMoveVercelOutputToRootHelpText,
|
|
18
19
|
runMoveVercelOutputToRootCli,
|
|
19
20
|
} from "../node-esm/scripts/move-vercel-output-to-root/index";
|
|
21
|
+
import { runRelizyRunnerCli } from "../node-esm/scripts/relizy-runner/index";
|
|
20
22
|
|
|
21
23
|
const CLI_NAME = "ruan-cat-utils";
|
|
22
24
|
|
|
@@ -29,6 +31,7 @@ function printMainHelp() {
|
|
|
29
31
|
"",
|
|
30
32
|
"可用命令:",
|
|
31
33
|
" move-vercel-output-to-root 将子包的 .vercel/output 搬运到 monorepo 根目录",
|
|
34
|
+
" relizy-runner relizy 发版兼容层(Windows GNU + 基线 tag)",
|
|
32
35
|
"",
|
|
33
36
|
"全局选项:",
|
|
34
37
|
" -h, --help 查看帮助信息",
|
|
@@ -38,6 +41,7 @@ function printMainHelp() {
|
|
|
38
41
|
` ${CLI_NAME} move-vercel-output-to-root`,
|
|
39
42
|
` ${CLI_NAME} move-vercel-output-to-root --dry-run`,
|
|
40
43
|
` ${CLI_NAME} move-vercel-output-to-root --root-dir ../../..`,
|
|
44
|
+
` ${CLI_NAME} relizy-runner release --no-publish --no-provider-release`,
|
|
41
45
|
].join("\n");
|
|
42
46
|
|
|
43
47
|
console.log(helpText);
|
|
@@ -75,6 +79,12 @@ function main() {
|
|
|
75
79
|
break;
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
case "relizy-runner": {
|
|
83
|
+
const subArgs = args.slice(1);
|
|
84
|
+
runRelizyRunnerCli(subArgs);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
|
|
78
88
|
default: {
|
|
79
89
|
consola.error(`未知命令:${command}`);
|
|
80
90
|
console.log("");
|
package/src/node-esm/index.ts
CHANGED
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
buildBootstrapInstructions,
|
|
8
|
+
getRelizyRunnerHelpText,
|
|
9
|
+
getWorkspacePackages,
|
|
10
|
+
parseRelizyRunnerCliArgs,
|
|
11
|
+
shouldCheckIndependentBootstrap,
|
|
12
|
+
} from "./index";
|
|
13
|
+
|
|
14
|
+
function createWorkspaceFixture() {
|
|
15
|
+
const tempRoot = fs.mkdtempSync(path.join(tmpdir(), "relizy-runner-"));
|
|
16
|
+
|
|
17
|
+
fs.writeFileSync(path.join(tempRoot, "pnpm-workspace.yaml"), "packages:\n - apps/*\n - packages/*\n", "utf8");
|
|
18
|
+
|
|
19
|
+
const appsDir = path.join(tempRoot, "apps");
|
|
20
|
+
const packagesDir = path.join(tempRoot, "packages");
|
|
21
|
+
fs.mkdirSync(appsDir, { recursive: true });
|
|
22
|
+
fs.mkdirSync(packagesDir, { recursive: true });
|
|
23
|
+
|
|
24
|
+
const adminDir = path.join(appsDir, "admin");
|
|
25
|
+
fs.mkdirSync(adminDir, { recursive: true });
|
|
26
|
+
fs.writeFileSync(
|
|
27
|
+
path.join(adminDir, "package.json"),
|
|
28
|
+
JSON.stringify({ name: "@my-project/admin", version: "1.0.0" }),
|
|
29
|
+
"utf8",
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
const typeDir = path.join(appsDir, "type");
|
|
33
|
+
fs.mkdirSync(typeDir, { recursive: true });
|
|
34
|
+
fs.writeFileSync(
|
|
35
|
+
path.join(typeDir, "package.json"),
|
|
36
|
+
JSON.stringify({ name: "@my-project/type", version: "0.1.0" }),
|
|
37
|
+
"utf8",
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const utilsDir = path.join(packagesDir, "utils");
|
|
41
|
+
fs.mkdirSync(utilsDir, { recursive: true });
|
|
42
|
+
fs.writeFileSync(
|
|
43
|
+
path.join(utilsDir, "package.json"),
|
|
44
|
+
JSON.stringify({ name: "@my-project/utils", version: "2.0.0" }),
|
|
45
|
+
"utf8",
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return { tempRoot };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe("relizy-runner", () => {
|
|
52
|
+
const temporaryDirectories = new Set<string>();
|
|
53
|
+
|
|
54
|
+
beforeEach(() => {
|
|
55
|
+
temporaryDirectories.clear();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
afterEach(() => {
|
|
59
|
+
for (const tempDirectory of temporaryDirectories) {
|
|
60
|
+
fs.rmSync(tempDirectory, { force: true, recursive: true });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("shouldCheckIndependentBootstrap", () => {
|
|
65
|
+
test("仅 release 与 bump 需要检查基线 tag", () => {
|
|
66
|
+
expect(shouldCheckIndependentBootstrap(["release"])).toBe(true);
|
|
67
|
+
expect(shouldCheckIndependentBootstrap(["bump"])).toBe(true);
|
|
68
|
+
expect(shouldCheckIndependentBootstrap(["changelog"])).toBe(false);
|
|
69
|
+
expect(shouldCheckIndependentBootstrap(["version"])).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("空参数返回 false", () => {
|
|
73
|
+
expect(shouldCheckIndependentBootstrap([])).toBe(false);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("带额外参数时仍以第一个参数判断", () => {
|
|
77
|
+
expect(shouldCheckIndependentBootstrap(["release", "--dry-run", "--no-publish"])).toBe(true);
|
|
78
|
+
expect(shouldCheckIndependentBootstrap(["changelog", "--dry-run"])).toBe(false);
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe("buildBootstrapInstructions", () => {
|
|
83
|
+
test("缺少基线 tag 时生成明确提示", () => {
|
|
84
|
+
const instructions = buildBootstrapInstructions([
|
|
85
|
+
{ name: "@my-project/admin", version: "1.0.0" },
|
|
86
|
+
{ name: "@my-project/type", version: "0.1.0" },
|
|
87
|
+
]);
|
|
88
|
+
|
|
89
|
+
expect(instructions).toContain(
|
|
90
|
+
"[release:relizy] 检测到本仓库尚未为以下包建立基线 tag(independent 模式首次发版前需要):",
|
|
91
|
+
);
|
|
92
|
+
expect(instructions).toContain("- @my-project/admin@1.0.0");
|
|
93
|
+
expect(instructions).toContain("- @my-project/type@0.1.0");
|
|
94
|
+
expect(instructions).toContain('git tag "@my-project/admin@1.0.0"');
|
|
95
|
+
expect(instructions).toContain('git tag "@my-project/type@0.1.0"');
|
|
96
|
+
expect(instructions).toContain('git push origin "@my-project/admin@1.0.0" "@my-project/type@0.1.0"');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("单个包时仅生成一条 tag 命令", () => {
|
|
100
|
+
const instructions = buildBootstrapInstructions([{ name: "@my-project/admin", version: "2.0.0" }]);
|
|
101
|
+
|
|
102
|
+
expect(instructions).toContain('git tag "@my-project/admin@2.0.0"');
|
|
103
|
+
expect(instructions).toContain('git push origin "@my-project/admin@2.0.0"');
|
|
104
|
+
expect(instructions).not.toContain("@my-project/type");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("空数组时仍包含提示前缀但无 tag 命令", () => {
|
|
108
|
+
const instructions = buildBootstrapInstructions([]);
|
|
109
|
+
|
|
110
|
+
expect(instructions).toContain(
|
|
111
|
+
"[release:relizy] 检测到本仓库尚未为以下包建立基线 tag(independent 模式首次发版前需要):",
|
|
112
|
+
);
|
|
113
|
+
expect(instructions).not.toContain("git tag");
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
describe("getWorkspacePackages", () => {
|
|
118
|
+
test("从 pnpm-workspace.yaml 解析子包信息", () => {
|
|
119
|
+
const fixture = createWorkspaceFixture();
|
|
120
|
+
temporaryDirectories.add(fixture.tempRoot);
|
|
121
|
+
|
|
122
|
+
const packages = getWorkspacePackages(fixture.tempRoot);
|
|
123
|
+
|
|
124
|
+
expect(packages).toHaveLength(3);
|
|
125
|
+
|
|
126
|
+
const names = packages.map((pkg) => pkg.name);
|
|
127
|
+
expect(names).toContain("@my-project/admin");
|
|
128
|
+
expect(names).toContain("@my-project/type");
|
|
129
|
+
expect(names).toContain("@my-project/utils");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("不存在 pnpm-workspace.yaml 时返回空数组", () => {
|
|
133
|
+
const tempRoot = fs.mkdtempSync(path.join(tmpdir(), "relizy-runner-no-yaml-"));
|
|
134
|
+
temporaryDirectories.add(tempRoot);
|
|
135
|
+
|
|
136
|
+
const packages = getWorkspacePackages(tempRoot);
|
|
137
|
+
expect(packages).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("跳过没有 package.json 的子目录", () => {
|
|
141
|
+
const fixture = createWorkspaceFixture();
|
|
142
|
+
temporaryDirectories.add(fixture.tempRoot);
|
|
143
|
+
|
|
144
|
+
const emptyDir = path.join(fixture.tempRoot, "apps", "empty-app");
|
|
145
|
+
fs.mkdirSync(emptyDir, { recursive: true });
|
|
146
|
+
|
|
147
|
+
const packages = getWorkspacePackages(fixture.tempRoot);
|
|
148
|
+
expect(packages).toHaveLength(3);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("跳过缺少 name 或 version 的子包", () => {
|
|
152
|
+
const fixture = createWorkspaceFixture();
|
|
153
|
+
temporaryDirectories.add(fixture.tempRoot);
|
|
154
|
+
|
|
155
|
+
const noNameDir = path.join(fixture.tempRoot, "apps", "no-name");
|
|
156
|
+
fs.mkdirSync(noNameDir, { recursive: true });
|
|
157
|
+
fs.writeFileSync(path.join(noNameDir, "package.json"), JSON.stringify({ version: "1.0.0" }), "utf8");
|
|
158
|
+
|
|
159
|
+
const noVersionDir = path.join(fixture.tempRoot, "apps", "no-version");
|
|
160
|
+
fs.mkdirSync(noVersionDir, { recursive: true });
|
|
161
|
+
fs.writeFileSync(
|
|
162
|
+
path.join(noVersionDir, "package.json"),
|
|
163
|
+
JSON.stringify({ name: "@my-project/no-version" }),
|
|
164
|
+
"utf8",
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
const packages = getWorkspacePackages(fixture.tempRoot);
|
|
168
|
+
expect(packages).toHaveLength(3);
|
|
169
|
+
const names = packages.map((pkg) => pkg.name);
|
|
170
|
+
expect(names).not.toContain("@my-project/no-version");
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("跳过 ! 开头的排除模式和非标准 glob", () => {
|
|
174
|
+
const tempRoot = fs.mkdtempSync(path.join(tmpdir(), "relizy-runner-glob-"));
|
|
175
|
+
temporaryDirectories.add(tempRoot);
|
|
176
|
+
|
|
177
|
+
fs.writeFileSync(
|
|
178
|
+
path.join(tempRoot, "pnpm-workspace.yaml"),
|
|
179
|
+
"packages:\n - apps/*\n - '!apps/ignored'\n - deep/**/*\n",
|
|
180
|
+
"utf8",
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
const appsDir = path.join(tempRoot, "apps");
|
|
184
|
+
fs.mkdirSync(appsDir, { recursive: true });
|
|
185
|
+
|
|
186
|
+
const appDir = path.join(appsDir, "my-app");
|
|
187
|
+
fs.mkdirSync(appDir, { recursive: true });
|
|
188
|
+
fs.writeFileSync(
|
|
189
|
+
path.join(appDir, "package.json"),
|
|
190
|
+
JSON.stringify({ name: "@test/my-app", version: "1.0.0" }),
|
|
191
|
+
"utf8",
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
const deepDir = path.join(tempRoot, "deep", "nested", "pkg");
|
|
195
|
+
fs.mkdirSync(deepDir, { recursive: true });
|
|
196
|
+
fs.writeFileSync(
|
|
197
|
+
path.join(deepDir, "package.json"),
|
|
198
|
+
JSON.stringify({ name: "@test/deep-pkg", version: "0.1.0" }),
|
|
199
|
+
"utf8",
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
const packages = getWorkspacePackages(tempRoot);
|
|
203
|
+
|
|
204
|
+
const names = packages.map((pkg) => pkg.name);
|
|
205
|
+
expect(names).toContain("@test/my-app");
|
|
206
|
+
expect(names).not.toContain("@test/deep-pkg");
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test("workspace 目录不存在时静默跳过", () => {
|
|
210
|
+
const tempRoot = fs.mkdtempSync(path.join(tmpdir(), "relizy-runner-nodir-"));
|
|
211
|
+
temporaryDirectories.add(tempRoot);
|
|
212
|
+
|
|
213
|
+
fs.writeFileSync(path.join(tempRoot, "pnpm-workspace.yaml"), "packages:\n - nonexistent/*\n", "utf8");
|
|
214
|
+
|
|
215
|
+
const packages = getWorkspacePackages(tempRoot);
|
|
216
|
+
expect(packages).toEqual([]);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("parseRelizyRunnerCliArgs", () => {
|
|
221
|
+
test("无参数时返回 help", () => {
|
|
222
|
+
const result = parseRelizyRunnerCliArgs([]);
|
|
223
|
+
expect(result.help).toBe(true);
|
|
224
|
+
expect(result.relizyArgs).toEqual([]);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("--help 返回 help", () => {
|
|
228
|
+
const result = parseRelizyRunnerCliArgs(["--help"]);
|
|
229
|
+
expect(result.help).toBe(true);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("-h 返回 help", () => {
|
|
233
|
+
const result = parseRelizyRunnerCliArgs(["-h"]);
|
|
234
|
+
expect(result.help).toBe(true);
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("正常参数透传给 relizy", () => {
|
|
238
|
+
const result = parseRelizyRunnerCliArgs(["release", "--no-publish", "--no-provider-release"]);
|
|
239
|
+
expect(result.help).toBe(false);
|
|
240
|
+
expect(result.relizyArgs).toEqual(["release", "--no-publish", "--no-provider-release"]);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
test("changelog 子命令透传", () => {
|
|
244
|
+
const result = parseRelizyRunnerCliArgs(["changelog", "--dry-run"]);
|
|
245
|
+
expect(result.help).toBe(false);
|
|
246
|
+
expect(result.relizyArgs).toEqual(["changelog", "--dry-run"]);
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
test("bump 子命令与多参数透传", () => {
|
|
250
|
+
const result = parseRelizyRunnerCliArgs(["bump", "--patch", "--no-verify"]);
|
|
251
|
+
expect(result.help).toBe(false);
|
|
252
|
+
expect(result.relizyArgs).toEqual(["bump", "--patch", "--no-verify"]);
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("getRelizyRunnerHelpText", () => {
|
|
257
|
+
test("帮助文本包含关键用法信息", () => {
|
|
258
|
+
const helpText = getRelizyRunnerHelpText();
|
|
259
|
+
|
|
260
|
+
expect(helpText).toContain("relizy-runner");
|
|
261
|
+
expect(helpText).toContain("release");
|
|
262
|
+
expect(helpText).toContain("changelog");
|
|
263
|
+
expect(helpText).toContain("--dry-run");
|
|
264
|
+
expect(helpText).toContain("--no-publish");
|
|
265
|
+
expect(helpText).toContain("--no-provider-release");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test("帮助文本引导用户查阅 relizy 完整参数", () => {
|
|
269
|
+
const helpText = getRelizyRunnerHelpText();
|
|
270
|
+
|
|
271
|
+
expect(helpText).toContain("npx relizy --help");
|
|
272
|
+
expect(helpText).toContain("npx relizy release --help");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("帮助文本包含 --help 选项说明", () => {
|
|
276
|
+
const helpText = getRelizyRunnerHelpText();
|
|
277
|
+
|
|
278
|
+
expect(helpText).toContain("-h, --help");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|