@mindbase/deploy 1.1.5 → 1.2.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/bin/shared.ts +1 -24
- package/package.json +1 -1
- package/src/app.ts +60 -103
- package/src/config/schema.ts +0 -2
- package/src/init/app.ts +0 -1
package/bin/shared.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import * as clack from '@clack/prompts';
|
|
3
|
-
import { runApp
|
|
3
|
+
import { runApp } from '../src/app.js';
|
|
4
4
|
import { runInit } from '../src/init/app.js';
|
|
5
5
|
import { createServerCommand } from '../src/server/app.js';
|
|
6
6
|
|
|
@@ -14,14 +14,12 @@ export function createDeployProgram(): Command {
|
|
|
14
14
|
.description('部署工具')
|
|
15
15
|
.version('1.0.0')
|
|
16
16
|
.action(async () => {
|
|
17
|
-
clack.intro('deploy');
|
|
18
17
|
try {
|
|
19
18
|
await runApp();
|
|
20
19
|
} catch (err) {
|
|
21
20
|
clack.log.error(`执行失败: ${err}`);
|
|
22
21
|
process.exit(1);
|
|
23
22
|
}
|
|
24
|
-
clack.outro('再见');
|
|
25
23
|
});
|
|
26
24
|
|
|
27
25
|
program
|
|
@@ -37,27 +35,6 @@ export function createDeployProgram(): Command {
|
|
|
37
35
|
}
|
|
38
36
|
});
|
|
39
37
|
|
|
40
|
-
program
|
|
41
|
-
.command('run [path]')
|
|
42
|
-
.description('部署单个项目(path 缺省为 cwd,可传目录或 deploy.config.json)')
|
|
43
|
-
.option('-s, --server <server>', '覆盖项目配置中的 server')
|
|
44
|
-
.action(async (path: string | undefined, options: { server?: string }) => {
|
|
45
|
-
clack.intro('deploy run');
|
|
46
|
-
try {
|
|
47
|
-
await runProject(path, options.server);
|
|
48
|
-
} catch (err) {
|
|
49
|
-
clack.log.error(`执行失败: ${err}`);
|
|
50
|
-
process.exit(1);
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
program
|
|
55
|
-
.command('config')
|
|
56
|
-
.description('查看全局 servers 配置')
|
|
57
|
-
.action(() => {
|
|
58
|
-
showConfig();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
38
|
// 服务器管理子命令
|
|
62
39
|
program.addCommand(createServerCommand());
|
|
63
40
|
|
package/package.json
CHANGED
package/src/app.ts
CHANGED
|
@@ -2,7 +2,7 @@ import * as clack from '@clack/prompts';
|
|
|
2
2
|
import pc from 'picocolors';
|
|
3
3
|
import { relative } from 'path';
|
|
4
4
|
import { noteBox } from '@mindbase/cli-ui';
|
|
5
|
-
import { getConfig,
|
|
5
|
+
import { getConfig, discoverProjectConfigs } from './config/manager.js';
|
|
6
6
|
import type { ProjectConfig } from './config/schema.js';
|
|
7
7
|
import { connectSftp } from './ssh/client.js';
|
|
8
8
|
import { runSteps } from './script/runner.js';
|
|
@@ -17,86 +17,18 @@ function handleCancel<T>(result: T | symbol): T {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
21
|
-
*/
|
|
22
|
-
export function showConfig(): void {
|
|
23
|
-
clack.intro('配置信息');
|
|
24
|
-
const config = getConfig();
|
|
25
|
-
const path = getConfigPath();
|
|
26
|
-
noteBox(`配置文件: ${path}\n\n${JSON.stringify(config, null, 2)}`, '当前配置');
|
|
27
|
-
clack.outro('完成');
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 显示项目配置摘要并让用户确认部署
|
|
32
|
-
*/
|
|
33
|
-
async function confirmProjectDeploy(project: ProjectConfig, serverId: string): Promise<boolean> {
|
|
34
|
-
const summary = [
|
|
35
|
-
`名称: ${project.name}`,
|
|
36
|
-
`服务器: ${serverId}`,
|
|
37
|
-
`本地目录: ${project.localDir}`,
|
|
38
|
-
`远端目录: ${project.remoteDir}`,
|
|
39
|
-
`步骤数: ${project.steps.length}`,
|
|
40
|
-
].join('\n');
|
|
41
|
-
noteBox(summary, '即将部署');
|
|
42
|
-
return handleCancel<boolean>(
|
|
43
|
-
await clack.confirm({ message: '确认部署?', initialValue: true })
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* 直接运行指定项目(单项目模式)
|
|
49
|
-
*/
|
|
50
|
-
export async function runProject(targetPath?: string, serverOverride?: string): Promise<void> {
|
|
51
|
-
const project = loadProjectConfig(targetPath);
|
|
52
|
-
const globalConfig = getConfig();
|
|
53
|
-
|
|
54
|
-
const serverId = serverOverride || project.server;
|
|
55
|
-
const serverConfig = globalConfig.servers[serverId];
|
|
56
|
-
if (!serverConfig) {
|
|
57
|
-
clack.log.error(`服务器 "${serverId}" 不存在,请先运行 deploy server add`);
|
|
58
|
-
process.exit(1);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
clack.intro(`部署 ${pc.cyan(project.name)} → ${pc.cyan(serverId)}`);
|
|
62
|
-
|
|
63
|
-
if (!(await confirmProjectDeploy(project, serverId))) {
|
|
64
|
-
clack.log.info('已取消');
|
|
65
|
-
clack.outro('完成');
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const spinner = clack.spinner();
|
|
70
|
-
spinner.start('连接服务器...');
|
|
71
|
-
const sftp = await connectSftp(project.name, serverConfig);
|
|
72
|
-
spinner.stop('已连接');
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
await runSteps(project.steps, project, sftp);
|
|
76
|
-
clack.log.success(`${project.name} 部署完成`);
|
|
77
|
-
} catch (err) {
|
|
78
|
-
clack.log.error(`部署失败: ${err}`);
|
|
79
|
-
} finally {
|
|
80
|
-
await sftp.end();
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
clack.outro('完成');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 交互式主流程:扫描 cwd 下所有 deploy.config.json → 多选 → 按 server 分组部署
|
|
20
|
+
* 交互式主流程:选 server → 选项目 → 按笛卡尔分组部署
|
|
88
21
|
*/
|
|
89
22
|
export async function runApp(): Promise<void> {
|
|
90
23
|
const cwd = process.cwd();
|
|
91
24
|
const discovered = discoverProjectConfigs(cwd);
|
|
92
25
|
const globalConfig = getConfig();
|
|
93
26
|
|
|
27
|
+
// 边界校验(intro 之前)
|
|
94
28
|
if (discovered.length === 0) {
|
|
95
29
|
clack.log.error(`未在 ${cwd} 下找到任何 deploy.config.json,请先运行 deploy init`);
|
|
96
30
|
return;
|
|
97
31
|
}
|
|
98
|
-
|
|
99
|
-
// 0 个服务器直接报错
|
|
100
32
|
if (Object.keys(globalConfig.servers).length === 0) {
|
|
101
33
|
clack.log.error('没有已配置的服务器,请先运行 deploy server add');
|
|
102
34
|
return;
|
|
@@ -104,8 +36,21 @@ export async function runApp(): Promise<void> {
|
|
|
104
36
|
|
|
105
37
|
clack.intro('deploy');
|
|
106
38
|
|
|
107
|
-
//
|
|
108
|
-
|
|
39
|
+
// 多选 server
|
|
40
|
+
const serverIds = Object.keys(globalConfig.servers);
|
|
41
|
+
const selectedServers = handleCancel<string[]>(
|
|
42
|
+
await clack.multiselect({
|
|
43
|
+
message: '选择要部署到的服务器(空格切换,a 全选)',
|
|
44
|
+
options: serverIds.map((id) => ({
|
|
45
|
+
value: id,
|
|
46
|
+
label: `${id} (${globalConfig.servers[id].host})`,
|
|
47
|
+
})),
|
|
48
|
+
required: true,
|
|
49
|
+
})
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// 多选项目
|
|
53
|
+
let selectedProjects: typeof discovered;
|
|
109
54
|
|
|
110
55
|
if (discovered.length === 1) {
|
|
111
56
|
const only = discovered[0];
|
|
@@ -117,57 +62,69 @@ export async function runApp(): Promise<void> {
|
|
|
117
62
|
clack.outro('已取消');
|
|
118
63
|
return;
|
|
119
64
|
}
|
|
120
|
-
|
|
65
|
+
selectedProjects = [only];
|
|
121
66
|
} else {
|
|
122
|
-
|
|
67
|
+
selectedProjects = handleCancel<typeof discovered>(
|
|
123
68
|
await clack.multiselect({
|
|
124
69
|
message: '选择要部署的项目(空格切换,a 全选)',
|
|
125
70
|
options: discovered.map((item) => ({
|
|
126
71
|
value: item,
|
|
127
|
-
label: `${item.config.name} (${relative(cwd, item.path) || item.path})
|
|
72
|
+
label: `${item.config.name} (${relative(cwd, item.path) || item.path})`,
|
|
128
73
|
})),
|
|
129
74
|
required: true,
|
|
130
75
|
})
|
|
131
76
|
);
|
|
132
77
|
}
|
|
133
78
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (missingServers.size > 0) {
|
|
142
|
-
clack.log.error(`以下服务器未配置: ${[...missingServers].join(', ')}`);
|
|
143
|
-
clack.outro('已中止');
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
79
|
+
// 确认摘要
|
|
80
|
+
const summaryLines = [
|
|
81
|
+
`服务器: ${selectedServers.map((s) => `${s} (${globalConfig.servers[s].host})`).join(', ')}`,
|
|
82
|
+
`项目: ${selectedProjects.map((p) => p.config.name).join(', ')}`,
|
|
83
|
+
`组合: ${selectedServers.length} × ${selectedProjects.length} = ${selectedServers.length * selectedProjects.length} 次部署`,
|
|
84
|
+
].join('\n');
|
|
85
|
+
noteBox(summaryLines, '即将部署');
|
|
146
86
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
87
|
+
const confirmed = handleCancel<boolean>(
|
|
88
|
+
await clack.confirm({ message: '确认开始部署?', initialValue: true })
|
|
89
|
+
);
|
|
90
|
+
if (!confirmed) {
|
|
91
|
+
clack.outro('已取消');
|
|
92
|
+
return;
|
|
153
93
|
}
|
|
154
94
|
|
|
155
|
-
//
|
|
156
|
-
|
|
95
|
+
// 按 server 分组遍历(笛卡尔:每个 server 部署全部选中项目)
|
|
96
|
+
let anyFailure = false;
|
|
97
|
+
for (const serverId of selectedServers) {
|
|
157
98
|
const serverConfig = globalConfig.servers[serverId];
|
|
158
99
|
const spinner = clack.spinner();
|
|
159
|
-
spinner.start(`连接 ${serverId}...`);
|
|
160
|
-
|
|
161
|
-
|
|
100
|
+
spinner.start(`连接 ${serverId} (${serverConfig.host})...`);
|
|
101
|
+
let sftp;
|
|
102
|
+
try {
|
|
103
|
+
sftp = await connectSftp('deploy', serverConfig);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
spinner.stop(`连接失败: ${serverId}`);
|
|
106
|
+
clack.log.error(`${serverId} 连接失败: ${err}`);
|
|
107
|
+
anyFailure = true;
|
|
108
|
+
const cont = handleCancel<boolean>(
|
|
109
|
+
await clack.confirm({ message: `${serverId} 不可用,是否跳过并继续下一个服务器?` })
|
|
110
|
+
);
|
|
111
|
+
if (!cont) {
|
|
112
|
+
clack.outro('已中止');
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
spinner.stop(`已连接 ${serverId}`);
|
|
162
118
|
|
|
163
119
|
try {
|
|
164
|
-
for (const { config } of
|
|
120
|
+
for (const { config } of selectedProjects) {
|
|
165
121
|
clack.log.step(`开始部署 ${pc.cyan(config.name)} → ${pc.cyan(serverId)}`);
|
|
166
122
|
try {
|
|
167
123
|
await runSteps(config.steps, config, sftp);
|
|
168
|
-
clack.log.success(`${config.name}
|
|
124
|
+
clack.log.success(`${config.name} → ${serverId} 完成`);
|
|
169
125
|
} catch (err) {
|
|
170
|
-
|
|
126
|
+
anyFailure = true;
|
|
127
|
+
clack.log.error(`${config.name} → ${serverId} 失败: ${err}`);
|
|
171
128
|
const cont = handleCancel<boolean>(
|
|
172
129
|
await clack.confirm({ message: '是否继续部署下一个项目?' })
|
|
173
130
|
);
|
|
@@ -179,5 +136,5 @@ export async function runApp(): Promise<void> {
|
|
|
179
136
|
}
|
|
180
137
|
}
|
|
181
138
|
|
|
182
|
-
clack.outro('全部完成');
|
|
139
|
+
clack.outro(anyFailure ? '完成(存在失败项)' : '全部完成');
|
|
183
140
|
}
|
package/src/config/schema.ts
CHANGED