@cloudbase/manager-node 5.7.1-beta.1 → 5.8.0-beta.1
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/lib/cloudApp/index.js +12 -3
- package/lib/deploy/DatabaseDeployer.js +238 -0
- package/lib/deploy/DeployOrchestrator.js +645 -0
- package/lib/deploy/FunctionDeployer.js +606 -0
- package/lib/deploy/GatewayDeployer.js +612 -0
- package/lib/deploy/StateStore.js +327 -0
- package/lib/deploy/StaticDeployer.js +236 -0
- package/lib/deploy/domain.js +41 -0
- package/lib/deploy/framework.js +230 -0
- package/lib/deploy/function/artifact.js +214 -0
- package/lib/deploy/function/builders/cloud.js +779 -0
- package/lib/deploy/function/cam-preflight.js +173 -0
- package/lib/deploy/function/cloud-build-service.js +191 -0
- package/lib/deploy/function/config-guard.js +595 -0
- package/lib/deploy/function/docker-preflight.js +87 -0
- package/lib/deploy/function/image-preflight.js +164 -0
- package/lib/deploy/function/local-builder.js +129 -0
- package/lib/deploy/function/planner.js +278 -0
- package/lib/deploy/function/preflight.js +329 -0
- package/lib/deploy/types.js +107 -0
- package/lib/env/index.js +0 -27
- package/lib/environment.js +11 -0
- package/lib/function/index.js +30 -4
- package/lib/hosting/index.js +4 -1
- package/lib/index.js +31 -0
- package/lib/projectValidator/index.js +452 -0
- package/lib/projectValidator/types.js +2 -0
- package/lib/storage/index.js +6 -7
- package/lib/utils/index.js +62 -5
- package/package.json +2 -1
- package/types/cloudApp/index.d.ts +4 -0
- package/types/cloudApp/types.d.ts +87 -6
- package/types/deploy/DatabaseDeployer.d.ts +70 -0
- package/types/deploy/DeployOrchestrator.d.ts +170 -0
- package/types/deploy/FunctionDeployer.d.ts +161 -0
- package/types/deploy/GatewayDeployer.d.ts +194 -0
- package/types/deploy/StateStore.d.ts +170 -0
- package/types/deploy/StaticDeployer.d.ts +97 -0
- package/types/deploy/domain.d.ts +17 -0
- package/types/deploy/framework.d.ts +63 -0
- package/types/deploy/function/artifact.d.ts +40 -0
- package/types/deploy/function/builders/cloud.d.ts +110 -0
- package/types/deploy/function/cam-preflight.d.ts +47 -0
- package/types/deploy/function/cloud-build-service.d.ts +10 -0
- package/types/deploy/function/config-guard.d.ts +41 -0
- package/types/deploy/function/docker-preflight.d.ts +17 -0
- package/types/deploy/function/image-preflight.d.ts +21 -0
- package/types/deploy/function/local-builder.d.ts +26 -0
- package/types/deploy/function/planner.d.ts +15 -0
- package/types/deploy/function/preflight.d.ts +9 -0
- package/types/deploy/types.d.ts +433 -0
- package/types/env/index.d.ts +1 -17
- package/types/env/type.d.ts +6 -260
- package/types/environment.d.ts +7 -0
- package/types/function/index.d.ts +2 -0
- package/types/function/types.d.ts +15 -0
- package/types/hosting/index.d.ts +6 -0
- package/types/index.d.ts +18 -0
- package/types/interfaces/function.interface.d.ts +1 -1
- package/types/projectValidator/index.d.ts +38 -0
- package/types/projectValidator/types.d.ts +26 -0
- package/types/storage/index.d.ts +6 -0
- package/types/utils/index.d.ts +13 -0
package/lib/cloudApp/index.js
CHANGED
|
@@ -123,10 +123,14 @@ class CloudAppService {
|
|
|
123
123
|
* 注意:此方法不依赖应用是否已创建,可在创建应用前调用
|
|
124
124
|
* 返回的 cosTimestamp 用于 createApp 时传入 staticConfig.cosTimestamp
|
|
125
125
|
*
|
|
126
|
+
* 内置默认忽略:.git 目录、.DS_Store(含任意层级);node_modules 仅在
|
|
127
|
+
* excludeNodeModules 为 true(默认)时排除——云端构建(需云端 install)场景应传
|
|
128
|
+
* excludeNodeModules: false 允许私有/离线依赖随源码包上传。
|
|
129
|
+
*
|
|
126
130
|
* @param params 上传参数
|
|
127
131
|
*/
|
|
128
132
|
async uploadCode(params) {
|
|
129
|
-
const { serviceName, localPath, ignore = [], onPhase } = params;
|
|
133
|
+
const { serviceName, localPath, ignore = [], excludeNodeModules = true, onPhase } = params;
|
|
130
134
|
// 1. 验证本地路径
|
|
131
135
|
if (!fs_1.default.existsSync(localPath)) {
|
|
132
136
|
throw new error_1.CloudBaseError(`本地路径不存在: ${localPath}`);
|
|
@@ -135,9 +139,14 @@ class CloudAppService {
|
|
|
135
139
|
if (!stat.isDirectory()) {
|
|
136
140
|
throw new error_1.CloudBaseError(`localPath 必须是目录: ${localPath}`);
|
|
137
141
|
}
|
|
138
|
-
// 2. 打包本地文件为 ZIP
|
|
142
|
+
// 2. 打包本地文件为 ZIP(node_modules 按场景决定是否排除)
|
|
139
143
|
const tmpZipPath = path_1.default.join(os_1.default.tmpdir(), `cloudapp-${Date.now()}-${Math.random().toString(36).slice(2)}.zip`);
|
|
140
|
-
const defaultIgnore = [
|
|
144
|
+
const defaultIgnore = [
|
|
145
|
+
...(excludeNodeModules ? ['node_modules/**'] : []),
|
|
146
|
+
'.git/**',
|
|
147
|
+
'.DS_Store',
|
|
148
|
+
'**/.DS_Store'
|
|
149
|
+
];
|
|
141
150
|
const packStart = Date.now();
|
|
142
151
|
await (0, utils_1.compressToZip)({
|
|
143
152
|
dirPath: localPath,
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DatabaseDeployer = void 0;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const error_1 = require("../error");
|
|
10
|
+
/** migration 文件名正则:14位数字 + 下划线 + 小写字母下划线名称 + .sql */
|
|
11
|
+
const MIGRATION_FILE_PATTERN = /^(\d{14})_([a-z_]+)\.sql$/;
|
|
12
|
+
/** 轮询间隔(毫秒) */
|
|
13
|
+
const POLL_INTERVAL_MS = 2000;
|
|
14
|
+
/** 默认超时(毫秒) */
|
|
15
|
+
const DEFAULT_TIMEOUT_MS = 3000000;
|
|
16
|
+
class DatabaseDeployer {
|
|
17
|
+
constructor(environment) {
|
|
18
|
+
this.environment = environment;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 从本地目录解析 migration 文件列表
|
|
22
|
+
*
|
|
23
|
+
* 文件名格式:{14位时间戳}_{小写下划线名称}.sql
|
|
24
|
+
* 例如:20260526000000_create_users.sql
|
|
25
|
+
*
|
|
26
|
+
* 按文件名升序排列(即按版本号从早到晚)
|
|
27
|
+
*/
|
|
28
|
+
parseMigrationFiles(migrationsDir) {
|
|
29
|
+
if (!fs_1.default.existsSync(migrationsDir)) {
|
|
30
|
+
throw new error_1.CloudBaseError(`数据库迁移目录不存在: ${migrationsDir}`);
|
|
31
|
+
}
|
|
32
|
+
const files = fs_1.default.readdirSync(migrationsDir)
|
|
33
|
+
.filter(f => f.endsWith('.sql'))
|
|
34
|
+
.sort();
|
|
35
|
+
const migrations = [];
|
|
36
|
+
for (const file of files) {
|
|
37
|
+
const match = file.match(MIGRATION_FILE_PATTERN);
|
|
38
|
+
if (!match) {
|
|
39
|
+
throw new error_1.CloudBaseError(`migration 文件名格式错误: ${file},应为 {14位时间戳}_{小写下划线名称}.sql`);
|
|
40
|
+
}
|
|
41
|
+
const [, version, name] = match;
|
|
42
|
+
const filePath = path_1.default.join(migrationsDir, file);
|
|
43
|
+
const query = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
44
|
+
if (!query.trim()) {
|
|
45
|
+
throw new error_1.CloudBaseError(`migration 文件内容为空: ${file}`);
|
|
46
|
+
}
|
|
47
|
+
migrations.push({ Version: version, Name: name, Query: query });
|
|
48
|
+
}
|
|
49
|
+
return migrations;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 生成部署计划
|
|
53
|
+
*
|
|
54
|
+
* 调用 previewPGUserMigrations 获取云端对比结果,
|
|
55
|
+
* 返回每条migration 的状态(pending / applied / conflict)
|
|
56
|
+
*/
|
|
57
|
+
async plan(config, cwd, envId) {
|
|
58
|
+
this.assertDatabaseType(config);
|
|
59
|
+
const migrationsDir = this.resolveMigrationsDir(cwd, config.migrations);
|
|
60
|
+
const migrations = this.parseMigrationFiles(migrationsDir);
|
|
61
|
+
if (migrations.length === 0) {
|
|
62
|
+
return [];
|
|
63
|
+
}
|
|
64
|
+
const dbService = this.environment.getDatabaseService();
|
|
65
|
+
let preview;
|
|
66
|
+
try {
|
|
67
|
+
preview = await dbService.previewPGUserMigrations({
|
|
68
|
+
EnvId: envId,
|
|
69
|
+
Migrations: migrations
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
const err = error;
|
|
74
|
+
throw new error_1.CloudBaseError(`数据库迁移预览失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
75
|
+
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
76
|
+
original: err instanceof Error ? err : undefined
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const items = [];
|
|
80
|
+
// pending:将要执行的
|
|
81
|
+
for (const p of preview.Pending || []) {
|
|
82
|
+
items.push({ version: p.Version, name: p.Name, status: 'pending' });
|
|
83
|
+
}
|
|
84
|
+
// applied:已经执行过的
|
|
85
|
+
for (const a of preview.Applied || []) {
|
|
86
|
+
items.push({ version: a.Version, name: a.Name, status: 'applied' });
|
|
87
|
+
}
|
|
88
|
+
// conflict:有冲突的
|
|
89
|
+
for (const c of preview.Conflicts || []) {
|
|
90
|
+
items.push({
|
|
91
|
+
version: c.Version,
|
|
92
|
+
name: c.Name || '',
|
|
93
|
+
status: 'conflict',
|
|
94
|
+
reason: c.Reason || c.Message || 'checksum mismatch'
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return items;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* 执行数据库迁移
|
|
101
|
+
*
|
|
102
|
+
* 流程:
|
|
103
|
+
* 1. 解析本地 migration 文件
|
|
104
|
+
* 2. 预览确认无冲突(有冲突直接 throw,中断整个部署)
|
|
105
|
+
* 3. 无 pending 则跳过
|
|
106
|
+
* 4. 提交 pushPGUserMigrations
|
|
107
|
+
* 5. 轮询describeTaskResult 等待完成
|
|
108
|
+
*/
|
|
109
|
+
async deploy(config, cwd, envId) {
|
|
110
|
+
this.assertDatabaseType(config);
|
|
111
|
+
const migrationsDir = this.resolveMigrationsDir(cwd, config.migrations);
|
|
112
|
+
const migrations = this.parseMigrationFiles(migrationsDir);
|
|
113
|
+
if (migrations.length === 0) {
|
|
114
|
+
return { taskId: '' };
|
|
115
|
+
}
|
|
116
|
+
const dbService = this.environment.getDatabaseService();
|
|
117
|
+
// 1. 预览:确认无冲突
|
|
118
|
+
let preview;
|
|
119
|
+
try {
|
|
120
|
+
preview = await dbService.previewPGUserMigrations({
|
|
121
|
+
EnvId: envId,
|
|
122
|
+
Migrations: migrations
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
const err = error;
|
|
127
|
+
throw new error_1.CloudBaseError(`数据库迁移预览失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
128
|
+
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
129
|
+
original: err instanceof Error ? err : undefined
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
if (!preview.Executable) {
|
|
133
|
+
const conflictDetails = (preview.Conflicts || [])
|
|
134
|
+
.map((c) => ` - ${c.Version}_${c.Name || '?'}: ${c.Reason || c.Message || 'unknown'}`)
|
|
135
|
+
.join('\n');
|
|
136
|
+
throw new error_1.CloudBaseError(`数据库迁移存在冲突,中断部署。请先解决以下冲突:\n${conflictDetails}`, {
|
|
137
|
+
requestId: preview.RequestId
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
// 2. 无 pending → 全部已applied,跳过
|
|
141
|
+
if (!preview.Pending || preview.Pending.length === 0) {
|
|
142
|
+
return { taskId: '' };
|
|
143
|
+
}
|
|
144
|
+
// 3. 提交执行
|
|
145
|
+
let result;
|
|
146
|
+
try {
|
|
147
|
+
result = await dbService.pushPGUserMigrations({
|
|
148
|
+
EnvId: envId,
|
|
149
|
+
Migrations: migrations
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
catch (error) {
|
|
153
|
+
const err = error;
|
|
154
|
+
throw new error_1.CloudBaseError(`数据库迁移提交失败 [EnvId=${envId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
155
|
+
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
156
|
+
original: err instanceof Error ? err : undefined
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// 4. 轮询等待完成
|
|
160
|
+
try {
|
|
161
|
+
await this.waitForTask(envId, result.TaskId, DEFAULT_TIMEOUT_MS);
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
if (error instanceof error_1.CloudBaseError) {
|
|
165
|
+
throw error;
|
|
166
|
+
}
|
|
167
|
+
const err = error;
|
|
168
|
+
throw new error_1.CloudBaseError(`数据库迁移任务等待失败 [TaskId=${result.TaskId}]:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
169
|
+
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
170
|
+
original: err instanceof Error ? err : undefined
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
return { taskId: result.TaskId };
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* 解析 migrations 目录路径
|
|
177
|
+
* - 未配置:默认 ./cloudbase/migrations
|
|
178
|
+
* - 仅允许相对路径(禁止 / 开头)
|
|
179
|
+
*/
|
|
180
|
+
resolveMigrationsDir(cwd, migrations) {
|
|
181
|
+
const raw = migrations || './cloudbase/migrations';
|
|
182
|
+
if (path_1.default.isAbsolute(raw) || raw.startsWith('/')) {
|
|
183
|
+
throw new error_1.CloudBaseError('database.migrations 仅支持相对路径(例如 ./cloudbase/migrations)');
|
|
184
|
+
}
|
|
185
|
+
return path_1.default.resolve(cwd, raw);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* 校验 database.type(当前仅支持 postgresql)
|
|
189
|
+
*/
|
|
190
|
+
assertDatabaseType(config) {
|
|
191
|
+
if (config.type !== 'postgresql') {
|
|
192
|
+
throw new error_1.CloudBaseError(`database.type 仅支持 postgresql,当前为: ${String(config.type)}`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* 轮询等待异步任务完成
|
|
197
|
+
*/
|
|
198
|
+
async waitForTask(envId, taskId, timeoutMs) {
|
|
199
|
+
const dbService = this.environment.getDatabaseService();
|
|
200
|
+
const startTime = Date.now();
|
|
201
|
+
while (true) {
|
|
202
|
+
let task;
|
|
203
|
+
try {
|
|
204
|
+
task = await dbService.describeTaskResult({ EnvId: envId, TaskId: taskId });
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
208
|
+
const err = error;
|
|
209
|
+
throw new error_1.CloudBaseError(`数据库迁移任务状态查询超时(${timeoutMs / 1000}s),TaskId: ${taskId},最后错误:${(err === null || err === void 0 ? void 0 : err.message) || String(error)}`, {
|
|
210
|
+
requestId: (err === null || err === void 0 ? void 0 : err.RequestId) || (err === null || err === void 0 ? void 0 : err.requestId),
|
|
211
|
+
original: err instanceof Error ? err : undefined
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
await new Promise(resolve => {
|
|
215
|
+
setTimeout(resolve, POLL_INTERVAL_MS);
|
|
216
|
+
});
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (task.Status === 'Succeed') {
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (task.Status === 'Failed') {
|
|
223
|
+
throw new error_1.CloudBaseError(`数据库迁移任务失败 [TaskId=${taskId}]: ${task.Reason || '未知原因'}`, {
|
|
224
|
+
requestId: task.RequestId
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
if (Date.now() - startTime > timeoutMs) {
|
|
228
|
+
throw new error_1.CloudBaseError(`数据库迁移任务超时(${timeoutMs / 1000}s),TaskId: ${taskId},当前状态: ${task.Status}/${task.Phase}`, {
|
|
229
|
+
requestId: task.RequestId
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
await new Promise(resolve => {
|
|
233
|
+
setTimeout(resolve, POLL_INTERVAL_MS);
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.DatabaseDeployer = DatabaseDeployer;
|