@cloudbase/manager-node 5.8.1 → 5.8.3
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/CHANGELOG.md +12 -0
- package/lib/context.js +2 -1
- package/lib/database/index.js +11 -6
- package/lib/deploy/DeployOrchestrator.js +30 -16
- package/lib/deploy/FunctionDeployer.js +269 -39
- package/lib/deploy/StaticDeployer.js +7 -1
- package/lib/deploy/function/builders/cloud.js +45 -348
- package/lib/deploy/function/cloud-build-service.js +2 -16
- package/lib/deploy/function/config-guard.js +42 -32
- package/lib/deploy/function/enterprise-tcr.js +194 -0
- package/lib/deploy/function/image-tag.js +31 -0
- package/lib/deploy/function/local-builder.js +170 -25
- package/lib/deploy/function/personal-tcr.js +153 -0
- package/lib/deploy/types.js +11 -0
- package/lib/hosting/index.js +27 -0
- package/lib/index.js +4 -3
- package/lib/projectValidator/index.js +22 -20
- package/lib/utils/cloud-api-request.js +26 -2
- package/package.json +2 -2
- package/types/context.d.ts +17 -3
- package/types/database/index.d.ts +1 -0
- package/types/deploy/DeployOrchestrator.d.ts +10 -0
- package/types/deploy/FunctionDeployer.d.ts +20 -2
- package/types/deploy/StaticDeployer.d.ts +11 -0
- package/types/deploy/function/builders/cloud.d.ts +6 -39
- package/types/deploy/function/enterprise-tcr.d.ts +32 -0
- package/types/deploy/function/image-tag.d.ts +10 -0
- package/types/deploy/function/local-builder.d.ts +35 -6
- package/types/deploy/function/personal-tcr.d.ts +35 -0
- package/types/deploy/types.d.ts +34 -8
- package/types/hosting/index.d.ts +13 -0
- package/types/index.d.ts +5 -0
- package/types/projectValidator/index.d.ts +3 -2
- package/types/utils/cloud-api-request.d.ts +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## @cloudbase/manager-node v5.8.2 版本发布 🚀(2026-08-25)
|
|
2
|
+
|
|
3
|
+
- 新增 support requestFn injection to bypass TC3 signing
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## @cloudbase/manager-node v5.8.1 版本发布 🚀(2026-08-21)
|
|
8
|
+
|
|
9
|
+
- 新增 支持一致性发布
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
1
13
|
## @cloudbase/manager-node v5.8.0 版本发布 🚀(2026-08-20)
|
|
2
14
|
|
|
3
15
|
- 新增 声明式部署编排(hosting 本地构建/gateway 域名绑定/certId 自动查询)
|
package/lib/context.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CloudBaseContext = void 0;
|
|
4
4
|
const constant_1 = require("./constant");
|
|
5
5
|
class CloudBaseContext {
|
|
6
|
-
constructor({ secretId = '', secretKey = '', token = '', proxy = '', region = '', envType = '', useInternalEndpoint = undefined, internalEndpointRegion = undefined }) {
|
|
6
|
+
constructor({ secretId = '', secretKey = '', token = '', proxy = '', region = '', envType = '', useInternalEndpoint = undefined, internalEndpointRegion = undefined, requestFn = undefined }) {
|
|
7
7
|
this.secretId = secretId;
|
|
8
8
|
this.secretKey = secretKey;
|
|
9
9
|
this.token = token;
|
|
@@ -12,6 +12,7 @@ class CloudBaseContext {
|
|
|
12
12
|
this.envType = envType;
|
|
13
13
|
this.useInternalEndpoint = useInternalEndpoint;
|
|
14
14
|
this.internalEndpointRegion = internalEndpointRegion;
|
|
15
|
+
this.requestFn = requestFn;
|
|
15
16
|
}
|
|
16
17
|
isInternalEndpoint() {
|
|
17
18
|
return this.useInternalEndpoint !== undefined ? this.useInternalEndpoint : constant_1.USE_INTERNAL_ENDPOINT;
|
package/lib/database/index.js
CHANGED
|
@@ -40,17 +40,13 @@ class DatabaseService {
|
|
|
40
40
|
return this.environment;
|
|
41
41
|
}
|
|
42
42
|
getDatabaseConfig() {
|
|
43
|
-
const currEnv = this.environment;
|
|
44
|
-
const { Databases } = currEnv.lazyEnvironmentConfig;
|
|
45
43
|
return {
|
|
46
|
-
Tag:
|
|
44
|
+
Tag: this.getDefaultDatabaseInstanceId()
|
|
47
45
|
};
|
|
48
46
|
}
|
|
49
47
|
getMongoConnector() {
|
|
50
|
-
const currEnv = this.environment;
|
|
51
|
-
const { Databases } = currEnv.lazyEnvironmentConfig;
|
|
52
48
|
return {
|
|
53
|
-
DatabaseName:
|
|
49
|
+
DatabaseName: this.getDefaultDatabaseInstanceId(),
|
|
54
50
|
InstanceId: 'flexdb'
|
|
55
51
|
};
|
|
56
52
|
}
|
|
@@ -500,6 +496,15 @@ class DatabaseService {
|
|
|
500
496
|
TaskId
|
|
501
497
|
});
|
|
502
498
|
}
|
|
499
|
+
getDefaultDatabaseInstanceId() {
|
|
500
|
+
var _a, _b, _c;
|
|
501
|
+
const currEnv = this.environment;
|
|
502
|
+
const instanceId = (_c = (_b = (_a = currEnv.lazyEnvironmentConfig) === null || _a === void 0 ? void 0 : _a.Databases) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.InstanceId;
|
|
503
|
+
if (!instanceId) {
|
|
504
|
+
throw new error_1.CloudBaseError('当前环境未开通文档型数据库');
|
|
505
|
+
}
|
|
506
|
+
return instanceId;
|
|
507
|
+
}
|
|
503
508
|
}
|
|
504
509
|
exports.DatabaseService = DatabaseService;
|
|
505
510
|
DatabaseService.tcbServiceVersion = {
|
|
@@ -44,8 +44,8 @@ class DeployOrchestrator {
|
|
|
44
44
|
* - database 失败始终强制中断(无论是否 continueOnError)
|
|
45
45
|
*/
|
|
46
46
|
async deploy(options) {
|
|
47
|
-
var _a, _b, _c, _d, _e, _f, _g, _h
|
|
48
|
-
const { config, envId, dryRun, cwd = process.cwd(), log, concurrency =
|
|
47
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
48
|
+
const { config, envId, dryRun, cwd = process.cwd(), log, concurrency = 5, continueOnError = false } = options;
|
|
49
49
|
// 1. 轻量 inline 守卫(validateProject 未接入时的兜底)
|
|
50
50
|
this.guardConfig(config);
|
|
51
51
|
const plan = await this.buildPlan(config, options);
|
|
@@ -119,8 +119,25 @@ class DeployOrchestrator {
|
|
|
119
119
|
const tasks = toExecute.map((item) => async () => {
|
|
120
120
|
var _a, _b, _c, _d, _e;
|
|
121
121
|
try {
|
|
122
|
-
const stepResult = await this.executeStep({
|
|
123
|
-
|
|
122
|
+
const stepResult = await this.executeStep({
|
|
123
|
+
item,
|
|
124
|
+
config,
|
|
125
|
+
envId,
|
|
126
|
+
cwd,
|
|
127
|
+
onProgress: options.onProgress
|
|
128
|
+
});
|
|
129
|
+
if (item.type === 'functions') {
|
|
130
|
+
results.push({
|
|
131
|
+
type: item.type,
|
|
132
|
+
name: item.name,
|
|
133
|
+
ok: true,
|
|
134
|
+
url: stepResult === null || stepResult === void 0 ? void 0 : stepResult.url,
|
|
135
|
+
detail: stepResult
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
results.push({ type: item.type, name: item.name, ok: true, url: stepResult });
|
|
140
|
+
}
|
|
124
141
|
}
|
|
125
142
|
catch (e) {
|
|
126
143
|
const message = (e === null || e === void 0 ? void 0 : e.message) || String(e);
|
|
@@ -130,8 +147,10 @@ class DeployOrchestrator {
|
|
|
130
147
|
((_b = e === null || e === void 0 ? void 0 : e.original) === null || _b === void 0 ? void 0 : _b.RequestId) ||
|
|
131
148
|
((_c = e === null || e === void 0 ? void 0 : e.response) === null || _c === void 0 ? void 0 : _c.requestId) ||
|
|
132
149
|
((_d = e === null || e === void 0 ? void 0 : e.response) === null || _d === void 0 ? void 0 : _d.RequestId);
|
|
133
|
-
(
|
|
134
|
-
|
|
150
|
+
if (!(item.type === 'functions' && options.onProgress)) {
|
|
151
|
+
(_e = log === null || log === void 0 ? void 0 : log.error) === null || _e === void 0 ? void 0 : _e.call(log, `[${item.type}] ${item.name} 部署失败: ${message}`);
|
|
152
|
+
}
|
|
153
|
+
results.push(Object.assign(Object.assign(Object.assign({ type: item.type, name: item.name, ok: false, error: message }, ((e === null || e === void 0 ? void 0 : e.code) ? { errorCode: e.code } : {})), ((e === null || e === void 0 ? void 0 : e.failedStage) ? { failedStage: e.failedStage } : {})), (requestId ? { requestId } : {})));
|
|
135
154
|
segmentFailed = true;
|
|
136
155
|
if (item.type === 'database') {
|
|
137
156
|
segmentDbFailed = true;
|
|
@@ -147,7 +166,6 @@ class DeployOrchestrator {
|
|
|
147
166
|
return { plan, results };
|
|
148
167
|
}
|
|
149
168
|
if (!continueOnError) {
|
|
150
|
-
(_h = log === null || log === void 0 ? void 0 : log.error) === null || _h === void 0 ? void 0 : _h.call(log, '存在资源部署失败,中断部署');
|
|
151
169
|
return { plan, results };
|
|
152
170
|
}
|
|
153
171
|
}
|
|
@@ -161,7 +179,7 @@ class DeployOrchestrator {
|
|
|
161
179
|
await this.persistState({ config, plan, results, cwd, envId: options.envId });
|
|
162
180
|
}
|
|
163
181
|
catch (e) {
|
|
164
|
-
(
|
|
182
|
+
(_h = log === null || log === void 0 ? void 0 : log.warn) === null || _h === void 0 ? void 0 : _h.call(log, `状态快照写入失败:${(e === null || e === void 0 ? void 0 : e.message) || String(e)}`);
|
|
165
183
|
}
|
|
166
184
|
}
|
|
167
185
|
}
|
|
@@ -566,7 +584,7 @@ class DeployOrchestrator {
|
|
|
566
584
|
* 执行单个计划步骤
|
|
567
585
|
*/
|
|
568
586
|
async executeStep(options) {
|
|
569
|
-
const { item, config, envId, cwd } = options;
|
|
587
|
+
const { item, config, envId, cwd, onProgress } = options;
|
|
570
588
|
switch (item.type) {
|
|
571
589
|
case 'database': {
|
|
572
590
|
const dbConfig = config.database;
|
|
@@ -585,14 +603,10 @@ class DeployOrchestrator {
|
|
|
585
603
|
const functionPath = fn.dir
|
|
586
604
|
? path_1.default.resolve(cwd, fn.dir)
|
|
587
605
|
: path_1.default.join(functionRootPath, fn.name);
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
functionRootPath,
|
|
592
|
-
functionPath,
|
|
593
|
-
force: true
|
|
606
|
+
const deployConfig = Object.assign(Object.assign({}, fn), { functionPath });
|
|
607
|
+
return this.functionDeployer.deployFunction(deployConfig, {
|
|
608
|
+
onProgress: event => onProgress === null || onProgress === void 0 ? void 0 : onProgress(Object.assign(Object.assign({}, event), { resourceType: 'functions', resourceName: item.name }))
|
|
594
609
|
});
|
|
595
|
-
return result.url;
|
|
596
610
|
}
|
|
597
611
|
case 'app': {
|
|
598
612
|
const result = await this.staticDeployer.deployApp({ config: config.app, cwd });
|
|
@@ -26,6 +26,8 @@ const GatewayDeployer_1 = require("./GatewayDeployer");
|
|
|
26
26
|
const domain_1 = require("./domain");
|
|
27
27
|
const image_preflight_1 = require("./function/image-preflight");
|
|
28
28
|
const local_builder_1 = require("./function/local-builder");
|
|
29
|
+
const enterprise_tcr_1 = require("./function/enterprise-tcr");
|
|
30
|
+
const personal_tcr_1 = require("./function/personal-tcr");
|
|
29
31
|
const planner_1 = require("./function/planner");
|
|
30
32
|
const preflight_1 = require("./function/preflight");
|
|
31
33
|
const types_1 = require("./types");
|
|
@@ -41,16 +43,21 @@ const AUTHZ_USER_REGO_KEY = 'authz.user.rego';
|
|
|
41
43
|
*
|
|
42
44
|
* 已支持代码包、已有镜像、云端构建、本地构建、匿名访问与网关路径收敛;
|
|
43
45
|
* 函数类型、运行时或协议等创建期字段变化时不会自动删除函数,而是引导用户
|
|
44
|
-
* 在控制台确认并手工删除后重新部署。
|
|
46
|
+
* 在控制台确认并手工删除后重新部署。
|
|
45
47
|
*/
|
|
46
48
|
class FunctionDeployer {
|
|
47
49
|
constructor(environment, deps) {
|
|
48
|
-
var _a;
|
|
50
|
+
var _a, _b, _c;
|
|
49
51
|
this.environment = environment;
|
|
50
52
|
this.functionService = environment.getFunctionService();
|
|
51
53
|
this.imagePreflightDeps = deps === null || deps === void 0 ? void 0 : deps.imagePreflightDeps;
|
|
52
54
|
this.cloudBuildServiceFactory =
|
|
53
55
|
(_a = deps === null || deps === void 0 ? void 0 : deps.cloudBuildServiceFactory) !== null && _a !== void 0 ? _a : (() => (0, cloud_build_service_1.createCloudBuildService)(this.environment.getCloudAppService()));
|
|
56
|
+
this.localBuilderDeps = deps === null || deps === void 0 ? void 0 : deps.localBuilderDeps;
|
|
57
|
+
this.tcrServiceFactory =
|
|
58
|
+
(_b = deps === null || deps === void 0 ? void 0 : deps.tcrServiceFactory) !== null && _b !== void 0 ? _b : (() => (0, enterprise_tcr_1.createTcrApiService)(this.environment));
|
|
59
|
+
this.personalTcrServiceFactory =
|
|
60
|
+
(_c = deps === null || deps === void 0 ? void 0 : deps.personalTcrServiceFactory) !== null && _c !== void 0 ? _c : (() => (0, personal_tcr_1.createPersonalTcrService)(this.environment));
|
|
54
61
|
}
|
|
55
62
|
/**
|
|
56
63
|
* 兼容既有声明式部署入口
|
|
@@ -214,27 +221,52 @@ class FunctionDeployer {
|
|
|
214
221
|
steps: ctx.steps,
|
|
215
222
|
warnings,
|
|
216
223
|
startTime,
|
|
217
|
-
dryRun: true
|
|
224
|
+
dryRun: true,
|
|
225
|
+
options
|
|
218
226
|
});
|
|
219
227
|
}
|
|
220
228
|
// 不可原地更新的变更必须在任何可能产生副作用的 Preflight 之前中止。
|
|
221
229
|
// 尤其避免镜像拉取授权自动补齐等云端写操作先发生,再提示用户手工删除函数。
|
|
222
230
|
this.assertPlanExecutable(plan);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
231
|
+
if (normalized.type === 'HTTP' &&
|
|
232
|
+
(normalized.buildStrategy === 'cloud' || normalized.buildStrategy === 'zip')) {
|
|
233
|
+
await this.runLabeledStep(ctx, {
|
|
234
|
+
stage: 'preflight',
|
|
235
|
+
handler: async () => {
|
|
236
|
+
const report = (0, preflight_1.runLocalPreflight)(normalized);
|
|
237
|
+
this.appendWarnings(warnings, report);
|
|
238
|
+
this.assertPreflightReady(report);
|
|
239
|
+
},
|
|
240
|
+
label: normalized.buildStrategy === 'cloud'
|
|
241
|
+
? '检查云端构建上下文'
|
|
242
|
+
: '检查函数代码包'
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
if (normalized.type === 'HTTP' &&
|
|
246
|
+
(normalized.buildStrategy === 'image' ||
|
|
247
|
+
normalized.buildStrategy === 'local' ||
|
|
248
|
+
normalized.buildStrategy === 'cloud')) {
|
|
249
|
+
await this.runLabeledStep(ctx, {
|
|
250
|
+
stage: 'preflight',
|
|
251
|
+
handler: async () => {
|
|
252
|
+
const report = await (0, cam_preflight_1.runImagePullAuthPreflight)(this.createCamPreflightAdapter(), normalized, options.autoGrant !== false);
|
|
253
|
+
this.appendWarnings(warnings, report);
|
|
254
|
+
this.assertPreflightReady(report);
|
|
255
|
+
},
|
|
256
|
+
label: '检查镜像仓库访问权限'
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
if (normalized.type === 'HTTP' && normalized.buildStrategy === 'image') {
|
|
260
|
+
await this.runLabeledStep(ctx, {
|
|
261
|
+
stage: 'preflight',
|
|
262
|
+
handler: async () => {
|
|
263
|
+
const report = await (0, image_preflight_1.runImageRemotePreflight)(normalized, this.resolveDeployRegion(), this.imagePreflightDeps);
|
|
264
|
+
this.appendWarnings(warnings, report);
|
|
265
|
+
this.assertPreflightReady(report);
|
|
266
|
+
},
|
|
267
|
+
label: '检查远端镜像与地域'
|
|
268
|
+
});
|
|
269
|
+
}
|
|
238
270
|
// noop:函数本身无变更,但仍可能需要收敛访问配置
|
|
239
271
|
if (plan.action === 'noop') {
|
|
240
272
|
await this.applyAccessPlan(ctx, normalized, plan);
|
|
@@ -244,7 +276,8 @@ class FunctionDeployer {
|
|
|
244
276
|
steps: ctx.steps,
|
|
245
277
|
warnings,
|
|
246
278
|
startTime,
|
|
247
|
-
dryRun: false
|
|
279
|
+
dryRun: false,
|
|
280
|
+
options
|
|
248
281
|
});
|
|
249
282
|
}
|
|
250
283
|
const artifact = await this.prepareArtifact(ctx, normalized, warnings);
|
|
@@ -257,6 +290,7 @@ class FunctionDeployer {
|
|
|
257
290
|
warnings,
|
|
258
291
|
startTime,
|
|
259
292
|
dryRun: false,
|
|
293
|
+
options,
|
|
260
294
|
artifact
|
|
261
295
|
});
|
|
262
296
|
}
|
|
@@ -310,6 +344,21 @@ class FunctionDeployer {
|
|
|
310
344
|
return undefined;
|
|
311
345
|
}
|
|
312
346
|
}
|
|
347
|
+
resolveEnvironmentId() {
|
|
348
|
+
try {
|
|
349
|
+
return this.environment.getEnvId();
|
|
350
|
+
}
|
|
351
|
+
catch (_a) {
|
|
352
|
+
return '';
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
requireDeployRegion() {
|
|
356
|
+
const region = this.resolveDeployRegion();
|
|
357
|
+
if (!region) {
|
|
358
|
+
throw new error_1.CloudBaseError('企业版 TCR 自动发现需要明确部署地域,请在环境配置中设置 region。', { code: types_1.FUNCTION_DEPLOY_ERROR.TCR_REGION_MISSING });
|
|
359
|
+
}
|
|
360
|
+
return region;
|
|
361
|
+
}
|
|
313
362
|
/**
|
|
314
363
|
* 构造镜像拉取授权 Preflight 所需的 CAM 适配器
|
|
315
364
|
*
|
|
@@ -398,6 +447,64 @@ class FunctionDeployer {
|
|
|
398
447
|
'\n部署器不会自动删除已有云函数。请先在云开发控制台确认触发器、访问配置和服务中断影响,手工删除该函数后重新部署。', { code: types_1.FUNCTION_DEPLOY_ERROR.FUNCTION_RECREATE_REQUIRED });
|
|
399
448
|
}
|
|
400
449
|
}
|
|
450
|
+
async prepareEnterpriseBuildConfig(ctx, config) {
|
|
451
|
+
const repository = config.build.repository || '';
|
|
452
|
+
const hasPreparedTarget = Boolean((config.registryId || config.build.registryId) && repository.split('/').length >= 3);
|
|
453
|
+
if (config.imageType !== 'enterprise' || hasPreparedTarget) {
|
|
454
|
+
return { config };
|
|
455
|
+
}
|
|
456
|
+
const prepared = await this.runLabeledStep(ctx, {
|
|
457
|
+
stage: 'preflight',
|
|
458
|
+
label: '准备企业版 TCR 目标仓库',
|
|
459
|
+
handler: () => (0, enterprise_tcr_1.prepareEnterpriseTcrTarget)(config, {
|
|
460
|
+
envId: this.resolveEnvironmentId(),
|
|
461
|
+
region: this.requireDeployRegion(),
|
|
462
|
+
service: this.tcrServiceFactory()
|
|
463
|
+
})
|
|
464
|
+
});
|
|
465
|
+
return { config: prepared.config, target: prepared.target };
|
|
466
|
+
}
|
|
467
|
+
async preparePersonalBuildConfig(ctx, config) {
|
|
468
|
+
var _a, _b, _c;
|
|
469
|
+
if (config.imageType !== 'personal') {
|
|
470
|
+
return config;
|
|
471
|
+
}
|
|
472
|
+
const envId = this.resolveEnvironmentId();
|
|
473
|
+
const configuredNamespace = (_a = config.build.namespace) === null || _a === void 0 ? void 0 : _a.trim();
|
|
474
|
+
const hasConfiguredRepository = Boolean((_b = config.build.repository) === null || _b === void 0 ? void 0 : _b.trim());
|
|
475
|
+
const configuredRepository = ((_c = config.build.repository) === null || _c === void 0 ? void 0 : _c.trim()) || config.name;
|
|
476
|
+
const parsedRepository = (0, config_guard_1.parseImageReference)(configuredRepository);
|
|
477
|
+
if (!parsedRepository || parsedRepository.tag || parsedRepository.digest) {
|
|
478
|
+
throw new error_1.CloudBaseError('个人版 TCR repository 不合法。', {
|
|
479
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
const repositoryParts = parsedRepository.repository.split('/');
|
|
483
|
+
const repository = repositoryParts[repositoryParts.length - 1];
|
|
484
|
+
const repositoryNamespace = repositoryParts.length > 1 ? repositoryParts.slice(0, -1).join('/') : undefined;
|
|
485
|
+
const namespace = configuredNamespace || repositoryNamespace || envId;
|
|
486
|
+
const hasConfiguredNamespace = Boolean(configuredNamespace || repositoryNamespace);
|
|
487
|
+
const registry = parsedRepository.registry || personal_tcr_1.DEFAULT_PERSONAL_TCR_REGISTRY;
|
|
488
|
+
const credential = config.build.registryCredential;
|
|
489
|
+
if (!(credential === null || credential === void 0 ? void 0 : credential.username) || !credential.password) {
|
|
490
|
+
throw new error_1.CloudBaseError('个人版 TCR 构建缺少推送凭证。', {
|
|
491
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_MISSING
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
await this.runLabeledStep(ctx, {
|
|
495
|
+
stage: 'preflight',
|
|
496
|
+
label: '准备个人版 TCR 目标仓库',
|
|
497
|
+
handler: () => this.personalTcrServiceFactory().ensurePushTarget({
|
|
498
|
+
namespace,
|
|
499
|
+
repository,
|
|
500
|
+
ensureNamespace: !hasConfiguredNamespace,
|
|
501
|
+
ensureRepository: !hasConfiguredRepository
|
|
502
|
+
})
|
|
503
|
+
});
|
|
504
|
+
const targetRepository = `${registry}/${namespace}/${repository}`;
|
|
505
|
+
const preparedConfig = Object.assign(Object.assign({}, config), { build: Object.assign(Object.assign({}, config.build), { namespace, repository: targetRepository, registryCredential: credential }) });
|
|
506
|
+
return preparedConfig;
|
|
507
|
+
}
|
|
401
508
|
/**
|
|
402
509
|
* 准备部署物
|
|
403
510
|
*
|
|
@@ -406,8 +513,22 @@ class FunctionDeployer {
|
|
|
406
513
|
* 其余策略走同步的 pack。
|
|
407
514
|
*/
|
|
408
515
|
async prepareArtifact(ctx, config, warnings) {
|
|
516
|
+
var _a;
|
|
409
517
|
if (config.buildStrategy === 'cloud') {
|
|
410
|
-
const
|
|
518
|
+
const prepared = await this.prepareEnterpriseBuildConfig(ctx, config);
|
|
519
|
+
const cloudConfig = await this.preparePersonalBuildConfig(ctx, prepared.config);
|
|
520
|
+
// 企业版登录凭证在触发 CloudApp 构建前换取,构建容器只消费 Secrets,
|
|
521
|
+
// 不再自行签名调用 TCR API。
|
|
522
|
+
const cloudRegistryId = ((_a = prepared.target) === null || _a === void 0 ? void 0 : _a.registryId) ||
|
|
523
|
+
cloudConfig.build.registryId ||
|
|
524
|
+
cloudConfig.registryId;
|
|
525
|
+
const enterpriseCredential = cloudConfig.imageType === 'enterprise' && cloudRegistryId
|
|
526
|
+
? await this.runLabeledStep(ctx, {
|
|
527
|
+
stage: 'preflight',
|
|
528
|
+
label: '获取企业版 TCR 临时登录凭证',
|
|
529
|
+
handler: () => (0, enterprise_tcr_1.createEnterpriseTcrCredential)(this.tcrServiceFactory(), cloudRegistryId)
|
|
530
|
+
})
|
|
531
|
+
: undefined;
|
|
411
532
|
const buildResult = await this.runStep(ctx, 'build', async () => {
|
|
412
533
|
const cloudBuildService = this.cloudBuildServiceFactory();
|
|
413
534
|
return (0, cloud_1.buildImageOnCloud)(cloudConfig, cloudBuildService, {
|
|
@@ -416,27 +537,99 @@ class FunctionDeployer {
|
|
|
416
537
|
status: 'start',
|
|
417
538
|
message: line
|
|
418
539
|
}),
|
|
419
|
-
|
|
540
|
+
enterpriseCredential
|
|
420
541
|
});
|
|
421
542
|
});
|
|
422
|
-
return (0, artifact_1.createImageArtifactFromCloudBuild)(
|
|
543
|
+
return (0, artifact_1.createImageArtifactFromCloudBuild)(cloudConfig, buildResult);
|
|
423
544
|
}
|
|
424
545
|
if (config.buildStrategy === 'local') {
|
|
425
|
-
await this.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
546
|
+
await this.runLabeledStep(ctx, {
|
|
547
|
+
stage: 'preflight',
|
|
548
|
+
handler: async () => {
|
|
549
|
+
const report = await (0, docker_preflight_1.runDockerPreflight)();
|
|
550
|
+
this.appendWarnings(warnings, report);
|
|
551
|
+
this.assertPreflightReady(report);
|
|
552
|
+
},
|
|
553
|
+
label: '检查本地 Docker 环境'
|
|
429
554
|
});
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
555
|
+
const prepared = await this.prepareEnterpriseBuildConfig(ctx, config);
|
|
556
|
+
const localConfig = await this.preparePersonalBuildConfig(ctx, prepared.config);
|
|
557
|
+
const credential = await this.resolveLocalRegistryCredential(localConfig);
|
|
558
|
+
const dockerConfig = credential ? (0, local_builder_1.createTemporaryDockerConfig)() : undefined;
|
|
559
|
+
const commandOptions = dockerConfig ? { env: dockerConfig.env } : undefined;
|
|
560
|
+
try {
|
|
561
|
+
if (credential) {
|
|
562
|
+
await this.runStep(ctx, 'login', async () => {
|
|
563
|
+
await (0, local_builder_1.loginDockerRegistry)(this.resolveRegistryFromRepository(localConfig.build.repository), credential, { deps: this.localBuilderDeps, commandOptions });
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
const buildResult = await this.runStep(ctx, 'build', async () => {
|
|
567
|
+
return (0, local_builder_1.buildLocalImage)(localConfig, line => this.emitProgress(ctx.options, {
|
|
568
|
+
stage: 'build',
|
|
569
|
+
status: 'start',
|
|
570
|
+
message: line
|
|
571
|
+
}), { deps: this.localBuilderDeps, commandOptions });
|
|
572
|
+
});
|
|
573
|
+
const pushResult = await this.runStep(ctx, 'push', async () => {
|
|
574
|
+
return (0, local_builder_1.pushLocalImage)(buildResult.imageUri, line => this.emitProgress(ctx.options, {
|
|
575
|
+
stage: 'push',
|
|
576
|
+
status: 'start',
|
|
577
|
+
message: line
|
|
578
|
+
}), { deps: this.localBuilderDeps, commandOptions });
|
|
579
|
+
});
|
|
580
|
+
if (localConfig.imageType === 'personal') {
|
|
581
|
+
await this.runLabeledStep(ctx, {
|
|
582
|
+
stage: 'push',
|
|
583
|
+
label: '清理历史镜像标签',
|
|
584
|
+
handler: () => this.personalTcrServiceFactory().cleanupTags({
|
|
585
|
+
namespace: localConfig.build.namespace,
|
|
586
|
+
repository: localConfig.build.repository,
|
|
587
|
+
retain: localConfig.build.retainedTags
|
|
588
|
+
})
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
return (0, artifact_1.createImageArtifactFromBuild)(localConfig, {
|
|
592
|
+
imageUri: buildResult.imageUri,
|
|
593
|
+
imageDigest: pushResult.imageDigest
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
finally {
|
|
597
|
+
dockerConfig === null || dockerConfig === void 0 ? void 0 : dockerConfig.cleanup();
|
|
598
|
+
}
|
|
435
599
|
}
|
|
436
600
|
return this.runStep(ctx, 'pack', async () => {
|
|
437
601
|
return this.createArtifact(config);
|
|
438
602
|
});
|
|
439
603
|
}
|
|
604
|
+
async resolveLocalRegistryCredential(config) {
|
|
605
|
+
var _a, _b, _c;
|
|
606
|
+
const imageType = (_a = config.imageType) !== null && _a !== void 0 ? _a : (((_b = config.registryId) !== null && _b !== void 0 ? _b : config.build.registryId) ? 'enterprise' : 'personal');
|
|
607
|
+
if (imageType === 'enterprise') {
|
|
608
|
+
const registryId = (_c = config.registryId) !== null && _c !== void 0 ? _c : config.build.registryId;
|
|
609
|
+
if (!registryId) {
|
|
610
|
+
throw new error_1.CloudBaseError('企业版 TCR local 构建缺少 registryId。', {
|
|
611
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.IMAGE_REGISTRY_ID_MISSING
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
const credential = await (0, enterprise_tcr_1.createEnterpriseTcrCredential)(this.tcrServiceFactory(), registryId);
|
|
615
|
+
return { username: credential.username, secret: credential.token };
|
|
616
|
+
}
|
|
617
|
+
const credential = config.build.registryCredential;
|
|
618
|
+
if (!credential) {
|
|
619
|
+
return undefined;
|
|
620
|
+
}
|
|
621
|
+
return { username: credential.username, secret: credential.password };
|
|
622
|
+
}
|
|
623
|
+
resolveRegistryFromRepository(repository) {
|
|
624
|
+
const normalized = typeof repository === 'string' ? repository.trim() : '';
|
|
625
|
+
const slashIndex = normalized.indexOf('/');
|
|
626
|
+
if (slashIndex <= 0) {
|
|
627
|
+
throw new error_1.CloudBaseError('local 策略的目标镜像地址缺少仓库域名', {
|
|
628
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.LOCAL_BUILD_REPOSITORY_MISSING
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
return normalized.slice(0, slashIndex);
|
|
632
|
+
}
|
|
440
633
|
/** 生成部署物 */
|
|
441
634
|
createArtifact(config) {
|
|
442
635
|
if (config.buildStrategy === 'image') {
|
|
@@ -499,24 +692,53 @@ class FunctionDeployer {
|
|
|
499
692
|
});
|
|
500
693
|
}
|
|
501
694
|
}
|
|
502
|
-
/**
|
|
695
|
+
/** 执行普通步骤并记录耗时与进度 */
|
|
503
696
|
async runStep(ctx, stage, handler) {
|
|
697
|
+
return this.executeStep(ctx, { stage, handler });
|
|
698
|
+
}
|
|
699
|
+
/** 执行带具体名称的步骤并记录耗时与进度 */
|
|
700
|
+
async runLabeledStep(ctx, options) {
|
|
701
|
+
return this.executeStep(ctx, options);
|
|
702
|
+
}
|
|
703
|
+
async executeStep(ctx, options) {
|
|
704
|
+
var _a, _b, _c, _d, _e;
|
|
705
|
+
const { stage, handler, label } = options;
|
|
504
706
|
const startTime = Date.now();
|
|
505
|
-
this.emitProgress(ctx.options, { stage, status: 'start' });
|
|
707
|
+
this.emitProgress(ctx.options, { stage, label, status: 'start' });
|
|
506
708
|
try {
|
|
507
709
|
const result = await handler();
|
|
508
|
-
|
|
509
|
-
|
|
710
|
+
const elapsedTime = Date.now() - startTime;
|
|
711
|
+
ctx.steps.push({ stage, label, status: 'success', elapsedTime });
|
|
712
|
+
this.emitProgress(ctx.options, { stage, label, status: 'success', elapsedTime });
|
|
510
713
|
return result;
|
|
511
714
|
}
|
|
512
715
|
catch (error) {
|
|
716
|
+
const elapsedTime = Date.now() - startTime;
|
|
513
717
|
ctx.steps.push({
|
|
514
718
|
stage,
|
|
719
|
+
label,
|
|
720
|
+
status: 'failed',
|
|
721
|
+
elapsedTime,
|
|
722
|
+
detail: error === null || error === void 0 ? void 0 : error.message,
|
|
723
|
+
errorCode: error === null || error === void 0 ? void 0 : error.code
|
|
724
|
+
});
|
|
725
|
+
this.emitProgress(ctx.options, {
|
|
726
|
+
stage,
|
|
727
|
+
label,
|
|
515
728
|
status: 'failed',
|
|
516
|
-
|
|
517
|
-
|
|
729
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
730
|
+
elapsedTime,
|
|
731
|
+
errorCode: error === null || error === void 0 ? void 0 : error.code,
|
|
732
|
+
requestId: (error === null || error === void 0 ? void 0 : error.requestId) ||
|
|
733
|
+
(error === null || error === void 0 ? void 0 : error.RequestId) ||
|
|
734
|
+
((_a = error === null || error === void 0 ? void 0 : error.original) === null || _a === void 0 ? void 0 : _a.requestId) ||
|
|
735
|
+
((_b = error === null || error === void 0 ? void 0 : error.original) === null || _b === void 0 ? void 0 : _b.RequestId) ||
|
|
736
|
+
((_d = (_c = error === null || error === void 0 ? void 0 : error.original) === null || _c === void 0 ? void 0 : _c.Response) === null || _d === void 0 ? void 0 : _d.RequestId) ||
|
|
737
|
+
((_e = error === null || error === void 0 ? void 0 : error.response) === null || _e === void 0 ? void 0 : _e.RequestId)
|
|
518
738
|
});
|
|
519
|
-
|
|
739
|
+
if (error && !error.failedStage) {
|
|
740
|
+
error.failedStage = stage;
|
|
741
|
+
}
|
|
520
742
|
throw error;
|
|
521
743
|
}
|
|
522
744
|
}
|
|
@@ -567,11 +789,19 @@ class FunctionDeployer {
|
|
|
567
789
|
return result;
|
|
568
790
|
}
|
|
569
791
|
emitProgressDone(params) {
|
|
792
|
+
const elapsedTime = Date.now() - params.startTime;
|
|
570
793
|
params.steps.push({
|
|
571
794
|
stage: 'done',
|
|
572
795
|
status: 'success',
|
|
573
|
-
elapsedTime
|
|
796
|
+
elapsedTime
|
|
574
797
|
});
|
|
798
|
+
if (params.options) {
|
|
799
|
+
this.emitProgress(params.options, {
|
|
800
|
+
stage: 'done',
|
|
801
|
+
status: 'success',
|
|
802
|
+
elapsedTime
|
|
803
|
+
});
|
|
804
|
+
}
|
|
575
805
|
}
|
|
576
806
|
}
|
|
577
807
|
exports.FunctionDeployer = FunctionDeployer;
|
|
@@ -73,7 +73,13 @@ class StaticDeployer {
|
|
|
73
73
|
const hostingOptions = {
|
|
74
74
|
localPath: outputDir,
|
|
75
75
|
cloudPath,
|
|
76
|
-
ignore: config.ignore || ['.DS_Store']
|
|
76
|
+
ignore: config.ignore || ['.DS_Store'],
|
|
77
|
+
// 声明式配置名为 entryFile,透传到 uploadFiles 时对应 entryFiles
|
|
78
|
+
entryFiles: config.entryFile,
|
|
79
|
+
// 一致性发布开关:任一开启即走一致性上传(uploadDirectoryConsistent)
|
|
80
|
+
verify: config.verify,
|
|
81
|
+
safe: config.safe,
|
|
82
|
+
prune: config.prune
|
|
77
83
|
};
|
|
78
84
|
await this.environment.getHostingService().uploadFiles(hostingOptions);
|
|
79
85
|
const url = await this.resolveHostingUrl(cloudPath);
|