@ruan-cat/utils 4.22.0 → 4.24.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-GY4LBTTR.js +247 -0
- package/dist/cli/chunk-GY4LBTTR.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 +68 -1
- package/dist/node-esm/index.js +262 -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 +319 -0
- package/src/node-esm/scripts/relizy-runner/index.ts +395 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/node-esm/scripts/relizy-runner/index.ts
|
|
4
|
+
import { execFileSync, spawnSync } from "child_process";
|
|
5
|
+
import { existsSync, readdirSync, readFileSync } from "fs";
|
|
6
|
+
import { dirname, join, resolve } from "path";
|
|
7
|
+
import process from "process";
|
|
8
|
+
import { fileURLToPath } from "url";
|
|
9
|
+
import consola from "consola";
|
|
10
|
+
import { parsePnpmWorkspaceYaml } from "pnpm-workspace-yaml";
|
|
11
|
+
var WINDOWS_GNU_COMMANDS = ["grep", "head", "sed"];
|
|
12
|
+
function getWorkspacePackages(workspaceRoot) {
|
|
13
|
+
const root = workspaceRoot ?? process.cwd();
|
|
14
|
+
const yamlPath = resolve(root, "pnpm-workspace.yaml");
|
|
15
|
+
if (!existsSync(yamlPath)) {
|
|
16
|
+
consola.error("release:relizy\uFF1A\u672A\u5728\u5F53\u524D\u76EE\u5F55\u627E\u5230 pnpm-workspace.yaml\uFF0C\u8BF7\u4ECE\u4ED3\u5E93\u6839\u76EE\u5F55\u6267\u884C\u3002");
|
|
17
|
+
return [];
|
|
18
|
+
}
|
|
19
|
+
const globs = parsePnpmWorkspaceYaml(readFileSync(yamlPath, "utf8")).toJSON().packages ?? [];
|
|
20
|
+
const packages = [];
|
|
21
|
+
for (const pattern of globs) {
|
|
22
|
+
const parts = pattern.split("/");
|
|
23
|
+
if (parts.length !== 2 || parts[1] !== "*") {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const dir = resolve(root, parts[0]);
|
|
27
|
+
if (!existsSync(dir)) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const discovered = readdirSync(dir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => join(dir, entry.name, "package.json")).filter((pkgPath) => existsSync(pkgPath)).map((pkgPath) => {
|
|
31
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
32
|
+
return { name: pkg.name, version: pkg.version };
|
|
33
|
+
}).filter((pkg) => typeof pkg.name === "string" && typeof pkg.version === "string");
|
|
34
|
+
packages.push(...discovered);
|
|
35
|
+
}
|
|
36
|
+
return packages;
|
|
37
|
+
}
|
|
38
|
+
function runLookup(command, args, env = process.env) {
|
|
39
|
+
return spawnSync(command, args, {
|
|
40
|
+
cwd: process.cwd(),
|
|
41
|
+
env,
|
|
42
|
+
encoding: "utf8",
|
|
43
|
+
stdio: "pipe"
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function hasExecutable(command, env = process.env) {
|
|
47
|
+
const lookupCommand = process.platform === "win32" ? "where" : "which";
|
|
48
|
+
return runLookup(lookupCommand, [command], env).status === 0;
|
|
49
|
+
}
|
|
50
|
+
function listExecutableMatches(command) {
|
|
51
|
+
const lookupCommand = process.platform === "win32" ? "where" : "which";
|
|
52
|
+
const result = runLookup(lookupCommand, [command]);
|
|
53
|
+
if (result.status !== 0) {
|
|
54
|
+
return [];
|
|
55
|
+
}
|
|
56
|
+
return result.stdout.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
57
|
+
}
|
|
58
|
+
function resolveGitUsrBinPath() {
|
|
59
|
+
if (process.platform !== "win32") {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
63
|
+
for (const executablePath of [...listExecutableMatches("bash"), ...listExecutableMatches("git")]) {
|
|
64
|
+
const executableDir = dirname(executablePath);
|
|
65
|
+
candidates.add(resolve(executableDir, "..", "usr", "bin"));
|
|
66
|
+
candidates.add(resolve(executableDir, "usr", "bin"));
|
|
67
|
+
}
|
|
68
|
+
for (const candidate of candidates) {
|
|
69
|
+
const hasAllCommands = WINDOWS_GNU_COMMANDS.every((command) => existsSync(join(candidate, `${command}.exe`)));
|
|
70
|
+
if (hasAllCommands) {
|
|
71
|
+
return candidate;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function ensureRelizyShellEnv() {
|
|
77
|
+
if (process.platform !== "win32") {
|
|
78
|
+
return { ...process.env };
|
|
79
|
+
}
|
|
80
|
+
if (WINDOWS_GNU_COMMANDS.every((command) => hasExecutable(command))) {
|
|
81
|
+
return { ...process.env };
|
|
82
|
+
}
|
|
83
|
+
const gitUsrBinPath = resolveGitUsrBinPath();
|
|
84
|
+
if (!gitUsrBinPath) {
|
|
85
|
+
consola.error("[release:relizy] \u5728 Windows \u4E0A\u672A\u627E\u5230 relizy \u6240\u9700\u7684 GNU \u5DE5\u5177\uFF08grep / head / sed\uFF09\u3002");
|
|
86
|
+
consola.error("\u8BF7\u5148\u5B89\u88C5 Git for Windows\uFF0C\u6216\u5C06\u5176\u5B89\u88C5\u76EE\u5F55\u4E0B\u7684 usr\\bin \u52A0\u5165 PATH\u3002");
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
const env = {
|
|
90
|
+
...process.env,
|
|
91
|
+
PATH: `${gitUsrBinPath};${process.env.PATH ?? ""}`
|
|
92
|
+
};
|
|
93
|
+
if (!WINDOWS_GNU_COMMANDS.every((command) => hasExecutable(command, env))) {
|
|
94
|
+
consola.error("[release:relizy] \u5DF2\u5B9A\u4F4D\u5230 Git for Windows\uFF0C\u4F46 grep / head / sed \u4ECD\u4E0D\u53EF\u7528\u3002");
|
|
95
|
+
consola.error(`\u8BF7\u68C0\u67E5 PATH\uFF0C\u6216\u624B\u52A8\u786E\u8BA4\u8BE5\u76EE\u5F55\u662F\u5426\u5B58\u5728\u6240\u9700\u53EF\u6267\u884C\u6587\u4EF6\uFF1A${gitUsrBinPath}`);
|
|
96
|
+
process.exit(1);
|
|
97
|
+
}
|
|
98
|
+
consola.info(`[release:relizy] Windows \u4E0B\u5DF2\u8865\u9F50 GNU \u5DE5\u5177\u8DEF\u5F84\uFF1A${gitUsrBinPath}`);
|
|
99
|
+
return env;
|
|
100
|
+
}
|
|
101
|
+
function getPackageTags(packageName, env) {
|
|
102
|
+
const stdout = execFileSync("git", ["tag", "--list", `${packageName}@*`], {
|
|
103
|
+
cwd: process.cwd(),
|
|
104
|
+
env,
|
|
105
|
+
encoding: "utf8"
|
|
106
|
+
});
|
|
107
|
+
return stdout.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
108
|
+
}
|
|
109
|
+
function shouldCheckIndependentBootstrap(relizyArgs) {
|
|
110
|
+
const [command] = relizyArgs;
|
|
111
|
+
return command === "release" || command === "bump";
|
|
112
|
+
}
|
|
113
|
+
var RELIZY_SUBCOMMANDS_WITH_YES_PRESET = /* @__PURE__ */ new Set(["release", "bump"]);
|
|
114
|
+
function prepareRelizySpawnArgs(relizyArgs) {
|
|
115
|
+
const optOutYes = relizyArgs.includes("--no-yes");
|
|
116
|
+
const forward = relizyArgs.filter((arg) => arg !== "--no-yes");
|
|
117
|
+
const [command] = forward;
|
|
118
|
+
const shouldInjectYes = !optOutYes && command !== void 0 && RELIZY_SUBCOMMANDS_WITH_YES_PRESET.has(command) && !forward.includes("--yes");
|
|
119
|
+
return shouldInjectYes ? [...forward, "--yes"] : forward;
|
|
120
|
+
}
|
|
121
|
+
function getPackagesMissingBootstrapTags(env) {
|
|
122
|
+
return getWorkspacePackages().filter((pkg) => getPackageTags(pkg.name, env).length === 0);
|
|
123
|
+
}
|
|
124
|
+
function buildBootstrapInstructions(missingPackages) {
|
|
125
|
+
const tagCommands = missingPackages.map((pkg) => `git tag "${pkg.name}@${pkg.version}"`);
|
|
126
|
+
const pushArgs = missingPackages.map((pkg) => `"${pkg.name}@${pkg.version}"`).join(" ");
|
|
127
|
+
return [
|
|
128
|
+
"[release:relizy] \u68C0\u6D4B\u5230\u672C\u4ED3\u5E93\u5C1A\u672A\u4E3A\u4EE5\u4E0B\u5305\u5EFA\u7ACB\u57FA\u7EBF tag\uFF08independent \u6A21\u5F0F\u9996\u6B21\u53D1\u7248\u524D\u9700\u8981\uFF09\uFF1A",
|
|
129
|
+
...missingPackages.map((pkg) => `- ${pkg.name}@${pkg.version}`),
|
|
130
|
+
"",
|
|
131
|
+
"\u8BF7\u6309\u5F53\u524D package.json \u7248\u672C\u521B\u5EFA\u57FA\u7EBF tag\uFF0C\u5E76\u63A8\u9001\u5230\u8FDC\u7AEF\uFF1A",
|
|
132
|
+
...tagCommands,
|
|
133
|
+
`git push origin ${pushArgs}`
|
|
134
|
+
].join("\n");
|
|
135
|
+
}
|
|
136
|
+
function printBootstrapInstructions(missingPackages) {
|
|
137
|
+
consola.error(buildBootstrapInstructions(missingPackages));
|
|
138
|
+
}
|
|
139
|
+
function getRelizyRunnerHelpText() {
|
|
140
|
+
return [
|
|
141
|
+
"relizy-runner <relizy \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570>",
|
|
142
|
+
"",
|
|
143
|
+
"\u5728 relizy \u6267\u884C\u524D\u8865\u9F50 Windows GNU \u5DE5\u5177\u8DEF\u5F84\uFF0C\u5E76\u5728\u9996\u6B21 independent \u53D1\u7248\u524D",
|
|
144
|
+
"\u6821\u9A8C\u57FA\u7EBF tag\u3002\u4E0D\u6539\u53D8 relizy \u81EA\u8EAB\u7684\u53D1\u7248\u4E0E\u7248\u672C\u8BA1\u7B97\u903B\u8F91\u3002",
|
|
145
|
+
"",
|
|
146
|
+
"\u7528\u6CD5\uFF1A",
|
|
147
|
+
" relizy-runner release --no-publish --no-provider-release",
|
|
148
|
+
" relizy-runner changelog --dry-run",
|
|
149
|
+
" relizy-runner bump",
|
|
150
|
+
"",
|
|
151
|
+
"runner \u884C\u4E3A\uFF1A\u5BF9 release / bump \u9ED8\u8BA4\u5728\u672B\u5C3E\u8FFD\u52A0 --yes\uFF08\u8DF3\u8FC7\u4E0A\u6E38\u786E\u8BA4\uFF09\uFF1B",
|
|
152
|
+
" \u9700\u8981\u4EA4\u4E92\u786E\u8BA4\u65F6\u8BF7\u52A0\u4E0A --no-yes\uFF08\u4EC5 runner \u8BC6\u522B\uFF0C\u4E0D\u4F20\u7ED9 relizy\uFF09\u3002",
|
|
153
|
+
"",
|
|
154
|
+
"\u5E38\u7528\u53C2\u6570\uFF08\u8282\u9009\uFF0C\u7531 relizy \u5904\u7406\uFF1B\u9664 --no-yes \u5916 runner \u4EC5\u900F\u4F20\uFF09\uFF1A",
|
|
155
|
+
" --dry-run \u9884\u89C8\uFF0C\u4E0D\u5199\u6587\u4EF6\u3001\u4E0D\u6253 tag\u3001\u4E0D\u63D0\u4EA4",
|
|
156
|
+
" --no-push \u4E0D push \u5230\u8FDC\u7AEF",
|
|
157
|
+
" --no-publish \u4E0D\u6267\u884C npm publish",
|
|
158
|
+
" --no-provider-release \u4E0D\u5728 GitHub/GitLab \u521B\u5EFA Release",
|
|
159
|
+
" --no-commit \u4E0D\u521B\u5EFA\u63D0\u4EA4\u4E0E tag",
|
|
160
|
+
" --no-changelog \u4E0D\u751F\u6210 changelog \u6587\u4EF6",
|
|
161
|
+
" --no-verify \u63D0\u4EA4\u65F6\u8DF3\u8FC7 git hooks",
|
|
162
|
+
" --yes \u8DF3\u8FC7 relizy \u7684\u786E\u8BA4\u63D0\u793A\uFF08release/bump \u4E0B runner \u4E5F\u4F1A\u81EA\u52A8\u8FFD\u52A0\uFF09",
|
|
163
|
+
"",
|
|
164
|
+
"\u4EE5\u4E0A\u4EC5\u4E3A\u5E38\u7528\u53C2\u6570\u8282\u9009\uFF0C\u5B8C\u6574\u53C2\u6570\u8BF7\u67E5\u9605 relizy \u5305\u81EA\u8EAB\u6587\u6863\uFF1A",
|
|
165
|
+
" npx relizy --help",
|
|
166
|
+
" npx relizy release --help",
|
|
167
|
+
" npx relizy changelog --help",
|
|
168
|
+
"",
|
|
169
|
+
"\u793A\u4F8B\uFF1A",
|
|
170
|
+
" npx relizy-runner release --no-publish --no-provider-release",
|
|
171
|
+
" npx ruan-cat-utils relizy-runner release --dry-run",
|
|
172
|
+
"",
|
|
173
|
+
"\u9009\u9879\uFF1A",
|
|
174
|
+
" --no-yes \u5173\u95ED release/bump \u7684\u81EA\u52A8 --yes\uFF0C\u6062\u590D relizy \u4EA4\u4E92\u786E\u8BA4",
|
|
175
|
+
" -h, --help \u67E5\u770B\u5E2E\u52A9\u4FE1\u606F"
|
|
176
|
+
].join("\n");
|
|
177
|
+
}
|
|
178
|
+
function resolveRelizyEntrypoint() {
|
|
179
|
+
return resolve(process.cwd(), "node_modules", "relizy", "bin", "relizy.mjs");
|
|
180
|
+
}
|
|
181
|
+
function runRelizyRunner(relizyArgs) {
|
|
182
|
+
if (relizyArgs.length === 0) {
|
|
183
|
+
consola.error("\u7528\u6CD5\uFF1Arelizy-runner <relizy \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570>");
|
|
184
|
+
consola.error("\u793A\u4F8B\uFF1Arelizy-runner release --no-publish --no-provider-release");
|
|
185
|
+
return 1;
|
|
186
|
+
}
|
|
187
|
+
const spawnArgs = prepareRelizySpawnArgs(relizyArgs);
|
|
188
|
+
consola.start(`[release:relizy] \u6267\u884C\u547D\u4EE4\uFF1Arelizy ${spawnArgs.join(" ")}`);
|
|
189
|
+
const env = ensureRelizyShellEnv();
|
|
190
|
+
if (shouldCheckIndependentBootstrap(spawnArgs)) {
|
|
191
|
+
consola.info("[release:relizy] \u68C0\u67E5 independent \u57FA\u7EBF tag...");
|
|
192
|
+
const missingPackages = getPackagesMissingBootstrapTags(env);
|
|
193
|
+
if (missingPackages.length > 0) {
|
|
194
|
+
printBootstrapInstructions(missingPackages);
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
197
|
+
consola.success("[release:relizy] \u57FA\u7EBF tag \u68C0\u67E5\u901A\u8FC7\u3002");
|
|
198
|
+
}
|
|
199
|
+
const relizyEntrypoint = resolveRelizyEntrypoint();
|
|
200
|
+
if (!existsSync(relizyEntrypoint)) {
|
|
201
|
+
consola.error("\u672A\u5728 node_modules \u4E2D\u627E\u5230 relizy \u547D\u4EE4\u884C\u5165\u53E3\uFF0C\u8BF7\u5148\u6267\u884C pnpm install\u3002");
|
|
202
|
+
return 1;
|
|
203
|
+
}
|
|
204
|
+
consola.info(`[release:relizy] \u8C03\u7528 relizy \u5165\u53E3\uFF1A${relizyEntrypoint}`);
|
|
205
|
+
const result = spawnSync(process.execPath, [relizyEntrypoint, ...spawnArgs], {
|
|
206
|
+
cwd: process.cwd(),
|
|
207
|
+
env,
|
|
208
|
+
stdio: "inherit"
|
|
209
|
+
});
|
|
210
|
+
if (result.status === 0) {
|
|
211
|
+
consola.success("[release:relizy] relizy \u6267\u884C\u5B8C\u6BD5\u3002");
|
|
212
|
+
} else {
|
|
213
|
+
consola.error(`[release:relizy] relizy \u4EE5\u9000\u51FA\u7801 ${result.status} \u7ED3\u675F\u3002`);
|
|
214
|
+
}
|
|
215
|
+
return result.status ?? 1;
|
|
216
|
+
}
|
|
217
|
+
function parseRelizyRunnerCliArgs(args) {
|
|
218
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") {
|
|
219
|
+
return { help: true, relizyArgs: [] };
|
|
220
|
+
}
|
|
221
|
+
return { help: false, relizyArgs: args };
|
|
222
|
+
}
|
|
223
|
+
function runRelizyRunnerCli(args = process.argv.slice(2)) {
|
|
224
|
+
const parsed = parseRelizyRunnerCliArgs(args);
|
|
225
|
+
if (parsed.help) {
|
|
226
|
+
console.log(getRelizyRunnerHelpText());
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const exitCode = runRelizyRunner(parsed.relizyArgs);
|
|
230
|
+
process.exitCode = exitCode;
|
|
231
|
+
}
|
|
232
|
+
function isRunningAsCli() {
|
|
233
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
234
|
+
const entryPath = process.argv[1];
|
|
235
|
+
if (!entryPath) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
return resolve(entryPath) === currentFilePath;
|
|
239
|
+
}
|
|
240
|
+
if (isRunningAsCli()) {
|
|
241
|
+
runRelizyRunnerCli();
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export {
|
|
245
|
+
runRelizyRunnerCli
|
|
246
|
+
};
|
|
247
|
+
//# sourceMappingURL=chunk-GY4LBTTR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/node-esm/scripts/relizy-runner/index.ts"],"sourcesContent":["import { execFileSync, spawnSync } from \"node:child_process\";\nimport { existsSync, readdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { fileURLToPath } from \"node:url\";\nimport consola from \"consola\";\nimport { parsePnpmWorkspaceYaml } from \"pnpm-workspace-yaml\";\nimport type { PackageJson } from \"pkg-types\";\n\n/**\n * 本脚本将(经本节所述规整后的)参数转发给 relizy CLI。\n *\n * **`release` / `bump` 默认附带 `--yes`(内部预设)**:`relizy` 在应用版本计划前会交互询问\n * 「Do you want to proceed with these version updates?」。在终端、CI、`pnpm` 脚本中若\n * 未关闭该提示,进程会一直等待 stdin,看起来像“卡死”。`--yes` 对应上游选项\n * *Skip confirmation prompt about bumping packages*,与改发版算法无关。\n *\n * 若需在本地逐步人工确认,可传入 **runner 专用** 的 `--no-yes`:该参数不会转发给 relizy,\n * 且会关闭上述自动注入(仅对 `release` / `bump` 生效)。\n */\n\nconst WINDOWS_GNU_COMMANDS = [\"grep\", \"head\", \"sed\"] as const;\n\n/** 发版基线 tag 校验所需的最小字段,由 {@link PackageJson} 派生。 */\nexport type WorkspacePackageInfo = Required<Pick<PackageJson, \"name\" | \"version\">>;\n\n// ── 工作区包发现 ──────────────────────────────────────────────────────────────\n\n/**\n * 解析根目录 `pnpm-workspace.yaml` 并展开 glob 模式,\n * 收集所有含 `package.json` 的子包目录,返回其 name 与 version。\n *\n * 使用 [pnpm-workspace-yaml](https://github.com/antfu/pnpm-workspace-utils/tree/main/packages/pnpm-workspace-yaml)\n * 解析工作区清单,再用 `pkg-types` 的 `PackageJson` 约束子包字段。\n */\nexport function getWorkspacePackages(workspaceRoot?: string): WorkspacePackageInfo[] {\n\tconst root = workspaceRoot ?? process.cwd();\n\tconst yamlPath = resolve(root, \"pnpm-workspace.yaml\");\n\n\tif (!existsSync(yamlPath)) {\n\t\tconsola.error(\"release:relizy:未在当前目录找到 pnpm-workspace.yaml,请从仓库根目录执行。\");\n\t\treturn [];\n\t}\n\n\tconst globs = parsePnpmWorkspaceYaml(readFileSync(yamlPath, \"utf8\")).toJSON().packages ?? [];\n\tconst packages: WorkspacePackageInfo[] = [];\n\n\tfor (const pattern of globs) {\n\t\tconst parts = pattern.split(\"/\");\n\n\t\tif (parts.length !== 2 || parts[1] !== \"*\") {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst dir = resolve(root, parts[0]);\n\n\t\tif (!existsSync(dir)) {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst discovered = readdirSync(dir, { withFileTypes: true })\n\t\t\t.filter((entry) => entry.isDirectory())\n\t\t\t.map((entry) => join(dir, entry.name, \"package.json\"))\n\t\t\t.filter((pkgPath) => existsSync(pkgPath))\n\t\t\t.map((pkgPath) => {\n\t\t\t\tconst pkg = JSON.parse(readFileSync(pkgPath, \"utf8\")) as PackageJson;\n\n\t\t\t\treturn { name: pkg.name, version: pkg.version };\n\t\t\t})\n\t\t\t.filter((pkg): pkg is WorkspacePackageInfo => typeof pkg.name === \"string\" && typeof pkg.version === \"string\");\n\n\t\tpackages.push(...discovered);\n\t}\n\n\treturn packages;\n}\n\n// ── Windows GNU 工具兼容层 ────────────────────────────────────────────────────\n\nfunction runLookup(command: string, args: string[], env: NodeJS.ProcessEnv = process.env) {\n\treturn spawnSync(command, args, {\n\t\tcwd: process.cwd(),\n\t\tenv,\n\t\tencoding: \"utf8\",\n\t\tstdio: \"pipe\",\n\t});\n}\n\nfunction hasExecutable(command: string, env: NodeJS.ProcessEnv = process.env) {\n\tconst lookupCommand = process.platform === \"win32\" ? \"where\" : \"which\";\n\n\treturn runLookup(lookupCommand, [command], env).status === 0;\n}\n\nfunction listExecutableMatches(command: string) {\n\tconst lookupCommand = process.platform === \"win32\" ? \"where\" : \"which\";\n\tconst result = runLookup(lookupCommand, [command]);\n\n\tif (result.status !== 0) {\n\t\treturn [];\n\t}\n\n\treturn result.stdout\n\t\t.split(/\\r?\\n/u)\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n}\n\nfunction resolveGitUsrBinPath() {\n\tif (process.platform !== \"win32\") {\n\t\treturn null;\n\t}\n\n\tconst candidates = new Set<string>();\n\n\tfor (const executablePath of [...listExecutableMatches(\"bash\"), ...listExecutableMatches(\"git\")]) {\n\t\tconst executableDir = dirname(executablePath);\n\n\t\tcandidates.add(resolve(executableDir, \"..\", \"usr\", \"bin\"));\n\t\tcandidates.add(resolve(executableDir, \"usr\", \"bin\"));\n\t}\n\n\tfor (const candidate of candidates) {\n\t\tconst hasAllCommands = WINDOWS_GNU_COMMANDS.every((command) => existsSync(join(candidate, `${command}.exe`)));\n\n\t\tif (hasAllCommands) {\n\t\t\treturn candidate;\n\t\t}\n\t}\n\n\treturn null;\n}\n\n/**\n * 确保 relizy 所需的 GNU 工具(grep / head / sed)在 PATH 中可用。\n * Windows 下会自动补齐 Git for Windows 的 `usr\\bin` 路径。\n */\nexport function ensureRelizyShellEnv() {\n\tif (process.platform !== \"win32\") {\n\t\treturn { ...process.env };\n\t}\n\n\tif (WINDOWS_GNU_COMMANDS.every((command) => hasExecutable(command))) {\n\t\treturn { ...process.env };\n\t}\n\n\tconst gitUsrBinPath = resolveGitUsrBinPath();\n\n\tif (!gitUsrBinPath) {\n\t\tconsola.error(\"[release:relizy] 在 Windows 上未找到 relizy 所需的 GNU 工具(grep / head / sed)。\");\n\t\tconsola.error(\"请先安装 Git for Windows,或将其安装目录下的 usr\\\\bin 加入 PATH。\");\n\t\tprocess.exit(1);\n\t}\n\n\tconst env = {\n\t\t...process.env,\n\t\tPATH: `${gitUsrBinPath};${process.env.PATH ?? \"\"}`,\n\t};\n\n\tif (!WINDOWS_GNU_COMMANDS.every((command) => hasExecutable(command, env))) {\n\t\tconsola.error(\"[release:relizy] 已定位到 Git for Windows,但 grep / head / sed 仍不可用。\");\n\t\tconsola.error(`请检查 PATH,或手动确认该目录是否存在所需可执行文件:${gitUsrBinPath}`);\n\t\tprocess.exit(1);\n\t}\n\n\tconsola.info(`[release:relizy] Windows 下已补齐 GNU 工具路径:${gitUsrBinPath}`);\n\n\treturn env;\n}\n\n// ── independent 模式 baseline tag 检查 ───────────────────────────────────────\n\nfunction getPackageTags(packageName: string, env: NodeJS.ProcessEnv) {\n\tconst stdout = execFileSync(\"git\", [\"tag\", \"--list\", `${packageName}@*`], {\n\t\tcwd: process.cwd(),\n\t\tenv,\n\t\tencoding: \"utf8\",\n\t});\n\n\treturn stdout\n\t\t.split(/\\r?\\n/u)\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n}\n\n/**\n * 判断当前 relizy 子命令是否需要检查 independent 基线 tag。\n * 仅 `release` 与 `bump` 需要。\n */\nexport function shouldCheckIndependentBootstrap(relizyArgs: string[]) {\n\tconst [command] = relizyArgs;\n\n\treturn command === \"release\" || command === \"bump\";\n}\n\nconst RELIZY_SUBCOMMANDS_WITH_YES_PRESET = new Set([\"release\", \"bump\"]);\n\n/**\n * 规整即将交给 relizy 的参数:移除 runner 专用选项,并在适当时追加 `--yes`。\n *\n * - 对 `release` / `bump`:若未出现 `--yes` 且未要求 `--no-yes`,则在末尾追加 `--yes`。\n * - `--no-yes` 仅由 relizy-runner 识别,不会传递给 relizy。\n */\nexport function prepareRelizySpawnArgs(relizyArgs: string[]): string[] {\n\tconst optOutYes = relizyArgs.includes(\"--no-yes\");\n\tconst forward = relizyArgs.filter((arg) => arg !== \"--no-yes\");\n\tconst [command] = forward;\n\n\tconst shouldInjectYes =\n\t\t!optOutYes &&\n\t\tcommand !== undefined &&\n\t\tRELIZY_SUBCOMMANDS_WITH_YES_PRESET.has(command) &&\n\t\t!forward.includes(\"--yes\");\n\n\treturn shouldInjectYes ? [...forward, \"--yes\"] : forward;\n}\n\nfunction getPackagesMissingBootstrapTags(env: NodeJS.ProcessEnv) {\n\treturn getWorkspacePackages().filter((pkg) => getPackageTags(pkg.name, env).length === 0);\n}\n\n/**\n * 根据缺少基线 tag 的包列表,生成包含补打 tag 命令的提示文本。\n */\nexport function buildBootstrapInstructions(missingPackages: WorkspacePackageInfo[]) {\n\tconst tagCommands = missingPackages.map((pkg) => `git tag \"${pkg.name}@${pkg.version}\"`);\n\tconst pushArgs = missingPackages.map((pkg) => `\"${pkg.name}@${pkg.version}\"`).join(\" \");\n\n\treturn [\n\t\t\"[release:relizy] 检测到本仓库尚未为以下包建立基线 tag(independent 模式首次发版前需要):\",\n\t\t...missingPackages.map((pkg) => `- ${pkg.name}@${pkg.version}`),\n\t\t\"\",\n\t\t\"请按当前 package.json 版本创建基线 tag,并推送到远端:\",\n\t\t...tagCommands,\n\t\t`git push origin ${pushArgs}`,\n\t].join(\"\\n\");\n}\n\nfunction printBootstrapInstructions(missingPackages: WorkspacePackageInfo[]) {\n\tconsola.error(buildBootstrapInstructions(missingPackages));\n}\n\n// ── 帮助信息 ──────────────────────────────────────────────────────────────────\n\n/**\n * 获取 relizy-runner CLI 的帮助文本。\n */\nexport function getRelizyRunnerHelpText() {\n\treturn [\n\t\t\"relizy-runner <relizy 子命令与参数>\",\n\t\t\"\",\n\t\t\"在 relizy 执行前补齐 Windows GNU 工具路径,并在首次 independent 发版前\",\n\t\t\"校验基线 tag。不改变 relizy 自身的发版与版本计算逻辑。\",\n\t\t\"\",\n\t\t\"用法:\",\n\t\t\" relizy-runner release --no-publish --no-provider-release\",\n\t\t\" relizy-runner changelog --dry-run\",\n\t\t\" relizy-runner bump\",\n\t\t\"\",\n\t\t\"runner 行为:对 release / bump 默认在末尾追加 --yes(跳过上游确认);\",\n\t\t\" 需要交互确认时请加上 --no-yes(仅 runner 识别,不传给 relizy)。\",\n\t\t\"\",\n\t\t\"常用参数(节选,由 relizy 处理;除 --no-yes 外 runner 仅透传):\",\n\t\t\" --dry-run 预览,不写文件、不打 tag、不提交\",\n\t\t\" --no-push 不 push 到远端\",\n\t\t\" --no-publish 不执行 npm publish\",\n\t\t\" --no-provider-release 不在 GitHub/GitLab 创建 Release\",\n\t\t\" --no-commit 不创建提交与 tag\",\n\t\t\" --no-changelog 不生成 changelog 文件\",\n\t\t\" --no-verify 提交时跳过 git hooks\",\n\t\t\" --yes 跳过 relizy 的确认提示(release/bump 下 runner 也会自动追加)\",\n\t\t\"\",\n\t\t\"以上仅为常用参数节选,完整参数请查阅 relizy 包自身文档:\",\n\t\t\" npx relizy --help\",\n\t\t\" npx relizy release --help\",\n\t\t\" npx relizy changelog --help\",\n\t\t\"\",\n\t\t\"示例:\",\n\t\t\" npx relizy-runner release --no-publish --no-provider-release\",\n\t\t\" npx ruan-cat-utils relizy-runner release --dry-run\",\n\t\t\"\",\n\t\t\"选项:\",\n\t\t\" --no-yes 关闭 release/bump 的自动 --yes,恢复 relizy 交互确认\",\n\t\t\" -h, --help 查看帮助信息\",\n\t].join(\"\\n\");\n}\n\n// ── 主入口 ────────────────────────────────────────────────────────────────────\n\nfunction resolveRelizyEntrypoint() {\n\treturn resolve(process.cwd(), \"node_modules\", \"relizy\", \"bin\", \"relizy.mjs\");\n}\n\n/**\n * 执行 relizy-runner。\n *\n * @description\n * 在 relizy 执行前做两件事:\n * 1. Windows 下自动补齐 Git for Windows 的 `usr\\bin` 路径,避免 relizy 内部调用 `grep`/`head`/`sed` 失败。\n * 2. 在 `release`/`bump` 前校验 independent 基线 tag,缺失时打印补打命令并终止。\n *\n * @param relizyArgs - 透传给 relizy 的子命令与参数(会先经 {@link prepareRelizySpawnArgs} 规整)\n * @returns 退出码\n */\nexport function runRelizyRunner(relizyArgs: string[]) {\n\tif (relizyArgs.length === 0) {\n\t\tconsola.error(\"用法:relizy-runner <relizy 子命令与参数>\");\n\t\tconsola.error(\"示例:relizy-runner release --no-publish --no-provider-release\");\n\t\treturn 1;\n\t}\n\n\tconst spawnArgs = prepareRelizySpawnArgs(relizyArgs);\n\n\tconsola.start(`[release:relizy] 执行命令:relizy ${spawnArgs.join(\" \")}`);\n\n\tconst env = ensureRelizyShellEnv();\n\n\tif (shouldCheckIndependentBootstrap(spawnArgs)) {\n\t\tconsola.info(\"[release:relizy] 检查 independent 基线 tag...\");\n\t\tconst missingPackages = getPackagesMissingBootstrapTags(env);\n\n\t\tif (missingPackages.length > 0) {\n\t\t\tprintBootstrapInstructions(missingPackages);\n\t\t\treturn 1;\n\t\t}\n\n\t\tconsola.success(\"[release:relizy] 基线 tag 检查通过。\");\n\t}\n\n\tconst relizyEntrypoint = resolveRelizyEntrypoint();\n\n\tif (!existsSync(relizyEntrypoint)) {\n\t\tconsola.error(\"未在 node_modules 中找到 relizy 命令行入口,请先执行 pnpm install。\");\n\t\treturn 1;\n\t}\n\n\tconsola.info(`[release:relizy] 调用 relizy 入口:${relizyEntrypoint}`);\n\n\tconst result = spawnSync(process.execPath, [relizyEntrypoint, ...spawnArgs], {\n\t\tcwd: process.cwd(),\n\t\tenv,\n\t\tstdio: \"inherit\",\n\t});\n\n\tif (result.status === 0) {\n\t\tconsola.success(\"[release:relizy] relizy 执行完毕。\");\n\t} else {\n\t\tconsola.error(`[release:relizy] relizy 以退出码 ${result.status} 结束。`);\n\t}\n\n\treturn result.status ?? 1;\n}\n\n/**\n * 解析 relizy-runner CLI 参数。\n * 如果首个参数是 `--help` 或 `-h`,返回 `{ help: true }`。\n * 否则将所有参数透传给 relizy。\n */\nexport function parseRelizyRunnerCliArgs(args: string[]): { help: boolean; relizyArgs: string[] } {\n\tif (args.length === 0 || args[0] === \"--help\" || args[0] === \"-h\") {\n\t\treturn { help: true, relizyArgs: [] };\n\t}\n\n\treturn { help: false, relizyArgs: args };\n}\n\n/**\n * 执行 relizy-runner CLI。\n */\nexport function runRelizyRunnerCli(args: string[] = process.argv.slice(2)) {\n\tconst parsed = parseRelizyRunnerCliArgs(args);\n\n\tif (parsed.help) {\n\t\tconsole.log(getRelizyRunnerHelpText());\n\t\treturn;\n\t}\n\n\tconst exitCode = runRelizyRunner(parsed.relizyArgs);\n\tprocess.exitCode = exitCode;\n}\n\nfunction isRunningAsCli() {\n\tconst currentFilePath = fileURLToPath(import.meta.url);\n\tconst entryPath = process.argv[1];\n\n\tif (!entryPath) {\n\t\treturn false;\n\t}\n\n\treturn resolve(entryPath) === currentFilePath;\n}\n\nif (isRunningAsCli()) {\n\trunRelizyRunnerCli();\n}\n"],"mappings":"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","names":[]}
|
package/dist/cli/index.js
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import {
|
|
3
3
|
runMoveVercelOutputToRootCli
|
|
4
4
|
} from "./chunk-46BA5ZMG.js";
|
|
5
|
+
import {
|
|
6
|
+
runRelizyRunnerCli
|
|
7
|
+
} from "./chunk-GY4LBTTR.js";
|
|
5
8
|
|
|
6
9
|
// src/cli/index.ts
|
|
7
10
|
import consola from "consola";
|
|
@@ -15,6 +18,7 @@ function printMainHelp() {
|
|
|
15
18
|
"",
|
|
16
19
|
"\u53EF\u7528\u547D\u4EE4\uFF1A",
|
|
17
20
|
" move-vercel-output-to-root \u5C06\u5B50\u5305\u7684 .vercel/output \u642C\u8FD0\u5230 monorepo \u6839\u76EE\u5F55",
|
|
21
|
+
" relizy-runner relizy \u53D1\u7248\u517C\u5BB9\u5C42\uFF08Windows GNU + \u57FA\u7EBF tag\uFF09",
|
|
18
22
|
"",
|
|
19
23
|
"\u5168\u5C40\u9009\u9879\uFF1A",
|
|
20
24
|
" -h, --help \u67E5\u770B\u5E2E\u52A9\u4FE1\u606F",
|
|
@@ -23,7 +27,8 @@ function printMainHelp() {
|
|
|
23
27
|
"\u793A\u4F8B\uFF1A",
|
|
24
28
|
` ${CLI_NAME} move-vercel-output-to-root`,
|
|
25
29
|
` ${CLI_NAME} move-vercel-output-to-root --dry-run`,
|
|
26
|
-
` ${CLI_NAME} move-vercel-output-to-root --root-dir
|
|
30
|
+
` ${CLI_NAME} move-vercel-output-to-root --root-dir ../../..`,
|
|
31
|
+
` ${CLI_NAME} relizy-runner release --no-publish --no-provider-release`
|
|
27
32
|
].join("\n");
|
|
28
33
|
console.log(helpText);
|
|
29
34
|
}
|
|
@@ -53,6 +58,11 @@ function main() {
|
|
|
53
58
|
}
|
|
54
59
|
break;
|
|
55
60
|
}
|
|
61
|
+
case "relizy-runner": {
|
|
62
|
+
const subArgs = args.slice(1);
|
|
63
|
+
runRelizyRunnerCli(subArgs);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
56
66
|
default: {
|
|
57
67
|
consola.error(`\u672A\u77E5\u547D\u4EE4\uFF1A${command}`);
|
|
58
68
|
console.log("");
|
package/dist/cli/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["/**\n * @ruan-cat/utils CLI 入口\n *\n * @description\n * 本文件是 @ruan-cat/utils 包的统一 CLI 入口。\n * 通过 package.json 的 bin 字段暴露为 `ruan-cat-utils` 命令。\n *\n * 用法:\n * npx ruan-cat-utils <command> [options]\n *\n * 支持的子命令:\n * move-vercel-output-to-root 将子包的 .vercel/output 搬运到 monorepo 根目录\n */\n\nimport consola from \"consola\";\nimport {\n\tgetMoveVercelOutputToRootHelpText,\n\trunMoveVercelOutputToRootCli,\n} from \"../node-esm/scripts/move-vercel-output-to-root/index\";\n\nconst CLI_NAME = \"ruan-cat-utils\";\n\nfunction printMainHelp() {\n\tconst helpText = [\n\t\t`${CLI_NAME} - @ruan-cat/utils 命令行工具`,\n\t\t\"\",\n\t\t\"用法:\",\n\t\t` ${CLI_NAME} <command> [options]`,\n\t\t\"\",\n\t\t\"可用命令:\",\n\t\t\" move-vercel-output-to-root 将子包的 .vercel/output 搬运到 monorepo 根目录\",\n\t\t\"\",\n\t\t\"全局选项:\",\n\t\t\" -h, --help 查看帮助信息\",\n\t\t\" -v, --version 查看版本号\",\n\t\t\"\",\n\t\t\"示例:\",\n\t\t` ${CLI_NAME} move-vercel-output-to-root`,\n\t\t` ${CLI_NAME} move-vercel-output-to-root --dry-run`,\n\t\t` ${CLI_NAME} move-vercel-output-to-root --root-dir ../../..`,\n\t].join(\"\\n\");\n\n\tconsole.log(helpText);\n}\n\nfunction printVersion() {\n\t// 动态读取版本号会引入额外复杂度,这里直接输出包名提示用户查看\n\tconsole.log(`${CLI_NAME} (from @ruan-cat/utils)`);\n\tconsole.log(\"运行 'npm list @ruan-cat/utils' 查看当前安装的版本。\");\n}\n\nfunction main() {\n\tconst args = process.argv.slice(2);\n\tconst command = args[0];\n\n\tif (!command || command === \"--help\" || command === \"-h\") {\n\t\tprintMainHelp();\n\t\treturn;\n\t}\n\n\tif (command === \"--version\" || command === \"-v\") {\n\t\tprintVersion();\n\t\treturn;\n\t}\n\n\tswitch (command) {\n\t\tcase \"move-vercel-output-to-root\": {\n\t\t\tconst subArgs = args.slice(1);\n\t\t\ttry {\n\t\t\t\trunMoveVercelOutputToRootCli(subArgs);\n\t\t\t} catch (error) {\n\t\t\t\tconsola.error(error instanceof Error ? error.message : String(error));\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault: {\n\t\t\tconsola.error(`未知命令:${command}`);\n\t\t\tconsole.log(\"\");\n\t\t\tprintMainHelp();\n\t\t\tprocess.exitCode = 1;\n\t\t}\n\t}\n}\n\nmain();\n"],"mappings":"
|
|
1
|
+
{"version":3,"sources":["../../src/cli/index.ts"],"sourcesContent":["/**\n * @ruan-cat/utils CLI 入口\n *\n * @description\n * 本文件是 @ruan-cat/utils 包的统一 CLI 入口。\n * 通过 package.json 的 bin 字段暴露为 `ruan-cat-utils` 命令。\n *\n * 用法:\n * npx ruan-cat-utils <command> [options]\n *\n * 支持的子命令:\n * move-vercel-output-to-root 将子包的 .vercel/output 搬运到 monorepo 根目录\n * relizy-runner relizy 发版兼容层(Windows GNU 工具补齐 + 基线 tag 检查)\n */\n\nimport consola from \"consola\";\nimport {\n\tgetMoveVercelOutputToRootHelpText,\n\trunMoveVercelOutputToRootCli,\n} from \"../node-esm/scripts/move-vercel-output-to-root/index\";\nimport { runRelizyRunnerCli } from \"../node-esm/scripts/relizy-runner/index\";\n\nconst CLI_NAME = \"ruan-cat-utils\";\n\nfunction printMainHelp() {\n\tconst helpText = [\n\t\t`${CLI_NAME} - @ruan-cat/utils 命令行工具`,\n\t\t\"\",\n\t\t\"用法:\",\n\t\t` ${CLI_NAME} <command> [options]`,\n\t\t\"\",\n\t\t\"可用命令:\",\n\t\t\" move-vercel-output-to-root 将子包的 .vercel/output 搬运到 monorepo 根目录\",\n\t\t\" relizy-runner relizy 发版兼容层(Windows GNU + 基线 tag)\",\n\t\t\"\",\n\t\t\"全局选项:\",\n\t\t\" -h, --help 查看帮助信息\",\n\t\t\" -v, --version 查看版本号\",\n\t\t\"\",\n\t\t\"示例:\",\n\t\t` ${CLI_NAME} move-vercel-output-to-root`,\n\t\t` ${CLI_NAME} move-vercel-output-to-root --dry-run`,\n\t\t` ${CLI_NAME} move-vercel-output-to-root --root-dir ../../..`,\n\t\t` ${CLI_NAME} relizy-runner release --no-publish --no-provider-release`,\n\t].join(\"\\n\");\n\n\tconsole.log(helpText);\n}\n\nfunction printVersion() {\n\t// 动态读取版本号会引入额外复杂度,这里直接输出包名提示用户查看\n\tconsole.log(`${CLI_NAME} (from @ruan-cat/utils)`);\n\tconsole.log(\"运行 'npm list @ruan-cat/utils' 查看当前安装的版本。\");\n}\n\nfunction main() {\n\tconst args = process.argv.slice(2);\n\tconst command = args[0];\n\n\tif (!command || command === \"--help\" || command === \"-h\") {\n\t\tprintMainHelp();\n\t\treturn;\n\t}\n\n\tif (command === \"--version\" || command === \"-v\") {\n\t\tprintVersion();\n\t\treturn;\n\t}\n\n\tswitch (command) {\n\t\tcase \"move-vercel-output-to-root\": {\n\t\t\tconst subArgs = args.slice(1);\n\t\t\ttry {\n\t\t\t\trunMoveVercelOutputToRootCli(subArgs);\n\t\t\t} catch (error) {\n\t\t\t\tconsola.error(error instanceof Error ? error.message : String(error));\n\t\t\t\tprocess.exitCode = 1;\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\n\t\tcase \"relizy-runner\": {\n\t\t\tconst subArgs = args.slice(1);\n\t\t\trunRelizyRunnerCli(subArgs);\n\t\t\tbreak;\n\t\t}\n\n\t\tdefault: {\n\t\t\tconsola.error(`未知命令:${command}`);\n\t\t\tconsole.log(\"\");\n\t\t\tprintMainHelp();\n\t\t\tprocess.exitCode = 1;\n\t\t}\n\t}\n}\n\nmain();\n"],"mappings":";;;;;;;;;AAeA,OAAO,aAAa;AAOpB,IAAM,WAAW;AAEjB,SAAS,gBAAgB;AACxB,QAAM,WAAW;AAAA,IAChB,GAAG,QAAQ;AAAA,IACX;AAAA,IACA;AAAA,IACA,KAAK,QAAQ;AAAA,IACb;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,QAAQ;AAAA,IACb,KAAK,QAAQ;AAAA,IACb,KAAK,QAAQ;AAAA,IACb,KAAK,QAAQ;AAAA,EACd,EAAE,KAAK,IAAI;AAEX,UAAQ,IAAI,QAAQ;AACrB;AAEA,SAAS,eAAe;AAEvB,UAAQ,IAAI,GAAG,QAAQ,yBAAyB;AAChD,UAAQ,IAAI,sGAA0C;AACvD;AAEA,SAAS,OAAO;AACf,QAAM,OAAO,QAAQ,KAAK,MAAM,CAAC;AACjC,QAAM,UAAU,KAAK,CAAC;AAEtB,MAAI,CAAC,WAAW,YAAY,YAAY,YAAY,MAAM;AACzD,kBAAc;AACd;AAAA,EACD;AAEA,MAAI,YAAY,eAAe,YAAY,MAAM;AAChD,iBAAa;AACb;AAAA,EACD;AAEA,UAAQ,SAAS;AAAA,IAChB,KAAK,8BAA8B;AAClC,YAAM,UAAU,KAAK,MAAM,CAAC;AAC5B,UAAI;AACH,qCAA6B,OAAO;AAAA,MACrC,SAAS,OAAO;AACf,gBAAQ,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AACpE,gBAAQ,WAAW;AAAA,MACpB;AACA;AAAA,IACD;AAAA,IAEA,KAAK,iBAAiB;AACrB,YAAM,UAAU,KAAK,MAAM,CAAC;AAC5B,yBAAmB,OAAO;AAC1B;AAAA,IACD;AAAA,IAEA,SAAS;AACR,cAAQ,MAAM,iCAAQ,OAAO,EAAE;AAC/B,cAAQ,IAAI,EAAE;AACd,oBAAc;AACd,cAAQ,WAAW;AAAA,IACpB;AAAA,EACD;AACD;AAEA,KAAK;","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/cli/relizy-runner.ts"],"sourcesContent":["/**\n * relizy-runner 快捷命令入口\n *\n * @description\n * 本文件是 `relizy-runner` bin 命令的直接入口。\n * 安装 @ruan-cat/utils 后,可以直接通过 `npx relizy-runner` 调用。\n */\n\nimport { runRelizyRunnerCli } from \"../node-esm/scripts/relizy-runner/index\";\n\nrunRelizyRunnerCli();\n"],"mappings":";;;;;;AAUA,mBAAmB;","names":[]}
|
package/dist/node-esm/index.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { PackageJson } from 'pkg-types';
|
|
2
|
+
|
|
1
3
|
/** 包的信息 */
|
|
2
4
|
interface PackageInfo {
|
|
3
5
|
/** 包名 */
|
|
@@ -107,6 +109,71 @@ declare function getMoveVercelOutputToRootHelpText(): string;
|
|
|
107
109
|
*/
|
|
108
110
|
declare function runMoveVercelOutputToRootCli(args?: string[]): void;
|
|
109
111
|
|
|
112
|
+
/** 发版基线 tag 校验所需的最小字段,由 {@link PackageJson} 派生。 */
|
|
113
|
+
type WorkspacePackageInfo = Required<Pick<PackageJson, "name" | "version">>;
|
|
114
|
+
/**
|
|
115
|
+
* 解析根目录 `pnpm-workspace.yaml` 并展开 glob 模式,
|
|
116
|
+
* 收集所有含 `package.json` 的子包目录,返回其 name 与 version。
|
|
117
|
+
*
|
|
118
|
+
* 使用 [pnpm-workspace-yaml](https://github.com/antfu/pnpm-workspace-utils/tree/main/packages/pnpm-workspace-yaml)
|
|
119
|
+
* 解析工作区清单,再用 `pkg-types` 的 `PackageJson` 约束子包字段。
|
|
120
|
+
*/
|
|
121
|
+
declare function getWorkspacePackages(workspaceRoot?: string): WorkspacePackageInfo[];
|
|
122
|
+
/**
|
|
123
|
+
* 确保 relizy 所需的 GNU 工具(grep / head / sed)在 PATH 中可用。
|
|
124
|
+
* Windows 下会自动补齐 Git for Windows 的 `usr\bin` 路径。
|
|
125
|
+
*/
|
|
126
|
+
declare function ensureRelizyShellEnv(): {
|
|
127
|
+
PATH: string;
|
|
128
|
+
} | {
|
|
129
|
+
[key: string]: string | undefined;
|
|
130
|
+
};
|
|
131
|
+
/**
|
|
132
|
+
* 判断当前 relizy 子命令是否需要检查 independent 基线 tag。
|
|
133
|
+
* 仅 `release` 与 `bump` 需要。
|
|
134
|
+
*/
|
|
135
|
+
declare function shouldCheckIndependentBootstrap(relizyArgs: string[]): boolean;
|
|
136
|
+
/**
|
|
137
|
+
* 规整即将交给 relizy 的参数:移除 runner 专用选项,并在适当时追加 `--yes`。
|
|
138
|
+
*
|
|
139
|
+
* - 对 `release` / `bump`:若未出现 `--yes` 且未要求 `--no-yes`,则在末尾追加 `--yes`。
|
|
140
|
+
* - `--no-yes` 仅由 relizy-runner 识别,不会传递给 relizy。
|
|
141
|
+
*/
|
|
142
|
+
declare function prepareRelizySpawnArgs(relizyArgs: string[]): string[];
|
|
143
|
+
/**
|
|
144
|
+
* 根据缺少基线 tag 的包列表,生成包含补打 tag 命令的提示文本。
|
|
145
|
+
*/
|
|
146
|
+
declare function buildBootstrapInstructions(missingPackages: WorkspacePackageInfo[]): string;
|
|
147
|
+
/**
|
|
148
|
+
* 获取 relizy-runner CLI 的帮助文本。
|
|
149
|
+
*/
|
|
150
|
+
declare function getRelizyRunnerHelpText(): string;
|
|
151
|
+
/**
|
|
152
|
+
* 执行 relizy-runner。
|
|
153
|
+
*
|
|
154
|
+
* @description
|
|
155
|
+
* 在 relizy 执行前做两件事:
|
|
156
|
+
* 1. Windows 下自动补齐 Git for Windows 的 `usr\bin` 路径,避免 relizy 内部调用 `grep`/`head`/`sed` 失败。
|
|
157
|
+
* 2. 在 `release`/`bump` 前校验 independent 基线 tag,缺失时打印补打命令并终止。
|
|
158
|
+
*
|
|
159
|
+
* @param relizyArgs - 透传给 relizy 的子命令与参数(会先经 {@link prepareRelizySpawnArgs} 规整)
|
|
160
|
+
* @returns 退出码
|
|
161
|
+
*/
|
|
162
|
+
declare function runRelizyRunner(relizyArgs: string[]): number;
|
|
163
|
+
/**
|
|
164
|
+
* 解析 relizy-runner CLI 参数。
|
|
165
|
+
* 如果首个参数是 `--help` 或 `-h`,返回 `{ help: true }`。
|
|
166
|
+
* 否则将所有参数透传给 relizy。
|
|
167
|
+
*/
|
|
168
|
+
declare function parseRelizyRunnerCliArgs(args: string[]): {
|
|
169
|
+
help: boolean;
|
|
170
|
+
relizyArgs: string[];
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* 执行 relizy-runner CLI。
|
|
174
|
+
*/
|
|
175
|
+
declare function runRelizyRunnerCli(args?: string[]): void;
|
|
176
|
+
|
|
110
177
|
interface WriteYaml2mdParams<T = Record<string, any>> {
|
|
111
178
|
/** 目标md文件地址 */
|
|
112
179
|
mdPath: string;
|
|
@@ -143,4 +210,4 @@ declare function findMonorepoRoot(startDir?: string): string | null;
|
|
|
143
210
|
*/
|
|
144
211
|
declare function isMonorepoProject(): boolean;
|
|
145
212
|
|
|
146
|
-
export { type MoveVercelOutputToRootOptions, type MoveVercelOutputToRootResult, type PackageInfo, type ResolvedMoveVercelOutputToRootOptions, type WriteYaml2mdParams, clean, defaultCleanTargets, findMonorepoRoot, getMoveVercelOutputToRootHelpText, getRuanCatPkgInfo, isMonorepoProject, moveVercelOutputToRoot, parseMoveVercelOutputToRootCliArgs, resolveMoveVercelOutputToRootOptions, runMoveVercelOutputToRootCli, writeYaml2md };
|
|
213
|
+
export { type MoveVercelOutputToRootOptions, type MoveVercelOutputToRootResult, type PackageInfo, type ResolvedMoveVercelOutputToRootOptions, type WorkspacePackageInfo, type WriteYaml2mdParams, buildBootstrapInstructions, clean, defaultCleanTargets, ensureRelizyShellEnv, findMonorepoRoot, getMoveVercelOutputToRootHelpText, getRelizyRunnerHelpText, getRuanCatPkgInfo, getWorkspacePackages, isMonorepoProject, moveVercelOutputToRoot, parseMoveVercelOutputToRootCliArgs, parseRelizyRunnerCliArgs, prepareRelizySpawnArgs, resolveMoveVercelOutputToRootOptions, runMoveVercelOutputToRootCli, runRelizyRunner, runRelizyRunnerCli, shouldCheckIndependentBootstrap, writeYaml2md };
|