@ruan-cat/vercel-deploy-tool 0.12.1 → 1.0.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/README.md +361 -75
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +523 -0
- package/dist/index.d.ts +254 -0
- package/dist/index.js +514 -0
- package/package.json +16 -8
- package/src/cli.ts +54 -0
- package/src/commands/deploy.ts +39 -0
- package/src/commands/init.ts +92 -0
- package/src/config/define-config.ts +20 -0
- package/src/config/loader.ts +85 -0
- package/src/config/schema.ts +108 -0
- package/src/core/executor.ts +50 -0
- package/src/core/tasks/after-build.ts +46 -0
- package/src/core/tasks/alias.ts +40 -0
- package/src/core/tasks/build.ts +43 -0
- package/src/core/tasks/copy-dist.ts +59 -0
- package/src/core/tasks/deploy.ts +46 -0
- package/src/core/tasks/index.ts +126 -0
- package/src/core/tasks/link.ts +42 -0
- package/src/core/tasks/user-commands.ts +31 -0
- package/src/core/vercel.ts +37 -0
- package/src/index.ts +68 -550
- package/src/templates/vercel-deploy-tool.config.ts +129 -0
- package/src/types/index.ts +14 -0
- package/src/utils/type-guards.ts +33 -0
- package/src/utils/vercel-null-config.ts +46 -0
- package/tsconfig.json +3 -1
package/dist/index.js
ADDED
|
@@ -0,0 +1,514 @@
|
|
|
1
|
+
// src/config/define-config.ts
|
|
2
|
+
function defineConfig(config2) {
|
|
3
|
+
return config2;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
// src/config/loader.ts
|
|
7
|
+
import { loadConfig as c12LoadConfig } from "c12";
|
|
8
|
+
import { resolve } from "pathe";
|
|
9
|
+
import { consola } from "consola";
|
|
10
|
+
import { isUndefined } from "lodash-es";
|
|
11
|
+
import { printFormat } from "@ruan-cat/utils";
|
|
12
|
+
var CONFIG_NAME = "vercel-deploy-tool";
|
|
13
|
+
var DEFAULT_CONFIG = {
|
|
14
|
+
vercelProjectName: "",
|
|
15
|
+
vercelToken: "",
|
|
16
|
+
vercelOrgId: "",
|
|
17
|
+
vercelProjectId: "",
|
|
18
|
+
deployTargets: []
|
|
19
|
+
};
|
|
20
|
+
async function loadConfig() {
|
|
21
|
+
const { config: config2 } = await c12LoadConfig({
|
|
22
|
+
cwd: resolve("."),
|
|
23
|
+
name: CONFIG_NAME,
|
|
24
|
+
dotenv: true,
|
|
25
|
+
defaults: DEFAULT_CONFIG
|
|
26
|
+
});
|
|
27
|
+
const vercelOrgId = process.env.VERCEL_ORG_ID;
|
|
28
|
+
const vercelProjectId = process.env.VERCEL_PROJECT_ID;
|
|
29
|
+
const vercelToken = process.env.VERCEL_TOKEN;
|
|
30
|
+
if (!isUndefined(vercelOrgId)) {
|
|
31
|
+
config2.vercelOrgId = vercelOrgId;
|
|
32
|
+
}
|
|
33
|
+
if (!isUndefined(vercelProjectId)) {
|
|
34
|
+
config2.vercelProjectId = vercelProjectId;
|
|
35
|
+
}
|
|
36
|
+
if (!isUndefined(vercelToken)) {
|
|
37
|
+
config2.vercelToken = vercelToken;
|
|
38
|
+
}
|
|
39
|
+
consola.success("\u914D\u7F6E\u52A0\u8F7D\u5B8C\u6210");
|
|
40
|
+
consola.box(printFormat(config2));
|
|
41
|
+
return config2;
|
|
42
|
+
}
|
|
43
|
+
var config = await loadConfig();
|
|
44
|
+
function getConfig() {
|
|
45
|
+
return config;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// src/core/tasks/index.ts
|
|
49
|
+
import fs from "fs";
|
|
50
|
+
import { consola as consola9 } from "consola";
|
|
51
|
+
|
|
52
|
+
// src/core/executor.ts
|
|
53
|
+
import task from "tasuku";
|
|
54
|
+
async function executeSequential(title, tasks) {
|
|
55
|
+
const results = [];
|
|
56
|
+
for (const t of tasks) {
|
|
57
|
+
const result = await task(t.name, t.fn);
|
|
58
|
+
results.push(result.result);
|
|
59
|
+
}
|
|
60
|
+
return results;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// src/utils/type-guards.ts
|
|
64
|
+
function isDeployTargetBase(target) {
|
|
65
|
+
return target.type === "static";
|
|
66
|
+
}
|
|
67
|
+
function isDeployTargetWithUserCommands(target) {
|
|
68
|
+
return target.type === "userCommands";
|
|
69
|
+
}
|
|
70
|
+
function getIsCopyDist(target) {
|
|
71
|
+
return target.isCopyDist ?? true;
|
|
72
|
+
}
|
|
73
|
+
function isNeedVercelBuild(target) {
|
|
74
|
+
return target.isNeedVercelBuild ?? true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// src/utils/vercel-null-config.ts
|
|
78
|
+
var VERCEL_NULL_CONFIG = {
|
|
79
|
+
framework: null,
|
|
80
|
+
buildCommand: null,
|
|
81
|
+
installCommand: null,
|
|
82
|
+
outputDirectory: null,
|
|
83
|
+
devCommand: null,
|
|
84
|
+
public: false,
|
|
85
|
+
/**
|
|
86
|
+
* 部署后提供干净的链接
|
|
87
|
+
* @see https://vercel.com/docs/projects/project-configuration#cleanurls
|
|
88
|
+
*
|
|
89
|
+
* @description
|
|
90
|
+
* 暂无效果
|
|
91
|
+
*
|
|
92
|
+
* 目前在 build-output-api 中,实现cleanUrls需要手动地写入配置文件
|
|
93
|
+
*
|
|
94
|
+
* 成本较大,目前不做投入。
|
|
95
|
+
*/
|
|
96
|
+
cleanUrls: true,
|
|
97
|
+
git: {
|
|
98
|
+
deploymentEnabled: {
|
|
99
|
+
main: false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
var VERCEL_NULL_CONFIG_PATH = "./vercel.null.def.json";
|
|
104
|
+
var VERCEL_OUTPUT_STATIC = ".vercel/output/static";
|
|
105
|
+
|
|
106
|
+
// src/core/tasks/link.ts
|
|
107
|
+
import { spawnSync } from "child_process";
|
|
108
|
+
import { concat } from "lodash-es";
|
|
109
|
+
import { consola as consola2 } from "consola";
|
|
110
|
+
|
|
111
|
+
// src/core/vercel.ts
|
|
112
|
+
function getVercelProjectNameArg(config2) {
|
|
113
|
+
return ["--name", config2.vercelProjectName];
|
|
114
|
+
}
|
|
115
|
+
function getVercelScopeArg(config2) {
|
|
116
|
+
return ["--scope", config2.vercelOrgId];
|
|
117
|
+
}
|
|
118
|
+
function getVercelTokenArg(config2) {
|
|
119
|
+
return ["--token", config2.vercelToken];
|
|
120
|
+
}
|
|
121
|
+
function getVercelLocalConfigArg() {
|
|
122
|
+
return ["--local-config", VERCEL_NULL_CONFIG_PATH];
|
|
123
|
+
}
|
|
124
|
+
function getTargetCWDArg(target) {
|
|
125
|
+
return ["--cwd", target.targetCWD];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// src/core/tasks/link.ts
|
|
129
|
+
function createLinkTask(config2, target) {
|
|
130
|
+
return {
|
|
131
|
+
name: `Link: ${target.targetCWD}`,
|
|
132
|
+
fn: async () => {
|
|
133
|
+
const args = concat(
|
|
134
|
+
["link"],
|
|
135
|
+
["--yes"],
|
|
136
|
+
getTargetCWDArg(target),
|
|
137
|
+
getVercelProjectNameArg(config2),
|
|
138
|
+
getVercelTokenArg(config2)
|
|
139
|
+
);
|
|
140
|
+
consola2.start(`\u5F00\u59CB link \u4EFB\u52A1: ${target.targetCWD}`);
|
|
141
|
+
const result = spawnSync("vercel", args, {
|
|
142
|
+
encoding: "utf-8",
|
|
143
|
+
stdio: "inherit"
|
|
144
|
+
});
|
|
145
|
+
if (result.error) {
|
|
146
|
+
consola2.error(`link \u4EFB\u52A1\u5931\u8D25: ${target.targetCWD}`);
|
|
147
|
+
throw result.error;
|
|
148
|
+
}
|
|
149
|
+
consola2.success(`\u5B8C\u6210 link \u4EFB\u52A1: ${target.targetCWD}`);
|
|
150
|
+
return result.stdout;
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// src/core/tasks/build.ts
|
|
156
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
157
|
+
import { concat as concat2 } from "lodash-es";
|
|
158
|
+
import { consola as consola3 } from "consola";
|
|
159
|
+
function createBuildTask(config2, target) {
|
|
160
|
+
return {
|
|
161
|
+
name: `Build: ${target.targetCWD}`,
|
|
162
|
+
fn: async () => {
|
|
163
|
+
const args = concat2(
|
|
164
|
+
["build"],
|
|
165
|
+
["--yes"],
|
|
166
|
+
["--prod"],
|
|
167
|
+
getTargetCWDArg(target),
|
|
168
|
+
getVercelLocalConfigArg(),
|
|
169
|
+
getVercelTokenArg(config2)
|
|
170
|
+
);
|
|
171
|
+
consola3.start(`\u5F00\u59CB build \u4EFB\u52A1: ${target.targetCWD}`);
|
|
172
|
+
const result = spawnSync2("vercel", args, {
|
|
173
|
+
encoding: "utf-8",
|
|
174
|
+
stdio: "inherit"
|
|
175
|
+
});
|
|
176
|
+
if (result.error) {
|
|
177
|
+
consola3.error(`build \u4EFB\u52A1\u5931\u8D25: ${target.targetCWD}`);
|
|
178
|
+
throw result.error;
|
|
179
|
+
}
|
|
180
|
+
consola3.success(`\u5B8C\u6210 build \u4EFB\u52A1: ${target.targetCWD}`);
|
|
181
|
+
return result.stdout;
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// src/core/tasks/after-build.ts
|
|
187
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
188
|
+
import { consola as consola4 } from "consola";
|
|
189
|
+
import { isUndefined as isUndefined2, isEmpty } from "lodash-es";
|
|
190
|
+
function createAfterBuildTasks(config2) {
|
|
191
|
+
const afterBuildTasks = config2.afterBuildTasks;
|
|
192
|
+
if (isUndefined2(afterBuildTasks) || isEmpty(afterBuildTasks)) {
|
|
193
|
+
return [
|
|
194
|
+
{
|
|
195
|
+
name: "AfterBuild: \u65E0\u4EFB\u52A1",
|
|
196
|
+
fn: async () => {
|
|
197
|
+
consola4.warn("\u5F53\u524D\u6CA1\u6709\u6709\u610F\u4E49\u7684 afterBuildTasks \u4EFB\u52A1\u914D\u7F6E");
|
|
198
|
+
return void 0;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
];
|
|
202
|
+
}
|
|
203
|
+
return afterBuildTasks.map((command) => ({
|
|
204
|
+
name: `AfterBuild: ${command}`,
|
|
205
|
+
fn: async () => {
|
|
206
|
+
consola4.start(`\u5F00\u59CB afterBuild \u4EFB\u52A1: ${command}`);
|
|
207
|
+
const result = spawnSync3(command, [], {
|
|
208
|
+
encoding: "utf-8",
|
|
209
|
+
stdio: "inherit",
|
|
210
|
+
shell: true
|
|
211
|
+
});
|
|
212
|
+
if (result.error) {
|
|
213
|
+
consola4.error(`afterBuild \u4EFB\u52A1\u5931\u8D25: ${command}`);
|
|
214
|
+
throw result.error;
|
|
215
|
+
}
|
|
216
|
+
consola4.success(`\u5B8C\u6210 afterBuild \u4EFB\u52A1: ${command}`);
|
|
217
|
+
return result.stdout;
|
|
218
|
+
}
|
|
219
|
+
}));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/core/tasks/user-commands.ts
|
|
223
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
224
|
+
import { consola as consola5 } from "consola";
|
|
225
|
+
function createUserCommandTasks(target) {
|
|
226
|
+
return target.userCommands.map((command) => ({
|
|
227
|
+
name: `UserCommand: ${command}`,
|
|
228
|
+
fn: async () => {
|
|
229
|
+
consola5.start(`\u5F00\u59CB\u7528\u6237\u547D\u4EE4\u4EFB\u52A1: ${command}`);
|
|
230
|
+
const result = spawnSync4(command, [], {
|
|
231
|
+
encoding: "utf-8",
|
|
232
|
+
stdio: "inherit",
|
|
233
|
+
shell: true
|
|
234
|
+
});
|
|
235
|
+
if (result.error) {
|
|
236
|
+
consola5.error(`\u7528\u6237\u547D\u4EE4\u4EFB\u52A1\u5931\u8D25: ${command}`);
|
|
237
|
+
throw result.error;
|
|
238
|
+
}
|
|
239
|
+
consola5.success(`\u5B8C\u6210\u7528\u6237\u547D\u4EE4\u4EFB\u52A1: ${command}`);
|
|
240
|
+
return result.stdout;
|
|
241
|
+
}
|
|
242
|
+
}));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// src/core/tasks/copy-dist.ts
|
|
246
|
+
import { resolve as resolve2 } from "path";
|
|
247
|
+
import { rmSync, mkdirSync, cpSync } from "fs";
|
|
248
|
+
import { consola as consola6 } from "consola";
|
|
249
|
+
function createCopyDistTasks(target) {
|
|
250
|
+
const targetCWD = target.targetCWD;
|
|
251
|
+
const outputDirectory = target.outputDirectory;
|
|
252
|
+
function joinPath(dir) {
|
|
253
|
+
return resolve2(process.cwd(), targetCWD, dir);
|
|
254
|
+
}
|
|
255
|
+
const pathVercelOutputStatic = joinPath(VERCEL_OUTPUT_STATIC);
|
|
256
|
+
const pathOutputDirectory = joinPath(outputDirectory);
|
|
257
|
+
return [
|
|
258
|
+
{
|
|
259
|
+
name: `\u5220\u9664\u76EE\u5F55: ${pathVercelOutputStatic}`,
|
|
260
|
+
fn: async () => {
|
|
261
|
+
consola6.start(`\u5F00\u59CB\u5220\u9664\u6587\u4EF6\u4EFB\u52A1: ${pathVercelOutputStatic}`);
|
|
262
|
+
rmSync(pathVercelOutputStatic, { recursive: true, force: true });
|
|
263
|
+
consola6.success(`\u5220\u9664\u8BE5\u8DEF\u5F84\u7684\u6587\u4EF6: ${pathVercelOutputStatic}`);
|
|
264
|
+
}
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: `\u521B\u5EFA\u76EE\u5F55: ${pathVercelOutputStatic}`,
|
|
268
|
+
fn: async () => {
|
|
269
|
+
consola6.start(`\u5F00\u59CB\u521B\u5EFA\u6587\u4EF6\u5939\u4EFB\u52A1: ${pathVercelOutputStatic}`);
|
|
270
|
+
mkdirSync(pathVercelOutputStatic, { recursive: true });
|
|
271
|
+
consola6.success(`\u521B\u5EFA\u7684\u65B0\u76EE\u5F55\u4E3A: ${pathVercelOutputStatic}`);
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
name: `\u590D\u5236\u6587\u4EF6: ${pathOutputDirectory} -> ${pathVercelOutputStatic}`,
|
|
276
|
+
fn: async () => {
|
|
277
|
+
consola6.start(`\u5F00\u59CB\u6587\u4EF6\u590D\u5236\u4EFB\u52A1`);
|
|
278
|
+
consola6.info(`\u4ECE ${pathOutputDirectory} \u5F00\u59CB`);
|
|
279
|
+
consola6.info(`\u590D\u5236\u5230 ${pathVercelOutputStatic} \u5185`);
|
|
280
|
+
cpSync(pathOutputDirectory, pathVercelOutputStatic, { recursive: true });
|
|
281
|
+
consola6.success(`\u5B8C\u6210\u6587\u4EF6\u590D\u5236\u4EFB\u52A1`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// src/core/tasks/deploy.ts
|
|
288
|
+
import { spawnSync as spawnSync5 } from "child_process";
|
|
289
|
+
import { concat as concat3 } from "lodash-es";
|
|
290
|
+
import { consola as consola7 } from "consola";
|
|
291
|
+
function createDeployTask(config2, target) {
|
|
292
|
+
return {
|
|
293
|
+
name: `Deploy: ${target.targetCWD}`,
|
|
294
|
+
fn: async () => {
|
|
295
|
+
const args = concat3(
|
|
296
|
+
["deploy"],
|
|
297
|
+
["--yes"],
|
|
298
|
+
["--prebuilt"],
|
|
299
|
+
["--prod"],
|
|
300
|
+
getTargetCWDArg(target),
|
|
301
|
+
getVercelTokenArg(config2)
|
|
302
|
+
);
|
|
303
|
+
consola7.start(`\u5F00\u59CB\u90E8\u7F72\u4EFB\u52A1: ${target.targetCWD}`);
|
|
304
|
+
const result = spawnSync5("vercel", args, {
|
|
305
|
+
encoding: "utf-8"
|
|
306
|
+
});
|
|
307
|
+
if (result.error) {
|
|
308
|
+
consola7.error(`\u90E8\u7F72\u5931\u8D25\u4E86: ${target.targetCWD}`);
|
|
309
|
+
consola7.error(result.error);
|
|
310
|
+
throw result.error;
|
|
311
|
+
}
|
|
312
|
+
const vercelUrl = result.stdout.toString().trim();
|
|
313
|
+
consola7.success(`\u5B8C\u6210\u90E8\u7F72\u4EFB\u52A1\uFF0C\u751F\u6210\u7684url\u4E3A:`);
|
|
314
|
+
consola7.box(vercelUrl);
|
|
315
|
+
return vercelUrl;
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/core/tasks/alias.ts
|
|
321
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
322
|
+
import { concat as concat4 } from "lodash-es";
|
|
323
|
+
import { consola as consola8 } from "consola";
|
|
324
|
+
function createAliasTask(config2, vercelUrl, userUrl) {
|
|
325
|
+
return {
|
|
326
|
+
name: `Alias: ${userUrl}`,
|
|
327
|
+
fn: async () => {
|
|
328
|
+
const args = concat4(["alias", "set", vercelUrl, userUrl], getVercelTokenArg(config2), getVercelScopeArg(config2));
|
|
329
|
+
consola8.start(`\u5F00\u59CB\u522B\u540D\u4EFB\u52A1: ${userUrl}`);
|
|
330
|
+
const result = spawnSync6("vercel", args, {
|
|
331
|
+
encoding: "utf-8",
|
|
332
|
+
stdio: "inherit"
|
|
333
|
+
});
|
|
334
|
+
if (result.error) {
|
|
335
|
+
consola8.error(`\u522B\u540D\u4EFB\u52A1\u5931\u8D25: ${userUrl}`);
|
|
336
|
+
throw result.error;
|
|
337
|
+
}
|
|
338
|
+
consola8.success(`\u5B8C\u6210\u522B\u540D\u4EFB\u52A1\uFF0C\u53EF\u7528\u7684\u522B\u540D\u5730\u5740\u4E3A:`);
|
|
339
|
+
consola8.box(`https://${userUrl}`);
|
|
340
|
+
return result.stdout;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// src/core/tasks/index.ts
|
|
346
|
+
async function generateVercelNullConfig() {
|
|
347
|
+
fs.writeFileSync(VERCEL_NULL_CONFIG_PATH, JSON.stringify(VERCEL_NULL_CONFIG, null, 2));
|
|
348
|
+
consola9.success(`\u751F\u6210 Vercel \u7A7A\u914D\u7F6E\u6587\u4EF6: ${VERCEL_NULL_CONFIG_PATH}`);
|
|
349
|
+
}
|
|
350
|
+
async function executeDeploymentWorkflow(config2) {
|
|
351
|
+
await generateVercelNullConfig();
|
|
352
|
+
const { deployTargets } = config2;
|
|
353
|
+
await task("Vercel \u90E8\u7F72\u5DE5\u4F5C\u6D41", async ({ task: task2 }) => {
|
|
354
|
+
await task2("1. Link \u9879\u76EE", async () => {
|
|
355
|
+
const linkTasks = deployTargets.map((target) => createLinkTask(config2, target));
|
|
356
|
+
await task2.group((task3) => linkTasks.map((t) => task3(t.name, t.fn)));
|
|
357
|
+
});
|
|
358
|
+
await task2("2. \u6784\u5EFA\u9879\u76EE", async () => {
|
|
359
|
+
const buildTasks = deployTargets.filter(isNeedVercelBuild).map((target) => createBuildTask(config2, target));
|
|
360
|
+
if (buildTasks.length === 0) {
|
|
361
|
+
consola9.warn("\u6CA1\u6709\u9700\u8981\u6267\u884C build \u7684\u76EE\u6807");
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
await task2.group((task3) => buildTasks.map((t) => task3(t.name, t.fn)));
|
|
365
|
+
});
|
|
366
|
+
await task2("3. \u6267\u884C AfterBuild \u4EFB\u52A1", async () => {
|
|
367
|
+
const afterBuildTasks = createAfterBuildTasks(config2);
|
|
368
|
+
await executeSequential("AfterBuild", afterBuildTasks);
|
|
369
|
+
});
|
|
370
|
+
await task2("4. \u6267\u884C\u7528\u6237\u547D\u4EE4\u4E0E\u6587\u4EF6\u590D\u5236", async () => {
|
|
371
|
+
const targetTasks = deployTargets.map((target) => ({
|
|
372
|
+
name: `\u5904\u7406\u76EE\u6807: ${target.targetCWD}`,
|
|
373
|
+
fn: async () => {
|
|
374
|
+
if (!isDeployTargetWithUserCommands(target)) {
|
|
375
|
+
consola9.warn(`\u76EE\u6807 ${target.targetCWD} \u4E0D\u5C5E\u4E8E userCommands \u7C7B\u578B`);
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
const userCommandTasks = createUserCommandTasks(target);
|
|
379
|
+
await executeSequential(`UserCommands: ${target.targetCWD}`, userCommandTasks);
|
|
380
|
+
if (getIsCopyDist(target)) {
|
|
381
|
+
const copyDistTasks = createCopyDistTasks(target);
|
|
382
|
+
await executeSequential(`CopyDist: ${target.targetCWD}`, copyDistTasks);
|
|
383
|
+
} else {
|
|
384
|
+
consola9.warn(`\u76EE\u6807 ${target.targetCWD} \u4E0D\u9700\u8981\u590D\u5236\u6587\u4EF6`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}));
|
|
388
|
+
await task2.group((task3) => targetTasks.map((t) => task3(t.name, t.fn)));
|
|
389
|
+
});
|
|
390
|
+
await task2("5. \u90E8\u7F72\u4E0E\u8BBE\u7F6E\u522B\u540D", async () => {
|
|
391
|
+
const deployAliasTasks = deployTargets.map((target) => ({
|
|
392
|
+
name: `\u90E8\u7F72\u4E0E\u522B\u540D: ${target.targetCWD}`,
|
|
393
|
+
fn: async () => {
|
|
394
|
+
const deployTask = createDeployTask(config2, target);
|
|
395
|
+
const deployResult = await task2(deployTask.name, deployTask.fn);
|
|
396
|
+
const vercelUrl = deployResult.result;
|
|
397
|
+
if (target.url && target.url.length > 0) {
|
|
398
|
+
const aliasTasks = target.url.map((userUrl) => createAliasTask(config2, vercelUrl, userUrl));
|
|
399
|
+
await task2.group((task3) => aliasTasks.map((t) => task3(t.name, t.fn)));
|
|
400
|
+
} else {
|
|
401
|
+
consola9.warn(`\u76EE\u6807 ${target.targetCWD} \u6CA1\u6709\u914D\u7F6E\u522B\u540D`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}));
|
|
405
|
+
await task2.group((task3) => deployAliasTasks.map((t) => task3(t.name, t.fn)));
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
consola9.success("\u{1F389} Vercel \u90E8\u7F72\u5DE5\u4F5C\u6D41\u5B8C\u6210\uFF01");
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// src/commands/deploy.ts
|
|
412
|
+
import { Command } from "commander";
|
|
413
|
+
import { consola as consola10 } from "consola";
|
|
414
|
+
function createDeployCommand() {
|
|
415
|
+
const command = new Command("deploy");
|
|
416
|
+
command.description("\u90E8\u7F72\u9879\u76EE\u5230 Vercel").action(async () => {
|
|
417
|
+
try {
|
|
418
|
+
consola10.start("\u5F00\u59CB\u52A0\u8F7D\u914D\u7F6E...");
|
|
419
|
+
const config2 = await loadConfig();
|
|
420
|
+
consola10.start("\u5F00\u59CB\u6267\u884C\u90E8\u7F72\u5DE5\u4F5C\u6D41...");
|
|
421
|
+
await executeDeploymentWorkflow(config2);
|
|
422
|
+
consola10.success("\u90E8\u7F72\u5B8C\u6210\uFF01");
|
|
423
|
+
} catch (error) {
|
|
424
|
+
consola10.error("\u90E8\u7F72\u5931\u8D25:");
|
|
425
|
+
consola10.error(error);
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
return command;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// src/commands/init.ts
|
|
433
|
+
import { Command as Command2 } from "commander";
|
|
434
|
+
import { readFileSync, writeFileSync, existsSync } from "fs";
|
|
435
|
+
import { join, dirname } from "path";
|
|
436
|
+
import { fileURLToPath } from "url";
|
|
437
|
+
import { consola as consola11 } from "consola";
|
|
438
|
+
var __filename2 = fileURLToPath(import.meta.url);
|
|
439
|
+
var __dirname2 = dirname(__filename2);
|
|
440
|
+
function createInitCommand() {
|
|
441
|
+
const command = new Command2("init");
|
|
442
|
+
command.description("\u521D\u59CB\u5316\u914D\u7F6E\u6587\u4EF6").option("-f, --force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u6587\u4EF6").action((options) => {
|
|
443
|
+
const cwd = process.cwd();
|
|
444
|
+
const configFile = "vercel-deploy-tool.config.ts";
|
|
445
|
+
const targetPath = join(cwd, configFile);
|
|
446
|
+
if (existsSync(targetPath) && !options.force) {
|
|
447
|
+
consola11.warn(`\u914D\u7F6E\u6587\u4EF6\u5DF2\u5B58\u5728: ${configFile}`);
|
|
448
|
+
consola11.info("\u4F7F\u7528 --force \u9009\u9879\u53EF\u4EE5\u8986\u76D6");
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
const templatePath = join(__dirname2, "..", "templates", configFile);
|
|
452
|
+
if (!existsSync(templatePath)) {
|
|
453
|
+
consola11.error(`\u6A21\u677F\u6587\u4EF6\u4E0D\u5B58\u5728: ${templatePath}`);
|
|
454
|
+
consola11.error("\u8BF7\u786E\u4FDD @ruan-cat/vercel-deploy-tool \u5305\u5DF2\u6B63\u786E\u5B89\u88C5");
|
|
455
|
+
process.exit(1);
|
|
456
|
+
}
|
|
457
|
+
const content = readFileSync(templatePath, "utf-8");
|
|
458
|
+
writeFileSync(targetPath, content, "utf-8");
|
|
459
|
+
consola11.success(`\u5DF2\u521B\u5EFA\u914D\u7F6E\u6587\u4EF6: ${configFile}`);
|
|
460
|
+
const pkgPath = join(cwd, "package.json");
|
|
461
|
+
if (existsSync(pkgPath)) {
|
|
462
|
+
try {
|
|
463
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
464
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
465
|
+
pkg.scripts["deploy-vercel"] = "vercel-deploy-tool deploy";
|
|
466
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf-8");
|
|
467
|
+
consola11.success('\u5DF2\u6DFB\u52A0\u811A\u672C: "deploy-vercel"');
|
|
468
|
+
} catch (error) {
|
|
469
|
+
consola11.warn("\u66F4\u65B0 package.json \u5931\u8D25:", error);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
consola11.box(`\u{1F389} \u521D\u59CB\u5316\u5B8C\u6210\uFF01
|
|
473
|
+
|
|
474
|
+
\u521B\u5EFA\u7684\u6587\u4EF6:
|
|
475
|
+
\u2022 ${configFile} - Vercel \u90E8\u7F72\u914D\u7F6E\u6587\u4EF6
|
|
476
|
+
|
|
477
|
+
\u6DFB\u52A0\u7684\u811A\u672C:
|
|
478
|
+
\u2022 deploy-vercel: vercel-deploy-tool deploy
|
|
479
|
+
|
|
480
|
+
\u4E0B\u4E00\u6B65:
|
|
481
|
+
1. \u7F16\u8F91 ${configFile} \u586B\u5199\u4F60\u7684\u914D\u7F6E
|
|
482
|
+
2. \u786E\u4FDD\u73AF\u5883\u53D8\u91CF\u5DF2\u8BBE\u7F6E:
|
|
483
|
+
- VERCEL_TOKEN
|
|
484
|
+
- VERCEL_ORG_ID
|
|
485
|
+
- VERCEL_PROJECT_ID
|
|
486
|
+
3. \u8FD0\u884C\u90E8\u7F72:
|
|
487
|
+
pnpm run deploy-vercel`);
|
|
488
|
+
});
|
|
489
|
+
return command;
|
|
490
|
+
}
|
|
491
|
+
export {
|
|
492
|
+
VERCEL_NULL_CONFIG,
|
|
493
|
+
VERCEL_NULL_CONFIG_PATH,
|
|
494
|
+
VERCEL_OUTPUT_STATIC,
|
|
495
|
+
config,
|
|
496
|
+
createDeployCommand,
|
|
497
|
+
createInitCommand,
|
|
498
|
+
defineConfig,
|
|
499
|
+
executeDeploymentWorkflow,
|
|
500
|
+
getConfig,
|
|
501
|
+
getIsCopyDist,
|
|
502
|
+
isDeployTargetBase,
|
|
503
|
+
isDeployTargetWithUserCommands,
|
|
504
|
+
isNeedVercelBuild,
|
|
505
|
+
loadConfig
|
|
506
|
+
};
|
|
507
|
+
/**
|
|
508
|
+
* @ruan-cat/vercel-deploy-tool
|
|
509
|
+
* @description
|
|
510
|
+
* Vercel 部署工具 - 支持 monorepo 的自动化部署
|
|
511
|
+
*
|
|
512
|
+
* @author ruan-cat
|
|
513
|
+
* @license MIT
|
|
514
|
+
*/
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruan-cat/vercel-deploy-tool",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "阮喵喵自用的vercel部署工具,用于实现复杂项目的部署。",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./
|
|
7
|
-
"types": "./
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
8
|
"homepage": "https://vercel-deploy-tool.ruancat6312.top",
|
|
9
9
|
"bugs": {
|
|
10
10
|
"url": "https://github.com/ruan-cat/monorepo/issues"
|
|
@@ -14,10 +14,15 @@
|
|
|
14
14
|
"url": "git+https://github.com/ruan-cat/monorepo.git",
|
|
15
15
|
"directory": "packages/vercel-deploy-tool"
|
|
16
16
|
},
|
|
17
|
+
"bin": {
|
|
18
|
+
"vercel-deploy-tool": "./dist/cli.js",
|
|
19
|
+
"vdt": "./dist/cli.js",
|
|
20
|
+
"@ruan-cat/vercel-deploy-tool": "./dist/cli.js"
|
|
21
|
+
},
|
|
17
22
|
"exports": {
|
|
18
23
|
".": {
|
|
19
|
-
"import": "./
|
|
20
|
-
"types": "./
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts"
|
|
21
26
|
},
|
|
22
27
|
"./src/*": "./src/*"
|
|
23
28
|
},
|
|
@@ -57,8 +62,9 @@
|
|
|
57
62
|
"lodash-es": "^4.17.21",
|
|
58
63
|
"pathe": "^2.0.3",
|
|
59
64
|
"rimraf": "^6.1.0",
|
|
65
|
+
"tasuku": "^2.0.4",
|
|
60
66
|
"vercel": "^41.7.8",
|
|
61
|
-
"@ruan-cat/utils": "^4.
|
|
67
|
+
"@ruan-cat/utils": "^4.18.0"
|
|
62
68
|
},
|
|
63
69
|
"devDependencies": {
|
|
64
70
|
"@types/gulp": "^4.0.18",
|
|
@@ -67,14 +73,16 @@
|
|
|
67
73
|
"automd": "^0.4.2",
|
|
68
74
|
"execa": "^9.6.0",
|
|
69
75
|
"vitepress": "^1.6.4",
|
|
70
|
-
"@ruan-cat/vitepress-preset-config": "^2.12.
|
|
76
|
+
"@ruan-cat/vitepress-preset-config": "^2.12.2"
|
|
71
77
|
},
|
|
72
78
|
"scripts": {
|
|
79
|
+
"build": "tsup",
|
|
73
80
|
"docs:dev": "vitepress dev src/docs",
|
|
74
81
|
"build:docs": "vitepress build src/docs",
|
|
75
82
|
"prebuild": "automd",
|
|
83
|
+
"test:cli": "node dist/cli.js deploy",
|
|
76
84
|
"test:prod": "node ./dist/index.js",
|
|
77
|
-
"test:dev": "tsx ./src/
|
|
85
|
+
"test:dev": "tsx ./src/cli.ts deploy",
|
|
78
86
|
"test:config": "dotenvx run -f .env.test -f .env.test-2 -- tsx ./tests/config.test.ts"
|
|
79
87
|
}
|
|
80
88
|
}
|
package/src/cli.ts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from "node:fs";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { createDeployCommand } from "./commands/deploy";
|
|
8
|
+
import { createInitCommand } from "./commands/init";
|
|
9
|
+
|
|
10
|
+
// 获取当前文件的目录
|
|
11
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
12
|
+
const __dirname = path.dirname(__filename);
|
|
13
|
+
|
|
14
|
+
// 读取 package.json 获取版本号
|
|
15
|
+
const packageJsonPath = path.join(__dirname, "..", "package.json");
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
17
|
+
const { version } = packageJson;
|
|
18
|
+
|
|
19
|
+
// 创建主程序
|
|
20
|
+
const program = new Command();
|
|
21
|
+
|
|
22
|
+
// 设置程序基本信息
|
|
23
|
+
program.name("vercel-deploy-tool").description("Vercel 部署工具 - 支持 monorepo 的自动化部署").version(version);
|
|
24
|
+
|
|
25
|
+
// 注册所有子命令
|
|
26
|
+
program.addCommand(createDeployCommand());
|
|
27
|
+
program.addCommand(createInitCommand());
|
|
28
|
+
|
|
29
|
+
// 自定义帮助信息
|
|
30
|
+
program.on("--help", () => {
|
|
31
|
+
console.log("");
|
|
32
|
+
console.log("使用示例 / Usage Examples:");
|
|
33
|
+
console.log("");
|
|
34
|
+
console.log(" # 初始化配置文件");
|
|
35
|
+
console.log(" vercel-deploy-tool init");
|
|
36
|
+
console.log(" vdt init");
|
|
37
|
+
console.log("");
|
|
38
|
+
console.log(" # 部署项目");
|
|
39
|
+
console.log(" vercel-deploy-tool deploy");
|
|
40
|
+
console.log(" vdt deploy");
|
|
41
|
+
console.log("");
|
|
42
|
+
console.log(" # 查看帮助");
|
|
43
|
+
console.log(" vercel-deploy-tool --help");
|
|
44
|
+
console.log(" vdt --help");
|
|
45
|
+
console.log("");
|
|
46
|
+
console.log("环境变量 / Environment Variables:");
|
|
47
|
+
console.log(" VERCEL_TOKEN - Vercel API Token");
|
|
48
|
+
console.log(" VERCEL_ORG_ID - Vercel 组织 ID");
|
|
49
|
+
console.log(" VERCEL_PROJECT_ID - Vercel 项目 ID");
|
|
50
|
+
console.log("");
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// 解析命令行参数
|
|
54
|
+
program.parse();
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { consola } from "consola";
|
|
3
|
+
import { loadConfig } from "../config/loader";
|
|
4
|
+
import { executeDeploymentWorkflow } from "../core/tasks";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 创建 deploy 命令
|
|
8
|
+
* @description
|
|
9
|
+
* 部署项目到 Vercel
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```bash
|
|
13
|
+
* vercel-deploy-tool deploy
|
|
14
|
+
* vdt deploy
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export function createDeployCommand(): Command {
|
|
18
|
+
const command = new Command("deploy");
|
|
19
|
+
|
|
20
|
+
command.description("部署项目到 Vercel").action(async () => {
|
|
21
|
+
try {
|
|
22
|
+
consola.start("开始加载配置...");
|
|
23
|
+
|
|
24
|
+
const config = await loadConfig();
|
|
25
|
+
|
|
26
|
+
consola.start("开始执行部署工作流...");
|
|
27
|
+
|
|
28
|
+
await executeDeploymentWorkflow(config);
|
|
29
|
+
|
|
30
|
+
consola.success("部署完成!");
|
|
31
|
+
} catch (error) {
|
|
32
|
+
consola.error("部署失败:");
|
|
33
|
+
consola.error(error);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
return command;
|
|
39
|
+
}
|