@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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { consola } from "consola";
|
|
3
|
+
import { task, executeSequential } from "../executor";
|
|
4
|
+
import type { VercelDeployConfig } from "../../config/schema";
|
|
5
|
+
import { isDeployTargetWithUserCommands, isNeedVercelBuild, getIsCopyDist } from "../../utils/type-guards";
|
|
6
|
+
import { VERCEL_NULL_CONFIG, VERCEL_NULL_CONFIG_PATH } from "../../utils/vercel-null-config";
|
|
7
|
+
import { createLinkTask } from "./link";
|
|
8
|
+
import { createBuildTask } from "./build";
|
|
9
|
+
import { createAfterBuildTasks } from "./after-build";
|
|
10
|
+
import { createUserCommandTasks } from "./user-commands";
|
|
11
|
+
import { createCopyDistTasks } from "./copy-dist";
|
|
12
|
+
import { createDeployTask } from "./deploy";
|
|
13
|
+
import { createAliasTask } from "./alias";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* 生成 Vercel 空配置文件
|
|
17
|
+
* @description
|
|
18
|
+
* 在根目录创建 vercel.null.def.json 文件
|
|
19
|
+
*/
|
|
20
|
+
async function generateVercelNullConfig() {
|
|
21
|
+
fs.writeFileSync(VERCEL_NULL_CONFIG_PATH, JSON.stringify(VERCEL_NULL_CONFIG, null, 2));
|
|
22
|
+
consola.success(`生成 Vercel 空配置文件: ${VERCEL_NULL_CONFIG_PATH}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 执行 Vercel 部署工作流
|
|
27
|
+
* @description
|
|
28
|
+
* 这是整个部署流程的总入口,使用 tasuku 编排所有任务
|
|
29
|
+
*
|
|
30
|
+
* 任务执行顺序:
|
|
31
|
+
* 1. Link 阶段(并行)
|
|
32
|
+
* 2. Build 阶段(并行)
|
|
33
|
+
* 3. AfterBuild 阶段(串行)
|
|
34
|
+
* 4. UserCommands + CopyDist 阶段(并行目标,串行步骤)
|
|
35
|
+
* 5. Deploy + Alias 阶段(并行目标,串行步骤)
|
|
36
|
+
*/
|
|
37
|
+
export async function executeDeploymentWorkflow(config: VercelDeployConfig) {
|
|
38
|
+
// 0. 生成 Vercel 空配置文件
|
|
39
|
+
await generateVercelNullConfig();
|
|
40
|
+
|
|
41
|
+
const { deployTargets } = config;
|
|
42
|
+
|
|
43
|
+
await task("Vercel 部署工作流", async ({ task }) => {
|
|
44
|
+
// 1. Link 阶段(并行)
|
|
45
|
+
await task("1. Link 项目", async () => {
|
|
46
|
+
const linkTasks = deployTargets.map((target) => createLinkTask(config, target));
|
|
47
|
+
|
|
48
|
+
await task.group((task) => linkTasks.map((t) => task(t.name, t.fn)));
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 2. Build 阶段(并行)
|
|
52
|
+
await task("2. 构建项目", async () => {
|
|
53
|
+
const buildTasks = deployTargets.filter(isNeedVercelBuild).map((target) => createBuildTask(config, target));
|
|
54
|
+
|
|
55
|
+
if (buildTasks.length === 0) {
|
|
56
|
+
consola.warn("没有需要执行 build 的目标");
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await task.group((task) => buildTasks.map((t) => task(t.name, t.fn)));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// 3. AfterBuild 阶段(串行)
|
|
64
|
+
await task("3. 执行 AfterBuild 任务", async () => {
|
|
65
|
+
const afterBuildTasks = createAfterBuildTasks(config);
|
|
66
|
+
|
|
67
|
+
await executeSequential("AfterBuild", afterBuildTasks);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 4. UserCommands + CopyDist 阶段(并行目标,串行步骤)
|
|
71
|
+
await task("4. 执行用户命令与文件复制", async () => {
|
|
72
|
+
const targetTasks = deployTargets.map((target) => ({
|
|
73
|
+
name: `处理目标: ${target.targetCWD}`,
|
|
74
|
+
fn: async () => {
|
|
75
|
+
// 如果不是 userCommands 类型,跳过
|
|
76
|
+
if (!isDeployTargetWithUserCommands(target)) {
|
|
77
|
+
consola.warn(`目标 ${target.targetCWD} 不属于 userCommands 类型`);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// 4.1 执行用户命令(串行)
|
|
82
|
+
const userCommandTasks = createUserCommandTasks(target);
|
|
83
|
+
await executeSequential(`UserCommands: ${target.targetCWD}`, userCommandTasks);
|
|
84
|
+
|
|
85
|
+
// 4.2 复制文件(串行)
|
|
86
|
+
if (getIsCopyDist(target)) {
|
|
87
|
+
const copyDistTasks = createCopyDistTasks(target);
|
|
88
|
+
await executeSequential(`CopyDist: ${target.targetCWD}`, copyDistTasks);
|
|
89
|
+
} else {
|
|
90
|
+
consola.warn(`目标 ${target.targetCWD} 不需要复制文件`);
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
}));
|
|
94
|
+
|
|
95
|
+
// 并行处理所有目标
|
|
96
|
+
await task.group((task) => targetTasks.map((t) => task(t.name, t.fn)));
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// 5. Deploy + Alias 阶段(并行目标,串行步骤)
|
|
100
|
+
await task("5. 部署与设置别名", async () => {
|
|
101
|
+
const deployAliasTasks = deployTargets.map((target) => ({
|
|
102
|
+
name: `部署与别名: ${target.targetCWD}`,
|
|
103
|
+
fn: async () => {
|
|
104
|
+
// 5.1 部署
|
|
105
|
+
const deployTask = createDeployTask(config, target);
|
|
106
|
+
const deployResult = await task(deployTask.name, deployTask.fn);
|
|
107
|
+
const vercelUrl = deployResult.result;
|
|
108
|
+
|
|
109
|
+
// 5.2 设置别名(并行)
|
|
110
|
+
if (target.url && target.url.length > 0) {
|
|
111
|
+
const aliasTasks = target.url.map((userUrl) => createAliasTask(config, vercelUrl, userUrl));
|
|
112
|
+
|
|
113
|
+
await task.group((task) => aliasTasks.map((t) => task(t.name, t.fn)));
|
|
114
|
+
} else {
|
|
115
|
+
consola.warn(`目标 ${target.targetCWD} 没有配置别名`);
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
}));
|
|
119
|
+
|
|
120
|
+
// 并行处理所有目标的部署和别名
|
|
121
|
+
await task.group((task) => deployAliasTasks.map((t) => task(t.name, t.fn)));
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
consola.success("🎉 Vercel 部署工作流完成!");
|
|
126
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { concat } from "lodash-es";
|
|
3
|
+
import { consola } from "consola";
|
|
4
|
+
import type { VercelDeployConfig, DeployTarget } from "../../config/schema";
|
|
5
|
+
import { getVercelProjectNameArg, getVercelTokenArg, getTargetCWDArg } from "../vercel";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 创建 Link 任务
|
|
9
|
+
* @description
|
|
10
|
+
* 旨在于封装类似于这样的命令:
|
|
11
|
+
*
|
|
12
|
+
* vc link --yes --cwd=./packages/docs --project=my-project -t TOKEN
|
|
13
|
+
*/
|
|
14
|
+
export function createLinkTask(config: VercelDeployConfig, target: DeployTarget) {
|
|
15
|
+
return {
|
|
16
|
+
name: `Link: ${target.targetCWD}`,
|
|
17
|
+
fn: async () => {
|
|
18
|
+
const args = concat(
|
|
19
|
+
["link"],
|
|
20
|
+
["--yes"],
|
|
21
|
+
getTargetCWDArg(target),
|
|
22
|
+
getVercelProjectNameArg(config),
|
|
23
|
+
getVercelTokenArg(config),
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
consola.start(`开始 link 任务: ${target.targetCWD}`);
|
|
27
|
+
|
|
28
|
+
const result = spawnSync("vercel", args, {
|
|
29
|
+
encoding: "utf-8",
|
|
30
|
+
stdio: "inherit",
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
if (result.error) {
|
|
34
|
+
consola.error(`link 任务失败: ${target.targetCWD}`);
|
|
35
|
+
throw result.error;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
consola.success(`完成 link 任务: ${target.targetCWD}`);
|
|
39
|
+
return result.stdout;
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { consola } from "consola";
|
|
3
|
+
import type { DeployTargetWithUserCommands } from "../../config/schema";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* 创建用户命令任务列表
|
|
7
|
+
* @description
|
|
8
|
+
* 执行用户自定义的命令列表
|
|
9
|
+
*/
|
|
10
|
+
export function createUserCommandTasks(target: DeployTargetWithUserCommands) {
|
|
11
|
+
return target.userCommands.map((command) => ({
|
|
12
|
+
name: `UserCommand: ${command}`,
|
|
13
|
+
fn: async () => {
|
|
14
|
+
consola.start(`开始用户命令任务: ${command}`);
|
|
15
|
+
|
|
16
|
+
const result = spawnSync(command, [], {
|
|
17
|
+
encoding: "utf-8",
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
shell: true,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (result.error) {
|
|
23
|
+
consola.error(`用户命令任务失败: ${command}`);
|
|
24
|
+
throw result.error;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
consola.success(`完成用户命令任务: ${command}`);
|
|
28
|
+
return result.stdout;
|
|
29
|
+
},
|
|
30
|
+
}));
|
|
31
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { VercelDeployConfig, DeployTarget } from "../config/schema";
|
|
2
|
+
import { VERCEL_NULL_CONFIG_PATH } from "../utils/vercel-null-config";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 获取 Vercel 项目名称参数
|
|
6
|
+
*/
|
|
7
|
+
export function getVercelProjectNameArg(config: VercelDeployConfig): string[] {
|
|
8
|
+
return ["--name", config.vercelProjectName];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 获取 Vercel scope 参数
|
|
13
|
+
*/
|
|
14
|
+
export function getVercelScopeArg(config: VercelDeployConfig): string[] {
|
|
15
|
+
return ["--scope", config.vercelOrgId];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 获取 Vercel token 参数
|
|
20
|
+
*/
|
|
21
|
+
export function getVercelTokenArg(config: VercelDeployConfig): string[] {
|
|
22
|
+
return ["--token", config.vercelToken];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* 获取 Vercel 本地配置文件参数
|
|
27
|
+
*/
|
|
28
|
+
export function getVercelLocalConfigArg(): string[] {
|
|
29
|
+
return ["--local-config", VERCEL_NULL_CONFIG_PATH];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 获取目标工作目录参数
|
|
34
|
+
*/
|
|
35
|
+
export function getTargetCWDArg(target: DeployTarget): string[] {
|
|
36
|
+
return ["--cwd", target.targetCWD];
|
|
37
|
+
}
|