@cloudbase/manager-node 5.6.8 → 5.7.0-beta.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.
Files changed (62) hide show
  1. package/lib/cloudApp/index.js +12 -3
  2. package/lib/cloudrun/index.js +0 -18
  3. package/lib/deploy/DatabaseDeployer.js +238 -0
  4. package/lib/deploy/DeployOrchestrator.js +564 -0
  5. package/lib/deploy/FunctionDeployer.js +611 -0
  6. package/lib/deploy/GatewayDeployer.js +522 -0
  7. package/lib/deploy/StateStore.js +327 -0
  8. package/lib/deploy/StaticDeployer.js +289 -0
  9. package/lib/deploy/domain.js +39 -0
  10. package/lib/deploy/framework.js +105 -0
  11. package/lib/deploy/function/artifact.js +207 -0
  12. package/lib/deploy/function/builders/cloud.js +454 -0
  13. package/lib/deploy/function/cam-preflight.js +157 -0
  14. package/lib/deploy/function/cloud-build-service.js +170 -0
  15. package/lib/deploy/function/config-guard.js +588 -0
  16. package/lib/deploy/function/docker-preflight.js +87 -0
  17. package/lib/deploy/function/image-preflight.js +164 -0
  18. package/lib/deploy/function/local-builder.js +110 -0
  19. package/lib/deploy/function/planner.js +282 -0
  20. package/lib/deploy/function/preflight.js +264 -0
  21. package/lib/deploy/types.js +99 -0
  22. package/lib/environment.js +11 -0
  23. package/lib/hosting/index.js +4 -1
  24. package/lib/index.js +31 -0
  25. package/lib/projectValidator/index.js +375 -0
  26. package/lib/projectValidator/types.js +2 -0
  27. package/lib/storage/index.js +6 -7
  28. package/lib/utils/index.js +41 -1
  29. package/package.json +2 -1
  30. package/types/cloudApp/index.d.ts +4 -0
  31. package/types/cloudApp/types.d.ts +87 -6
  32. package/types/cloudrun/index.d.ts +1 -7
  33. package/types/cloudrun/type.d.ts +0 -22
  34. package/types/deploy/DatabaseDeployer.d.ts +70 -0
  35. package/types/deploy/DeployOrchestrator.d.ts +150 -0
  36. package/types/deploy/FunctionDeployer.d.ts +158 -0
  37. package/types/deploy/GatewayDeployer.d.ts +175 -0
  38. package/types/deploy/StateStore.d.ts +170 -0
  39. package/types/deploy/StaticDeployer.d.ts +104 -0
  40. package/types/deploy/domain.d.ts +17 -0
  41. package/types/deploy/framework.d.ts +23 -0
  42. package/types/deploy/function/artifact.d.ts +40 -0
  43. package/types/deploy/function/builders/cloud.d.ts +110 -0
  44. package/types/deploy/function/cam-preflight.d.ts +51 -0
  45. package/types/deploy/function/cloud-build-service.d.ts +10 -0
  46. package/types/deploy/function/config-guard.d.ts +41 -0
  47. package/types/deploy/function/docker-preflight.d.ts +17 -0
  48. package/types/deploy/function/image-preflight.d.ts +21 -0
  49. package/types/deploy/function/local-builder.d.ts +26 -0
  50. package/types/deploy/function/planner.d.ts +15 -0
  51. package/types/deploy/function/preflight.d.ts +9 -0
  52. package/types/deploy/types.d.ts +429 -0
  53. package/types/env/type.d.ts +4 -4
  54. package/types/environment.d.ts +7 -0
  55. package/types/function/types.d.ts +15 -0
  56. package/types/hosting/index.d.ts +6 -0
  57. package/types/index.d.ts +18 -0
  58. package/types/interfaces/function.interface.d.ts +1 -1
  59. package/types/projectValidator/index.d.ts +24 -0
  60. package/types/projectValidator/types.d.ts +26 -0
  61. package/types/storage/index.d.ts +6 -0
  62. package/types/utils/index.d.ts +13 -0
@@ -0,0 +1,375 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ var __importDefault = (this && this.__importDefault) || function (mod) {
17
+ return (mod && mod.__esModule) ? mod : { "default": mod };
18
+ };
19
+ Object.defineProperty(exports, "__esModule", { value: true });
20
+ exports.ProjectValidator = void 0;
21
+ const fs_1 = __importDefault(require("fs"));
22
+ const path_1 = __importDefault(require("path"));
23
+ const toolbox_1 = require("@cloudbase/toolbox");
24
+ class ProjectValidator {
25
+ async validate(options = {}) {
26
+ var _a;
27
+ const cwd = path_1.default.resolve(options.cwd || process.cwd());
28
+ const errors = [];
29
+ const warnings = [];
30
+ const loaded = await this.loadProjectConfig(cwd, {
31
+ configPath: options.configPath,
32
+ mode: options.mode,
33
+ errors,
34
+ });
35
+ const version = String(((_a = loaded.rawConfig) === null || _a === void 0 ? void 0 : _a.version) || '2.1');
36
+ if (loaded.rawConfig) {
37
+ this.validateSchema(loaded.rawConfig, errors);
38
+ }
39
+ if (loaded.config) {
40
+ this.validateEnvId(cwd, { raw: loaded.rawConfig || {}, resolved: loaded.config }, errors);
41
+ this.validatePersonalRegistryConfig(loaded.config, errors);
42
+ const resourceIssues = this.validateResources(cwd, loaded.config);
43
+ errors.push(...resourceIssues.errors);
44
+ warnings.push(...resourceIssues.warnings);
45
+ this.validateConsistency(loaded.config, errors);
46
+ }
47
+ const summary = this.getSummary(loaded.config);
48
+ return {
49
+ valid: errors.length === 0,
50
+ version,
51
+ errors,
52
+ warnings,
53
+ summary,
54
+ };
55
+ }
56
+ async loadProjectConfig(cwd, options) {
57
+ const { configPath, mode, errors } = options;
58
+ let filepath = configPath ? path_1.default.resolve(cwd, configPath) : undefined;
59
+ try {
60
+ if (!filepath) {
61
+ const result = await (0, toolbox_1.searchConfig)(cwd);
62
+ filepath = result === null || result === void 0 ? void 0 : result.filepath;
63
+ }
64
+ if (!filepath) {
65
+ errors.push({
66
+ level: 'error',
67
+ field: 'cloudbaserc',
68
+ message: '未找到 cloudbaserc 配置文件',
69
+ fix: '在项目根目录新增 cloudbaserc.json / cloudbaserc.js / cloudbaserc.yaml / cloudbaserc.yml',
70
+ });
71
+ return {};
72
+ }
73
+ const rawConfig = (await (0, toolbox_1.loadConfig)({ configPath: filepath }));
74
+ const config = (await toolbox_1.ConfigParser.parseRawConfig(rawConfig || {}, cwd, {
75
+ mode,
76
+ }));
77
+ return {
78
+ filepath,
79
+ rawConfig,
80
+ config,
81
+ };
82
+ }
83
+ catch (e) {
84
+ errors.push({
85
+ level: 'error',
86
+ field: 'cloudbaserc',
87
+ message: `配置文件解析失败:${this.getErrorMessage(e)}`,
88
+ fix: '检查 cloudbaserc 配置文件语法是否正确',
89
+ });
90
+ return { filepath };
91
+ }
92
+ }
93
+ validateSchema(config, errors) {
94
+ const result = (0, toolbox_1.validateCloudBaseConfigBySchema)(config, { version: '2.1' });
95
+ result.errors.forEach((error) => {
96
+ errors.push({
97
+ level: 'error',
98
+ field: this.normalizeSchemaPath(error.dataPath),
99
+ message: `配置不符合 v2.1 Schema:${error.message}`,
100
+ fix: `请检查 ${this.normalizeSchemaPath(error.dataPath) || 'cloudbaserc'} 字段`,
101
+ });
102
+ });
103
+ }
104
+ validateEnvId(cwd, config, errors) {
105
+ if (this.hasPersonalCloudFunction(config.resolved)) {
106
+ if (typeof config.raw.envId !== 'string' || !config.raw.envId.trim()) {
107
+ errors.push({
108
+ level: 'error',
109
+ field: 'envId',
110
+ message: '个人版 TCR 云端构建必须在 cloudbaserc 中显式声明 envId',
111
+ fix: '声明 envId: "{{env.ENV_ID}}",并在 .env* 中设置 ENV_ID',
112
+ });
113
+ return;
114
+ }
115
+ if (typeof config.resolved.envId !== 'string' || !config.resolved.envId.trim()) {
116
+ errors.push({
117
+ level: 'error',
118
+ field: 'envId',
119
+ message: '个人版 TCR 云端构建的 envId 未赋值',
120
+ fix: '在 .env* 中设置 cloudbaserc 通过 {{env.ENV_ID}} 引用的 ENV_ID',
121
+ });
122
+ }
123
+ return;
124
+ }
125
+ if (config.resolved.envId || this.getLinkedEnvId(cwd)) {
126
+ return;
127
+ }
128
+ errors.push({
129
+ level: 'error',
130
+ field: 'envId',
131
+ message: '缺少 envId,且未检测到 .tcb/project.json 关联环境',
132
+ fix: '在 cloudbaserc 中配置 envId,或先执行环境关联流程',
133
+ });
134
+ }
135
+ /**
136
+ * 个人版 TCR 的 username/password/namespace 必须在 cloudbaserc 中显式声明。
137
+ * ConfigParser 已先解析 {{env.*}},因此这里同时承担缺失 env 值的前置校验。
138
+ */
139
+ validatePersonalRegistryConfig(config, errors) {
140
+ ;
141
+ (config.functions || []).forEach((func, index) => {
142
+ var _a;
143
+ if (!this.isPersonalCloudFunction(func)) {
144
+ return;
145
+ }
146
+ const build = (_a = func.imageConfig) === null || _a === void 0 ? void 0 : _a.build;
147
+ const namespace = build === null || build === void 0 ? void 0 : build.namespace;
148
+ if (typeof namespace !== 'string' || !namespace.trim()) {
149
+ errors.push({
150
+ level: 'error',
151
+ resource: `functions[${index}]`,
152
+ field: 'imageConfig.build.namespace',
153
+ message: '个人版 TCR 缺少 namespace,或对应环境变量未赋值',
154
+ fix: '声明 namespace: "{{env.TCB_TCR_NAMESPACE}}",并在 .env* 中设置该变量',
155
+ });
156
+ }
157
+ const credential = build === null || build === void 0 ? void 0 : build.registryCredential;
158
+ if (!credential || typeof credential !== 'object') {
159
+ errors.push({
160
+ level: 'error',
161
+ resource: `functions[${index}]`,
162
+ field: 'imageConfig.build.registryCredential',
163
+ message: '个人版 TCR 缺少 username/password 凭证声明',
164
+ fix: '在 registryCredential 中使用 {{env.TCB_TCR_USERNAME}} 与 {{env.TCB_TCR_PASSWORD}}',
165
+ });
166
+ return;
167
+ }
168
+ const { username, password } = credential;
169
+ if (typeof username !== 'string' || !/^\d{5,20}$/.test(username.trim())) {
170
+ errors.push({
171
+ level: 'error',
172
+ resource: `functions[${index}]`,
173
+ field: 'imageConfig.build.registryCredential.username',
174
+ message: '个人版 TCR username 未赋值或不是有效腾讯云账号 ID',
175
+ fix: '声明 username: "{{env.TCB_TCR_USERNAME}}",并在 .env* 中设置该变量',
176
+ });
177
+ }
178
+ if (typeof password !== 'string' || !password || password.length > 16 * 1024) {
179
+ errors.push({
180
+ level: 'error',
181
+ resource: `functions[${index}]`,
182
+ field: 'imageConfig.build.registryCredential.password',
183
+ message: '个人版 TCR password 未赋值或长度不合法',
184
+ fix: '声明 password: "{{env.TCB_TCR_PASSWORD}}",并在 .env* 中设置该变量',
185
+ });
186
+ }
187
+ });
188
+ }
189
+ hasPersonalCloudFunction(config) {
190
+ return (config.functions || []).some((func) => this.isPersonalCloudFunction(func));
191
+ }
192
+ isPersonalCloudFunction(func) {
193
+ var _a;
194
+ return (func.type === 'HTTP' &&
195
+ func.buildStrategy === 'cloud' &&
196
+ ((_a = func.imageConfig) === null || _a === void 0 ? void 0 : _a.imageType) === 'personal');
197
+ }
198
+ validateResources(cwd, config) {
199
+ const errors = [];
200
+ const warnings = [];
201
+ const functionRoot = config.functionRoot || './functions';
202
+ (config.functions || []).forEach((func, index) => {
203
+ const resource = `functions[${index}]`;
204
+ const functionDir = path_1.default.resolve(cwd, func.dir || path_1.default.join(functionRoot, func.name));
205
+ if (!fs_1.default.existsSync(functionDir)) {
206
+ errors.push({
207
+ level: 'error',
208
+ resource,
209
+ field: 'dir',
210
+ message: `函数目录不存在:${path_1.default.relative(cwd, functionDir)}`,
211
+ fix: '创建函数目录,或修正 functions[].dir / functionRoot 配置',
212
+ });
213
+ return;
214
+ }
215
+ const functionType = func.type || 'Event';
216
+ const buildStrategy = func.buildStrategy || 'zip';
217
+ if (functionType === 'HTTP' && buildStrategy === 'zip') {
218
+ const bootstrapPath = path_1.default.join(functionDir, 'scf_bootstrap');
219
+ if (!fs_1.default.existsSync(bootstrapPath)) {
220
+ errors.push({
221
+ level: 'error',
222
+ resource,
223
+ field: 'scf_bootstrap',
224
+ message: '缺少 scf_bootstrap 文件(HTTP 函数 zip 部署必须)',
225
+ fix: '在函数目录中新增 scf_bootstrap,并确保其具备执行权限',
226
+ });
227
+ }
228
+ }
229
+ if (functionType === 'Event') {
230
+ const handlerFile = `${(func.handler || 'index.main').split('.')[0]}.js`;
231
+ if (!fs_1.default.existsSync(path_1.default.join(functionDir, handlerFile))) {
232
+ warnings.push({
233
+ level: 'warning',
234
+ resource,
235
+ field: 'handler',
236
+ message: `未找到入口文件 ${handlerFile}`,
237
+ fix: '确认 handler 配置与函数入口文件一致',
238
+ });
239
+ }
240
+ }
241
+ });
242
+ (config.hosting || []).forEach((hosting, index) => {
243
+ const root = path_1.default.resolve(cwd, hosting.root || '.');
244
+ if (!fs_1.default.existsSync(root)) {
245
+ errors.push({
246
+ level: 'error',
247
+ resource: `hosting[${index}]`,
248
+ field: 'root',
249
+ message: `静态托管根目录不存在:${path_1.default.relative(cwd, root)}`,
250
+ fix: '创建目录,或修正 hosting[].root 配置',
251
+ });
252
+ }
253
+ });
254
+ if (config.database) {
255
+ const migrations = config.database.migrations || './cloudbase/migrations';
256
+ if (path_1.default.isAbsolute(migrations) || migrations.startsWith('/')) {
257
+ errors.push({
258
+ level: 'error',
259
+ resource: 'database',
260
+ field: 'migrations',
261
+ message: 'database.migrations 仅支持相对路径',
262
+ fix: '请使用相对路径,例如 ./cloudbase/migrations',
263
+ });
264
+ }
265
+ else {
266
+ const migrationsDir = path_1.default.resolve(cwd, migrations);
267
+ if (!fs_1.default.existsSync(migrationsDir)) {
268
+ errors.push({
269
+ level: 'error',
270
+ resource: 'database',
271
+ field: 'migrations',
272
+ message: `数据库迁移目录不存在:${path_1.default.relative(cwd, migrationsDir)}`,
273
+ fix: '创建迁移目录,或修正 database.migrations 配置',
274
+ });
275
+ }
276
+ }
277
+ }
278
+ return { errors, warnings };
279
+ }
280
+ validateConsistency(config, errors) {
281
+ this.validateUniqueNames('functions', config.functions || [], errors);
282
+ this.validateUniqueNames('hosting', config.hosting || [], errors);
283
+ this.validateUniqueField('hosting', config.hosting || [], { field: 'deployPath', errors });
284
+ this.validateGatewayTargets(config, errors);
285
+ }
286
+ validateUniqueNames(resource, items, errors) {
287
+ const names = new Map();
288
+ items.forEach((item, index) => {
289
+ if (!item.name)
290
+ return;
291
+ const prev = names.get(item.name);
292
+ if (typeof prev === 'number') {
293
+ errors.push({
294
+ level: 'error',
295
+ resource: `${resource}[${index}]`,
296
+ field: 'name',
297
+ message: `${resource} 名称重复:${item.name}`,
298
+ fix: `修改 ${resource}[${index}].name,避免与 ${resource}[${prev}].name 重复`,
299
+ });
300
+ }
301
+ else {
302
+ names.set(item.name, index);
303
+ }
304
+ });
305
+ }
306
+ validateUniqueField(resource, items, options) {
307
+ const { field, errors } = options;
308
+ const values = new Map();
309
+ items.forEach((item, index) => {
310
+ const value = item[field];
311
+ if (!value)
312
+ return;
313
+ const prev = values.get(value);
314
+ if (typeof prev === 'number') {
315
+ errors.push({
316
+ level: 'error',
317
+ resource: `${resource}[${index}]`,
318
+ field,
319
+ message: `${resource}.${field} 配置重复:${value}`,
320
+ fix: `修改 ${resource}[${index}].${field},避免与 ${resource}[${prev}].${field} 重复`,
321
+ });
322
+ }
323
+ else {
324
+ values.set(value, index);
325
+ }
326
+ });
327
+ }
328
+ validateGatewayTargets(config, errors) {
329
+ var _a;
330
+ const functionNames = new Set((config.functions || []).map((item) => item.name));
331
+ const hostingNames = new Set((config.hosting || []).map((item) => item.name));
332
+ (((_a = config.gateway) === null || _a === void 0 ? void 0 : _a.routes) || []).forEach((route, index) => {
333
+ const [type, name] = route.target.split(':');
334
+ if ((type === 'function' && !functionNames.has(name)) || (type === 'hosting' && !hostingNames.has(name))) {
335
+ errors.push({
336
+ level: 'error',
337
+ resource: `gateway.routes[${index}]`,
338
+ field: 'target',
339
+ message: `网关目标不存在:${route.target}`,
340
+ fix: '确认 gateway.routes[].target 引用了已声明的 function 或 hosting 资源',
341
+ });
342
+ }
343
+ });
344
+ }
345
+ getSummary(config) {
346
+ var _a, _b, _c, _d;
347
+ return {
348
+ functions: ((_a = config === null || config === void 0 ? void 0 : config.functions) === null || _a === void 0 ? void 0 : _a.length) || 0,
349
+ hosting: ((_b = config === null || config === void 0 ? void 0 : config.hosting) === null || _b === void 0 ? void 0 : _b.length) || 0,
350
+ databases: (config === null || config === void 0 ? void 0 : config.database) ? 1 : 0,
351
+ gateways: ((_d = (_c = config === null || config === void 0 ? void 0 : config.gateway) === null || _c === void 0 ? void 0 : _c.routes) === null || _d === void 0 ? void 0 : _d.length) || 0,
352
+ };
353
+ }
354
+ getLinkedEnvId(cwd) {
355
+ const projectPath = path_1.default.join(cwd, '.tcb', 'project.json');
356
+ if (!fs_1.default.existsSync(projectPath)) {
357
+ return '';
358
+ }
359
+ try {
360
+ const project = JSON.parse(fs_1.default.readFileSync(projectPath, 'utf8'));
361
+ return project.envId || project.EnvId || '';
362
+ }
363
+ catch (e) {
364
+ return '';
365
+ }
366
+ }
367
+ normalizeSchemaPath(dataPath) {
368
+ return dataPath.replace(/^\//, '').replace(/\//g, '.');
369
+ }
370
+ getErrorMessage(error) {
371
+ return (error === null || error === void 0 ? void 0 : error.message) || String(error);
372
+ }
373
+ }
374
+ exports.ProjectValidator = ProjectValidator;
375
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1046,20 +1046,19 @@ class StorageService {
1046
1046
  * 配置文档
1047
1047
  */
1048
1048
  async putBucketWebsite(options) {
1049
- const { indexDocument, errorDocument, bucket, region, routingRules } = options;
1049
+ const { indexDocument, errorDocument, originalHttpStatus, charity404, autoAddressing, bucket, region, routingRules } = options;
1050
1050
  const cos = this.getCos();
1051
1051
  const putBucketWebsite = util_1.default.promisify(cos.putBucketWebsite).bind(cos);
1052
1052
  let params = {
1053
1053
  Bucket: bucket,
1054
1054
  Region: region,
1055
- WebsiteConfiguration: {
1056
- IndexDocument: {
1055
+ WebsiteConfiguration: Object.assign(Object.assign(Object.assign({}, (autoAddressing ? { AutoAddressing: { Status: autoAddressing } } : {})), { IndexDocument: {
1057
1056
  Suffix: indexDocument
1058
- },
1059
- ErrorDocument: {
1060
- Key: errorDocument
1057
+ } }), (errorDocument !== undefined
1058
+ ? {
1059
+ ErrorDocument: Object.assign(Object.assign({ Key: errorDocument }, (originalHttpStatus ? { OriginalHttpStatus: originalHttpStatus } : {})), (charity404 ? { Charity404: charity404 } : {}))
1061
1060
  }
1062
- }
1061
+ : {}))
1063
1062
  };
1064
1063
  if (routingRules) {
1065
1064
  params.WebsiteConfiguration.RoutingRules = [];
@@ -34,6 +34,7 @@ const archiver_1 = __importDefault(require("archiver"));
34
34
  const crypto_1 = __importDefault(require("crypto"));
35
35
  const fs_extra_1 = __importDefault(require("fs-extra"));
36
36
  const lodash_1 = __importDefault(require("lodash"));
37
+ const os_1 = __importDefault(require("os"));
37
38
  const path_1 = __importDefault(require("path"));
38
39
  const unzipper_1 = __importDefault(require("unzipper"));
39
40
  const constant_1 = require("../constant");
@@ -47,17 +48,37 @@ __exportStar(require("./http-request"), exports);
47
48
  var uuid_1 = require("./uuid");
48
49
  Object.defineProperty(exports, "guid6", { enumerable: true, get: function () { return uuid_1.guid6; } });
49
50
  async function compressToZip(option) {
50
- const { dirPath, outputPath, ignore, pattern = '**/*' } = option;
51
+ const { dirPath, outputPath, ignore, pattern = '**/*', extraEntries } = option;
52
+ // 额外注入的文件先落地到临时目录,用 archive.file() 追加,
53
+ // 使 archiver 正确计算 CRC/size(archiver 3.x 的 append(buffer/string)
54
+ // 会写出 CRC=0 的流式条目,被严格解压器判为 bad CRC)。
55
+ let extraTmpDir;
56
+ if (extraEntries && extraEntries.length > 0) {
57
+ extraTmpDir = path_1.default.join(os_1.default.tmpdir(), `zip-extra-${Date.now()}-${Math.random().toString(36).slice(2)}`);
58
+ fs_extra_1.default.mkdirpSync(extraTmpDir);
59
+ }
51
60
  return new Promise((resolve, reject) => {
61
+ const cleanupExtra = () => {
62
+ if (extraTmpDir) {
63
+ try {
64
+ fs_extra_1.default.removeSync(extraTmpDir);
65
+ }
66
+ catch (_a) {
67
+ // 清理临时目录失败不影响主流程
68
+ }
69
+ }
70
+ };
52
71
  const output = fs_extra_1.default.createWriteStream(outputPath);
53
72
  const archive = (0, archiver_1.default)('zip');
54
73
  output.on('close', function () {
74
+ cleanupExtra();
55
75
  resolve({
56
76
  zipPath: outputPath,
57
77
  size: Math.ceil(archive.pointer() / 1024)
58
78
  });
59
79
  });
60
80
  archive.on('error', function (err) {
81
+ cleanupExtra();
61
82
  reject(err);
62
83
  });
63
84
  archive.pipe(output);
@@ -68,6 +89,25 @@ async function compressToZip(option) {
68
89
  ignore: ignore,
69
90
  dot: true
70
91
  });
92
+ // 追加 SDK 注入的额外文件(如构建推送脚本),不污染用户源码目录
93
+ if (extraEntries && extraEntries.length > 0 && extraTmpDir) {
94
+ try {
95
+ extraEntries.forEach((entry, index) => {
96
+ const tmpFile = path_1.default.join(extraTmpDir, `entry-${index}`);
97
+ const buf = Buffer.isBuffer(entry.content)
98
+ ? entry.content
99
+ : Buffer.from(entry.content, 'utf8');
100
+ fs_extra_1.default.writeFileSync(tmpFile, buf, { mode: entry.mode });
101
+ archive.file(tmpFile, { name: entry.zipPath, mode: entry.mode });
102
+ });
103
+ }
104
+ catch (error) {
105
+ cleanupExtra();
106
+ archive.abort();
107
+ reject(error);
108
+ return;
109
+ }
110
+ }
71
111
  archive.finalize();
72
112
  });
73
113
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cloudbase/manager-node",
3
- "version": "5.6.8",
3
+ "version": "5.7.0-beta.0",
4
4
  "description": "The node manage service api for cloudbase.",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {
@@ -42,6 +42,7 @@
42
42
  },
43
43
  "dependencies": {
44
44
  "@cloudbase/signature-nodejs": "^1.0.0",
45
+ "@cloudbase/toolbox": "^0.8.3-beta.0",
45
46
  "@cloudbase/database": "^0.6.2",
46
47
  "archiver": "^3.1.1",
47
48
  "cos-nodejs-sdk-v5": "^2.14.0",
@@ -57,6 +57,10 @@ export declare class CloudAppService {
57
57
  * 注意:此方法不依赖应用是否已创建,可在创建应用前调用
58
58
  * 返回的 cosTimestamp 用于 createApp 时传入 staticConfig.cosTimestamp
59
59
  *
60
+ * 内置默认忽略:.git 目录、.DS_Store(含任意层级);node_modules 仅在
61
+ * excludeNodeModules 为 true(默认)时排除——云端构建(需云端 install)场景应传
62
+ * excludeNodeModules: false 允许私有/离线依赖随源码包上传。
63
+ *
60
64
  * @param params 上传参数
61
65
  */
62
66
  uploadCode(params: IUploadCloudAppCodeParams): Promise<IUploadCloudAppCodeResult>;
@@ -10,19 +10,80 @@
10
10
  * 部署类型
11
11
  * - static-hosting: 静态托管部署
12
12
  * - http-function: HTTP 云函数部署(预留)
13
+ * - custom: 自定义构建(CloudApp custom:源码 ZIP → 容器内执行 CustomSteps → docker build/push TCR),
14
+ * 用于 HTTP 函数镜像的云端构建(buildStrategy: cloud)
13
15
  */
14
- export type DeployType = 'static-hosting' | 'http-function';
16
+ export type DeployType = 'static-hosting' | 'http-function' | 'custom';
15
17
  /**
16
18
  * 构建类型
17
19
  * - GIT: 从 Git 仓库构建
18
20
  * - ZIP: 从 ZIP 包构建
19
21
  * - TEMPLATE: 从模板构建
22
+ * - zip: custom 构建的源码 ZIP(与 API 文档 BuildType 大小写保持一致)
20
23
  */
21
- export type BuildType = 'GIT' | 'ZIP' | 'TEMPLATE';
24
+ export type BuildType = 'GIT' | 'ZIP' | 'TEMPLATE' | 'zip' | 'git';
25
+ /**
26
+ * custom 构建源码来源
27
+ *对应 API 的 Source 字段,zip 模式回填 DescribeCloudAppCosInfo 返回的时间戳
28
+ */
29
+ export interface ICustomBuildSource {
30
+ /** 源码类型,当前实现为 zip */
31
+ type: 'zip' | 'git';
32
+ /** COS 时间戳,原样使用 DescribeCloudAppCosInfo 返回的 UnixTimestamp */
33
+ cosTimestamp?: string;
34
+ /** COS 文件后缀,默认 .zip */
35
+ cosSuffix?: string;
36
+ }
37
+ /** custom 构建环境变量项(明文,禁止写入敏感值) */
38
+ export interface ICustomBuildEnv {
39
+ key: string;
40
+ value: string;
41
+ }
42
+ /**
43
+ * custom 构建密钥项
44
+ * CloudApp 在构建容器中按 $SECRET_{NAME} 注入,Value 不应进入日志或普通环境变量。
45
+ */
46
+ export interface ICustomBuildSecret {
47
+ name: string;
48
+ value: string;
49
+ }
50
+ /**
51
+ * custom 构建自定义步骤
52
+ * SDK 固定生成模板,用户不可控字段绝不作为任意 shell 片段拼接
53
+ */
54
+ export interface ICustomBuildStep {
55
+ name: string;
56
+ command: string;
57
+ }
58
+ /**
59
+ * custom 构建单步状态(API 响应,字段大小写与响应一致)
60
+ * 注意:Duration 为字符串、Status 为小写
61
+ */
62
+ export interface CloudAppBuildStep {
63
+ Name: string;
64
+ Status: string;
65
+ Duration?: string;
66
+ /** 失败时的退出码,用于定位 */
67
+ ExitCode?: number;
68
+ }
69
+ /**
70
+ * custom 构建产物(API 响应)
71
+ * 镜像构建成功后Type 通常为 image
72
+ */
73
+ export interface CloudAppArtifact {
74
+ Type: string;
75
+ Name: string;
76
+ Status?: string;
77
+ /** 部分场景直接回传完整镜像地址 */
78
+ ImageUri?: string;
79
+ /** 部分场景回传 digest */
80
+ Digest?: string;
81
+ }
22
82
  /**
23
83
  * 构建状态
84
+ * custom 构建终态为 SUCCESS / FAILED / CANCELED
24
85
  */
25
- export type BuildStatus = 'BUILDING' | 'SUCCESS' | 'FAILED';
86
+ export type BuildStatus = 'BUILDING' | 'SUCCESS' | 'FAILED' | 'CANCELED';
26
87
  /**
27
88
  * 构建命令配置(入参)
28
89
  */
@@ -149,14 +210,24 @@ export interface CloudAppVersion {
149
210
  export interface ICreateCloudAppParams {
150
211
  /** 环境 ID(可选,默认从 Environment 获取) */
151
212
  envId?: string;
152
- /** 部署类型:static-hosting / http-function */
213
+ /** 部署类型:static-hosting / http-function / custom */
153
214
  deployType: DeployType;
154
215
  /** 服务名称 */
155
216
  serviceName: string;
156
- /** 构建类型:GIT / ZIP / TEMPLATE */
217
+ /** 构建类型:GIT / ZIP / TEMPLATE / zip / git */
157
218
  buildType?: BuildType;
158
219
  /** 静态托管配置(deployType 为 static-hosting 时使用) */
159
220
  staticConfig?: IStaticConfig;
221
+ /** custom 构建源码来源(deployType 为 custom 时使用) */
222
+ source?: ICustomBuildSource;
223
+ /** custom 构建环境变量(deployType 为 custom 时使用,禁止敏感值) */
224
+ env?: ICustomBuildEnv[];
225
+ /** custom 构建密钥(deployType 为 custom 时使用) */
226
+ secrets?: ICustomBuildSecret[];
227
+ /** custom 构建自定义步骤(deployType 为 custom 时使用,SDK 固定模板) */
228
+ customSteps?: ICustomBuildStep[];
229
+ /** 跳过云端构建(纯静态直传场景:产物已直传,仅写部署记录,对应 API SkipBuild) */
230
+ skipBuild?: boolean;
160
231
  }
161
232
  export interface ICreateCloudAppResult {
162
233
  ServiceName: string;
@@ -232,6 +303,10 @@ export interface IDescribeCloudAppVersionResult {
232
303
  Framework?: string;
233
304
  StaticConfig?: CloudAppStaticConfig;
234
305
  BuildTime?: string;
306
+ /** custom 构建的分步状态(deployType 为 custom 时返回) */
307
+ Steps?: CloudAppBuildStep[] | null;
308
+ /** custom 构建产物(deployType 为 custom 时返回,镜像信息优先从此获取) */
309
+ Artifacts?: CloudAppArtifact[] | null;
235
310
  RequestId: string;
236
311
  }
237
312
  export interface IDescribeCloudAppVersionListParams {
@@ -324,8 +399,14 @@ export interface IUploadCloudAppCodeParams {
324
399
  serviceName: string;
325
400
  /** 本地文件夹路径 */
326
401
  localPath: string;
327
- /** 忽略的文件/目录模式 */
402
+ /** 忽略的文件/目录模式(追加到内置默认规则之后) */
328
403
  ignore?: string[];
404
+ /**
405
+ * 是否排除 node_modules:
406
+ * 默认 true(静态产物 / 版本快照场景,避免误传大目录);
407
+ * 云端构建(需云端 install)场景设为 false,允许私有/离线依赖随源码包上传
408
+ */
409
+ excludeNodeModules?: boolean;
329
410
  /** 进度回调 */
330
411
  onProgress?: (progress: IProgressData) => void;
331
412
  /** 分步计时回调(各阶段完成时触发,用于 verbose 模式输出耗时) */
@@ -1,6 +1,6 @@
1
1
  import { Environment } from '../environment';
2
2
  import { IResponseInfo } from '../interfaces';
3
- import { CloudrunServerType, ICloudrunDetailResponse, ICloudrunListResponse, ICloudrunServerBaseConfig, ICreateVpcInfo, IBuildLogResponse, IProcessLogResponse, IDiffConfigItem, ITemplate, ReleaseTypeEnum, IServerManageTaskInfo, IDescribeReleaseOrderParams, IDescribeReleaseOrderResponse, IDescribeCloudRunDeployRecordResponse, IDescribeVersionDetailParams, IDescribeVersionDetailResponse, ISubmitServerRollbackParams, ISubmitServerRollbackResponse, IDeleteCloudRunVersionsParams, IDeleteCloudRunVersionsResponse } from './type';
3
+ import { CloudrunServerType, ICloudrunDetailResponse, ICloudrunListResponse, ICloudrunServerBaseConfig, ICreateVpcInfo, IBuildLogResponse, IProcessLogResponse, IDiffConfigItem, ITemplate, ReleaseTypeEnum, IServerManageTaskInfo, IDescribeCloudRunDeployRecordResponse, IDescribeVersionDetailParams, IDescribeVersionDetailResponse, ISubmitServerRollbackParams, ISubmitServerRollbackResponse, IDeleteCloudRunVersionsParams, IDeleteCloudRunVersionsResponse } from './type';
4
4
  /**
5
5
  * 云托管服务管理类
6
6
  * 提供云托管服务的初始化、下载、列表查询和删除等功能
@@ -58,12 +58,6 @@ export declare class CloudRunService {
58
58
  Task?: IServerManageTaskInfo;
59
59
  RequestId?: string;
60
60
  }>;
61
- /**
62
- * 查询发布单信息(DescribeReleaseOrder)
63
- * @param {IDescribeReleaseOrderParams} params 查询参数
64
- * @returns {Promise<IDescribeReleaseOrderResponse>} 发布单详情
65
- */
66
- describeReleaseOrder(params: IDescribeReleaseOrderParams): Promise<IDescribeReleaseOrderResponse>;
67
61
  /**
68
62
  * 获取云托管服务列表
69
63
  * @param {Object} [params] 查询参数