@cloudbase/manager-node 5.8.2 → 5.8.4
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 +15 -0
- package/lib/database/index.js +11 -6
- package/lib/deploy/DeployOrchestrator.js +170 -278
- package/lib/deploy/FunctionDeployer.js +310 -52
- package/lib/deploy/StaticDeployer.js +7 -1
- package/lib/deploy/function/artifact.js +55 -41
- package/lib/deploy/function/builders/cloud.js +58 -359
- package/lib/deploy/function/cam-preflight.js +2 -1
- package/lib/deploy/function/cloud-build-service.js +2 -16
- package/lib/deploy/function/config-guard.js +210 -55
- package/lib/deploy/function/enterprise-tcr.js +195 -0
- package/lib/deploy/function/image-tag.js +31 -0
- package/lib/deploy/function/local-builder.js +178 -32
- package/lib/deploy/function/personal-tcr.js +153 -0
- package/lib/deploy/function/preflight.js +3 -2
- package/lib/deploy/types.js +11 -0
- package/lib/hosting/index.js +27 -0
- package/lib/projectValidator/index.js +22 -23
- package/package.json +2 -2
- package/types/database/index.d.ts +1 -0
- package/types/deploy/DeployOrchestrator.d.ts +19 -25
- package/types/deploy/FunctionDeployer.d.ts +35 -3
- package/types/deploy/StaticDeployer.d.ts +11 -0
- package/types/deploy/function/artifact.d.ts +0 -5
- package/types/deploy/function/builders/cloud.d.ts +6 -39
- package/types/deploy/function/config-guard.d.ts +18 -0
- 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 +110 -28
- package/types/hosting/index.d.ts +13 -0
- package/types/projectValidator/index.d.ts +3 -2
- package/types/projectValidator/types.d.ts +0 -1
- package/lib/deploy/StateStore.js +0 -328
- package/types/deploy/StateStore.d.ts +0 -174
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.IMAGE_PULL_GRANT_URL = exports.REQUIRED_IMAGE_PULL_POLICY = exports.SCF_PULL_IMAGE_ROLE = exports.isCamPermissionError = exports.runImagePullAuthPreflight = exports.runImageRemotePreflight = exports.runLocalPreflight = exports.createCloudBuildService = exports.buildImageOnCloud = exports.toCloudFunction = exports.createImageArtifactFromConfig = exports.createCodeArtifact = exports.toCurrentState = exports.createDeployPlan = exports.parseImageReference = exports.normalizeGatewayPath = exports.normalizeDeployConfig = exports.isCustomImageRuntime = exports.checkDeployConfig = exports.assertDeployConfig = exports.FunctionDeployer = void 0;
|
|
17
|
+
exports.IMAGE_PULL_GRANT_URL = exports.REQUIRED_IMAGE_PULL_POLICY = exports.SCF_PULL_IMAGE_ROLE = exports.isCamPermissionError = exports.runImagePullAuthPreflight = exports.runImageRemotePreflight = exports.runLocalPreflight = exports.createCloudBuildService = exports.buildImageOnCloud = exports.toCloudFunction = exports.createImageArtifactFromConfig = exports.createCodeArtifact = exports.toCurrentState = exports.createDeployPlan = exports.parseImageReference = exports.normalizeGatewayPath = exports.normalizeDeployConfig = exports.isCustomImageRuntime = exports.prepareMinimalDeployConfig = exports.getMissingDeployInputs = exports.applyMinimalDeployConfigDefaults = exports.checkDeployConfig = exports.assertDeployConfig = exports.FunctionDeployer = void 0;
|
|
18
18
|
const error_1 = require("../error");
|
|
19
19
|
const artifact_1 = require("./function/artifact");
|
|
20
20
|
const config_guard_1 = require("./function/config-guard");
|
|
@@ -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
|
* 兼容既有声明式部署入口
|
|
@@ -88,7 +95,12 @@ class FunctionDeployer {
|
|
|
88
95
|
return;
|
|
89
96
|
}
|
|
90
97
|
const rule = this.buildPublicAccessRego(functionName);
|
|
91
|
-
|
|
98
|
+
// 云端 Rego 校验器要求首条有效语句(注释不计)必须是 package 声明。
|
|
99
|
+
// currentRego 非空时已含 package authz.user,直接追加规则体即可;
|
|
100
|
+
// 为空(新环境首次配置)时需补齐 package 声明,否则触发 2:1: package expected。
|
|
101
|
+
const mergedRego = currentRego.trim()
|
|
102
|
+
? `${currentRego.trim()}\n\n${rule}`
|
|
103
|
+
: `package authz.user\n\n${rule}`;
|
|
92
104
|
await permissionService.modifyEnvAuthzConfig({
|
|
93
105
|
key: AUTHZ_USER_REGO_KEY,
|
|
94
106
|
value: mergedRego
|
|
@@ -169,6 +181,24 @@ class FunctionDeployer {
|
|
|
169
181
|
checkConfig(config) {
|
|
170
182
|
return (0, config_guard_1.checkDeployConfig)(config);
|
|
171
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* 计算最小化部署配置的默认值,不执行网络或云端变更。
|
|
186
|
+
*/
|
|
187
|
+
applyMinimalConfigDefaults(config, context = {}) {
|
|
188
|
+
return (0, config_guard_1.applyMinimalDeployConfigDefaults)(config, context);
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 返回最小化配置仍缺失的字段,不执行网络或云端变更。
|
|
192
|
+
*/
|
|
193
|
+
getMissingConfigInputs(config, context = {}) {
|
|
194
|
+
return (0, config_guard_1.getMissingDeployInputs)(config, context);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 应用安全默认值并返回缺失字段,供 CLI/MCP 逐步收集用户输入。
|
|
198
|
+
*/
|
|
199
|
+
prepareMinimalConfig(config, context = {}) {
|
|
200
|
+
return (0, config_guard_1.prepareMinimalDeployConfig)(config, context);
|
|
201
|
+
}
|
|
172
202
|
/**
|
|
173
203
|
* 生成部署计划
|
|
174
204
|
*
|
|
@@ -195,13 +225,14 @@ class FunctionDeployer {
|
|
|
195
225
|
* @param options 部署选项,dryRun 时只生成计划不做任何变更
|
|
196
226
|
*/
|
|
197
227
|
async deployFunction(config, options = {}) {
|
|
228
|
+
const deployConfig = config;
|
|
198
229
|
const startTime = Date.now();
|
|
199
230
|
const ctx = { steps: [], options };
|
|
200
231
|
const warnings = [];
|
|
201
|
-
const report = (0, config_guard_1.checkDeployConfig)(
|
|
232
|
+
const report = (0, config_guard_1.checkDeployConfig)(deployConfig);
|
|
202
233
|
this.appendWarnings(warnings, report);
|
|
203
234
|
const normalized = await this.runStep(ctx, 'validate', async () => {
|
|
204
|
-
return (0, config_guard_1.assertDeployConfig)(
|
|
235
|
+
return (0, config_guard_1.assertDeployConfig)(deployConfig);
|
|
205
236
|
});
|
|
206
237
|
const plan = await this.runStep(ctx, 'plan', async () => {
|
|
207
238
|
const current = await this.getCurrentFunction(normalized.name, normalized.codeSecret);
|
|
@@ -214,27 +245,52 @@ class FunctionDeployer {
|
|
|
214
245
|
steps: ctx.steps,
|
|
215
246
|
warnings,
|
|
216
247
|
startTime,
|
|
217
|
-
dryRun: true
|
|
248
|
+
dryRun: true,
|
|
249
|
+
options
|
|
218
250
|
});
|
|
219
251
|
}
|
|
220
252
|
// 不可原地更新的变更必须在任何可能产生副作用的 Preflight 之前中止。
|
|
221
253
|
// 尤其避免镜像拉取授权自动补齐等云端写操作先发生,再提示用户手工删除函数。
|
|
222
254
|
this.assertPlanExecutable(plan);
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
this.
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
255
|
+
if (normalized.type === 'HTTP' &&
|
|
256
|
+
(normalized.buildStrategy === 'cloud' || normalized.buildStrategy === 'zip')) {
|
|
257
|
+
await this.runLabeledStep(ctx, {
|
|
258
|
+
stage: 'preflight',
|
|
259
|
+
handler: async () => {
|
|
260
|
+
const report = (0, preflight_1.runLocalPreflight)(normalized);
|
|
261
|
+
this.appendWarnings(warnings, report);
|
|
262
|
+
this.assertPreflightReady(report);
|
|
263
|
+
},
|
|
264
|
+
label: normalized.buildStrategy === 'cloud'
|
|
265
|
+
? '检查云端构建上下文'
|
|
266
|
+
: '检查函数代码包'
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (normalized.type === 'HTTP' &&
|
|
270
|
+
(normalized.buildStrategy === 'image' ||
|
|
271
|
+
normalized.buildStrategy === 'local' ||
|
|
272
|
+
normalized.buildStrategy === 'cloud')) {
|
|
273
|
+
await this.runLabeledStep(ctx, {
|
|
274
|
+
stage: 'preflight',
|
|
275
|
+
handler: async () => {
|
|
276
|
+
const report = await (0, cam_preflight_1.runImagePullAuthPreflight)(this.createCamPreflightAdapter(), normalized, options.autoGrant !== false);
|
|
277
|
+
this.appendWarnings(warnings, report);
|
|
278
|
+
this.assertPreflightReady(report);
|
|
279
|
+
},
|
|
280
|
+
label: '检查镜像仓库访问权限'
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
if (normalized.type === 'HTTP' && normalized.buildStrategy === 'image') {
|
|
284
|
+
await this.runLabeledStep(ctx, {
|
|
285
|
+
stage: 'preflight',
|
|
286
|
+
handler: async () => {
|
|
287
|
+
const report = await (0, image_preflight_1.runImageRemotePreflight)(normalized, this.resolveDeployRegion(), this.imagePreflightDeps);
|
|
288
|
+
this.appendWarnings(warnings, report);
|
|
289
|
+
this.assertPreflightReady(report);
|
|
290
|
+
},
|
|
291
|
+
label: '检查远端镜像与地域'
|
|
292
|
+
});
|
|
293
|
+
}
|
|
238
294
|
// noop:函数本身无变更,但仍可能需要收敛访问配置
|
|
239
295
|
if (plan.action === 'noop') {
|
|
240
296
|
await this.applyAccessPlan(ctx, normalized, plan);
|
|
@@ -244,7 +300,8 @@ class FunctionDeployer {
|
|
|
244
300
|
steps: ctx.steps,
|
|
245
301
|
warnings,
|
|
246
302
|
startTime,
|
|
247
|
-
dryRun: false
|
|
303
|
+
dryRun: false,
|
|
304
|
+
options
|
|
248
305
|
});
|
|
249
306
|
}
|
|
250
307
|
const artifact = await this.prepareArtifact(ctx, normalized, warnings);
|
|
@@ -257,6 +314,7 @@ class FunctionDeployer {
|
|
|
257
314
|
warnings,
|
|
258
315
|
startTime,
|
|
259
316
|
dryRun: false,
|
|
317
|
+
options,
|
|
260
318
|
artifact
|
|
261
319
|
});
|
|
262
320
|
}
|
|
@@ -264,7 +322,7 @@ class FunctionDeployer {
|
|
|
264
322
|
* 将既有声明式调用补齐为完整部署配置
|
|
265
323
|
*/
|
|
266
324
|
withLegacyDeployContext(func, functionRootPath, functionPath) {
|
|
267
|
-
var _a, _b;
|
|
325
|
+
var _a, _b, _c, _d, _e;
|
|
268
326
|
const config = Object.assign({}, func);
|
|
269
327
|
const strategy = this.resolveStrategy(config);
|
|
270
328
|
if (functionPath) {
|
|
@@ -282,16 +340,14 @@ class FunctionDeployer {
|
|
|
282
340
|
(effectiveFunctionRootPath
|
|
283
341
|
? `${effectiveFunctionRootPath}/${config.name}`
|
|
284
342
|
: undefined);
|
|
285
|
-
|
|
286
|
-
|
|
343
|
+
const existingCwd = (_b = (_a = config.imageConfig) === null || _a === void 0 ? void 0 : _a.build) === null || _b === void 0 ? void 0 : _b.cwd;
|
|
344
|
+
if (buildCwd || existingCwd) {
|
|
345
|
+
config.imageConfig = Object.assign(Object.assign({}, (config.imageConfig || {})), { build: Object.assign(Object.assign({}, (((_c = config.imageConfig) === null || _c === void 0 ? void 0 : _c.build) || {})), { cwd: existingCwd || buildCwd }) });
|
|
287
346
|
}
|
|
288
347
|
}
|
|
289
|
-
//
|
|
290
|
-
if (strategy === 'image' && config.imageUri && !config.imageConfig) {
|
|
291
|
-
config.imageType = config.imageType || 'personal';
|
|
292
|
-
config.imageConfig = {
|
|
293
|
-
imageUri: config.imageUri
|
|
294
|
-
};
|
|
348
|
+
// 兼容旧声明式顶层 imageUri 字段,统一收敛到 imageConfig 契约
|
|
349
|
+
if (strategy === 'image' && config.imageUri && !((_d = config.imageConfig) === null || _d === void 0 ? void 0 : _d.imageUri)) {
|
|
350
|
+
config.imageConfig = Object.assign(Object.assign({ imageType: ((_e = config.imageConfig) === null || _e === void 0 ? void 0 : _e.imageType) || 'personal' }, (config.imageConfig || {})), { imageUri: config.imageUri });
|
|
295
351
|
delete config.imageUri;
|
|
296
352
|
}
|
|
297
353
|
return config;
|
|
@@ -310,6 +366,21 @@ class FunctionDeployer {
|
|
|
310
366
|
return undefined;
|
|
311
367
|
}
|
|
312
368
|
}
|
|
369
|
+
resolveEnvironmentId() {
|
|
370
|
+
try {
|
|
371
|
+
return this.environment.getEnvId();
|
|
372
|
+
}
|
|
373
|
+
catch (_a) {
|
|
374
|
+
return '';
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
requireDeployRegion() {
|
|
378
|
+
const region = this.resolveDeployRegion();
|
|
379
|
+
if (!region) {
|
|
380
|
+
throw new error_1.CloudBaseError('企业版 TCR 自动发现需要明确部署地域,请在环境配置中设置 region。', { code: types_1.FUNCTION_DEPLOY_ERROR.TCR_REGION_MISSING });
|
|
381
|
+
}
|
|
382
|
+
return region;
|
|
383
|
+
}
|
|
313
384
|
/**
|
|
314
385
|
* 构造镜像拉取授权 Preflight 所需的 CAM 适配器
|
|
315
386
|
*
|
|
@@ -398,6 +469,66 @@ class FunctionDeployer {
|
|
|
398
469
|
'\n部署器不会自动删除已有云函数。请先在云开发控制台确认触发器、访问配置和服务中断影响,手工删除该函数后重新部署。', { code: types_1.FUNCTION_DEPLOY_ERROR.FUNCTION_RECREATE_REQUIRED });
|
|
399
470
|
}
|
|
400
471
|
}
|
|
472
|
+
async prepareEnterpriseBuildConfig(ctx, config) {
|
|
473
|
+
const build = config.imageConfig.build;
|
|
474
|
+
const repository = build.repository || '';
|
|
475
|
+
const hasPreparedTarget = Boolean((config.imageConfig.registryId || build.registryId) && repository.split('/').length >= 3);
|
|
476
|
+
if (config.imageConfig.imageType !== 'enterprise' || hasPreparedTarget) {
|
|
477
|
+
return { config };
|
|
478
|
+
}
|
|
479
|
+
const prepared = await this.runLabeledStep(ctx, {
|
|
480
|
+
stage: 'preflight',
|
|
481
|
+
label: '准备企业版 TCR 目标仓库',
|
|
482
|
+
handler: () => (0, enterprise_tcr_1.prepareEnterpriseTcrTarget)(config, {
|
|
483
|
+
envId: this.resolveEnvironmentId(),
|
|
484
|
+
region: this.requireDeployRegion(),
|
|
485
|
+
service: this.tcrServiceFactory()
|
|
486
|
+
})
|
|
487
|
+
});
|
|
488
|
+
return { config: prepared.config, target: prepared.target };
|
|
489
|
+
}
|
|
490
|
+
async preparePersonalBuildConfig(ctx, config) {
|
|
491
|
+
var _a, _b, _c;
|
|
492
|
+
if (config.imageConfig.imageType !== 'personal') {
|
|
493
|
+
return config;
|
|
494
|
+
}
|
|
495
|
+
const envId = this.resolveEnvironmentId();
|
|
496
|
+
const build = config.imageConfig.build;
|
|
497
|
+
const configuredNamespace = (_a = build.namespace) === null || _a === void 0 ? void 0 : _a.trim();
|
|
498
|
+
const hasConfiguredRepository = Boolean((_b = build.repository) === null || _b === void 0 ? void 0 : _b.trim());
|
|
499
|
+
const configuredRepository = ((_c = build.repository) === null || _c === void 0 ? void 0 : _c.trim()) || config.name;
|
|
500
|
+
const parsedRepository = (0, config_guard_1.parseImageReference)(configuredRepository);
|
|
501
|
+
if (!parsedRepository || parsedRepository.tag || parsedRepository.digest) {
|
|
502
|
+
throw new error_1.CloudBaseError('个人版 TCR repository 不合法。', {
|
|
503
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.BUILD_REPOSITORY_INVALID
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
const repositoryParts = parsedRepository.repository.split('/');
|
|
507
|
+
const repository = repositoryParts[repositoryParts.length - 1];
|
|
508
|
+
const repositoryNamespace = repositoryParts.length > 1 ? repositoryParts.slice(0, -1).join('/') : undefined;
|
|
509
|
+
const namespace = configuredNamespace || repositoryNamespace || envId;
|
|
510
|
+
const hasConfiguredNamespace = Boolean(configuredNamespace || repositoryNamespace);
|
|
511
|
+
const registry = parsedRepository.registry || personal_tcr_1.DEFAULT_PERSONAL_TCR_REGISTRY;
|
|
512
|
+
const credential = build.registryCredential;
|
|
513
|
+
if (!(credential === null || credential === void 0 ? void 0 : credential.username) || !credential.password) {
|
|
514
|
+
throw new error_1.CloudBaseError('个人版 TCR 构建缺少推送凭证。', {
|
|
515
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.CLOUD_REGISTRY_CREDENTIAL_MISSING
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
await this.runLabeledStep(ctx, {
|
|
519
|
+
stage: 'preflight',
|
|
520
|
+
label: '准备个人版 TCR 目标仓库',
|
|
521
|
+
handler: () => this.personalTcrServiceFactory().ensurePushTarget({
|
|
522
|
+
namespace,
|
|
523
|
+
repository,
|
|
524
|
+
ensureNamespace: !hasConfiguredNamespace,
|
|
525
|
+
ensureRepository: !hasConfiguredRepository
|
|
526
|
+
})
|
|
527
|
+
});
|
|
528
|
+
const targetRepository = `${registry}/${namespace}/${repository}`;
|
|
529
|
+
const preparedConfig = Object.assign(Object.assign({}, config), { imageConfig: Object.assign(Object.assign({}, config.imageConfig), { build: Object.assign(Object.assign({}, build), { namespace, repository: targetRepository, registryCredential: credential }) }) });
|
|
530
|
+
return preparedConfig;
|
|
531
|
+
}
|
|
401
532
|
/**
|
|
402
533
|
* 准备部署物
|
|
403
534
|
*
|
|
@@ -406,8 +537,22 @@ class FunctionDeployer {
|
|
|
406
537
|
* 其余策略走同步的 pack。
|
|
407
538
|
*/
|
|
408
539
|
async prepareArtifact(ctx, config, warnings) {
|
|
540
|
+
var _a;
|
|
409
541
|
if (config.buildStrategy === 'cloud') {
|
|
410
|
-
const
|
|
542
|
+
const prepared = await this.prepareEnterpriseBuildConfig(ctx, config);
|
|
543
|
+
const cloudConfig = await this.preparePersonalBuildConfig(ctx, prepared.config);
|
|
544
|
+
// 企业版登录凭证在触发 CloudApp 构建前换取,构建容器只消费 Secrets,
|
|
545
|
+
// 不再自行签名调用 TCR API。
|
|
546
|
+
const cloudRegistryId = ((_a = prepared.target) === null || _a === void 0 ? void 0 : _a.registryId) ||
|
|
547
|
+
cloudConfig.imageConfig.build.registryId ||
|
|
548
|
+
cloudConfig.imageConfig.registryId;
|
|
549
|
+
const enterpriseCredential = cloudConfig.imageConfig.imageType === 'enterprise' && cloudRegistryId
|
|
550
|
+
? await this.runLabeledStep(ctx, {
|
|
551
|
+
stage: 'preflight',
|
|
552
|
+
label: '获取企业版 TCR 临时登录凭证',
|
|
553
|
+
handler: () => (0, enterprise_tcr_1.createEnterpriseTcrCredential)(this.tcrServiceFactory(), cloudRegistryId)
|
|
554
|
+
})
|
|
555
|
+
: undefined;
|
|
411
556
|
const buildResult = await this.runStep(ctx, 'build', async () => {
|
|
412
557
|
const cloudBuildService = this.cloudBuildServiceFactory();
|
|
413
558
|
return (0, cloud_1.buildImageOnCloud)(cloudConfig, cloudBuildService, {
|
|
@@ -416,27 +561,100 @@ class FunctionDeployer {
|
|
|
416
561
|
status: 'start',
|
|
417
562
|
message: line
|
|
418
563
|
}),
|
|
419
|
-
|
|
564
|
+
enterpriseCredential
|
|
420
565
|
});
|
|
421
566
|
});
|
|
422
|
-
return (0, artifact_1.createImageArtifactFromCloudBuild)(
|
|
567
|
+
return (0, artifact_1.createImageArtifactFromCloudBuild)(cloudConfig, buildResult);
|
|
423
568
|
}
|
|
424
569
|
if (config.buildStrategy === 'local') {
|
|
425
|
-
await this.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
570
|
+
await this.runLabeledStep(ctx, {
|
|
571
|
+
stage: 'preflight',
|
|
572
|
+
handler: async () => {
|
|
573
|
+
const report = await (0, docker_preflight_1.runDockerPreflight)();
|
|
574
|
+
this.appendWarnings(warnings, report);
|
|
575
|
+
this.assertPreflightReady(report);
|
|
576
|
+
},
|
|
577
|
+
label: '检查本地 Docker 环境'
|
|
429
578
|
});
|
|
430
|
-
const
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
579
|
+
const prepared = await this.prepareEnterpriseBuildConfig(ctx, config);
|
|
580
|
+
const localConfig = await this.preparePersonalBuildConfig(ctx, prepared.config);
|
|
581
|
+
const credential = await this.resolveLocalRegistryCredential(localConfig);
|
|
582
|
+
const dockerConfig = credential ? (0, local_builder_1.createTemporaryDockerConfig)() : undefined;
|
|
583
|
+
const commandOptions = dockerConfig ? { env: dockerConfig.env } : undefined;
|
|
584
|
+
try {
|
|
585
|
+
if (credential) {
|
|
586
|
+
await this.runStep(ctx, 'login', async () => {
|
|
587
|
+
await (0, local_builder_1.loginDockerRegistry)(this.resolveRegistryFromRepository(localConfig.imageConfig.build.repository), credential, { deps: this.localBuilderDeps, commandOptions });
|
|
588
|
+
});
|
|
589
|
+
}
|
|
590
|
+
const buildResult = await this.runStep(ctx, 'build', async () => {
|
|
591
|
+
return (0, local_builder_1.buildLocalImage)(localConfig, line => this.emitProgress(ctx.options, {
|
|
592
|
+
stage: 'build',
|
|
593
|
+
status: 'start',
|
|
594
|
+
message: line
|
|
595
|
+
}), { deps: this.localBuilderDeps, commandOptions });
|
|
596
|
+
});
|
|
597
|
+
const pushResult = await this.runStep(ctx, 'push', async () => {
|
|
598
|
+
return (0, local_builder_1.pushLocalImage)(buildResult.imageUri, line => this.emitProgress(ctx.options, {
|
|
599
|
+
stage: 'push',
|
|
600
|
+
status: 'start',
|
|
601
|
+
message: line
|
|
602
|
+
}), { deps: this.localBuilderDeps, commandOptions });
|
|
603
|
+
});
|
|
604
|
+
if (localConfig.imageConfig.imageType === 'personal') {
|
|
605
|
+
await this.runLabeledStep(ctx, {
|
|
606
|
+
stage: 'push',
|
|
607
|
+
label: '清理历史镜像标签',
|
|
608
|
+
handler: () => this.personalTcrServiceFactory().cleanupTags({
|
|
609
|
+
namespace: localConfig.imageConfig.build.namespace,
|
|
610
|
+
repository: localConfig.imageConfig.build.repository,
|
|
611
|
+
retain: localConfig.imageConfig.build.retainedTags
|
|
612
|
+
})
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return (0, artifact_1.createImageArtifactFromBuild)(localConfig, {
|
|
616
|
+
imageUri: buildResult.imageUri,
|
|
617
|
+
imageDigest: pushResult.imageDigest
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
finally {
|
|
621
|
+
dockerConfig === null || dockerConfig === void 0 ? void 0 : dockerConfig.cleanup();
|
|
622
|
+
}
|
|
435
623
|
}
|
|
436
624
|
return this.runStep(ctx, 'pack', async () => {
|
|
437
625
|
return this.createArtifact(config);
|
|
438
626
|
});
|
|
439
627
|
}
|
|
628
|
+
async resolveLocalRegistryCredential(config) {
|
|
629
|
+
var _a, _b, _c;
|
|
630
|
+
const build = config.imageConfig.build;
|
|
631
|
+
const imageType = (_a = config.imageConfig.imageType) !== null && _a !== void 0 ? _a : (((_b = config.imageConfig.registryId) !== null && _b !== void 0 ? _b : build.registryId) ? 'enterprise' : 'personal');
|
|
632
|
+
if (imageType === 'enterprise') {
|
|
633
|
+
const registryId = (_c = config.imageConfig.registryId) !== null && _c !== void 0 ? _c : build.registryId;
|
|
634
|
+
if (!registryId) {
|
|
635
|
+
throw new error_1.CloudBaseError('企业版 TCR local 构建缺少 registryId。', {
|
|
636
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.IMAGE_REGISTRY_ID_MISSING
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
const credential = await (0, enterprise_tcr_1.createEnterpriseTcrCredential)(this.tcrServiceFactory(), registryId);
|
|
640
|
+
return { username: credential.username, secret: credential.token };
|
|
641
|
+
}
|
|
642
|
+
const credential = build.registryCredential;
|
|
643
|
+
if (!credential) {
|
|
644
|
+
return undefined;
|
|
645
|
+
}
|
|
646
|
+
return { username: credential.username, secret: credential.password };
|
|
647
|
+
}
|
|
648
|
+
resolveRegistryFromRepository(repository) {
|
|
649
|
+
const normalized = typeof repository === 'string' ? repository.trim() : '';
|
|
650
|
+
const slashIndex = normalized.indexOf('/');
|
|
651
|
+
if (slashIndex <= 0) {
|
|
652
|
+
throw new error_1.CloudBaseError('local 策略的目标镜像地址缺少仓库域名', {
|
|
653
|
+
code: types_1.FUNCTION_DEPLOY_ERROR.LOCAL_BUILD_REPOSITORY_MISSING
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
return normalized.slice(0, slashIndex);
|
|
657
|
+
}
|
|
440
658
|
/** 生成部署物 */
|
|
441
659
|
createArtifact(config) {
|
|
442
660
|
if (config.buildStrategy === 'image') {
|
|
@@ -499,24 +717,53 @@ class FunctionDeployer {
|
|
|
499
717
|
});
|
|
500
718
|
}
|
|
501
719
|
}
|
|
502
|
-
/**
|
|
720
|
+
/** 执行普通步骤并记录耗时与进度 */
|
|
503
721
|
async runStep(ctx, stage, handler) {
|
|
722
|
+
return this.executeStep(ctx, { stage, handler });
|
|
723
|
+
}
|
|
724
|
+
/** 执行带具体名称的步骤并记录耗时与进度 */
|
|
725
|
+
async runLabeledStep(ctx, options) {
|
|
726
|
+
return this.executeStep(ctx, options);
|
|
727
|
+
}
|
|
728
|
+
async executeStep(ctx, options) {
|
|
729
|
+
var _a, _b, _c, _d, _e;
|
|
730
|
+
const { stage, handler, label } = options;
|
|
504
731
|
const startTime = Date.now();
|
|
505
|
-
this.emitProgress(ctx.options, { stage, status: 'start' });
|
|
732
|
+
this.emitProgress(ctx.options, { stage, label, status: 'start' });
|
|
506
733
|
try {
|
|
507
734
|
const result = await handler();
|
|
508
|
-
|
|
509
|
-
|
|
735
|
+
const elapsedTime = Date.now() - startTime;
|
|
736
|
+
ctx.steps.push({ stage, label, status: 'success', elapsedTime });
|
|
737
|
+
this.emitProgress(ctx.options, { stage, label, status: 'success', elapsedTime });
|
|
510
738
|
return result;
|
|
511
739
|
}
|
|
512
740
|
catch (error) {
|
|
741
|
+
const elapsedTime = Date.now() - startTime;
|
|
513
742
|
ctx.steps.push({
|
|
514
743
|
stage,
|
|
744
|
+
label,
|
|
745
|
+
status: 'failed',
|
|
746
|
+
elapsedTime,
|
|
747
|
+
detail: error === null || error === void 0 ? void 0 : error.message,
|
|
748
|
+
errorCode: error === null || error === void 0 ? void 0 : error.code
|
|
749
|
+
});
|
|
750
|
+
this.emitProgress(ctx.options, {
|
|
751
|
+
stage,
|
|
752
|
+
label,
|
|
515
753
|
status: 'failed',
|
|
516
|
-
|
|
517
|
-
|
|
754
|
+
message: error === null || error === void 0 ? void 0 : error.message,
|
|
755
|
+
elapsedTime,
|
|
756
|
+
errorCode: error === null || error === void 0 ? void 0 : error.code,
|
|
757
|
+
requestId: (error === null || error === void 0 ? void 0 : error.requestId) ||
|
|
758
|
+
(error === null || error === void 0 ? void 0 : error.RequestId) ||
|
|
759
|
+
((_a = error === null || error === void 0 ? void 0 : error.original) === null || _a === void 0 ? void 0 : _a.requestId) ||
|
|
760
|
+
((_b = error === null || error === void 0 ? void 0 : error.original) === null || _b === void 0 ? void 0 : _b.RequestId) ||
|
|
761
|
+
((_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) ||
|
|
762
|
+
((_e = error === null || error === void 0 ? void 0 : error.response) === null || _e === void 0 ? void 0 : _e.RequestId)
|
|
518
763
|
});
|
|
519
|
-
|
|
764
|
+
if (error && !error.failedStage) {
|
|
765
|
+
error.failedStage = stage;
|
|
766
|
+
}
|
|
520
767
|
throw error;
|
|
521
768
|
}
|
|
522
769
|
}
|
|
@@ -567,11 +814,19 @@ class FunctionDeployer {
|
|
|
567
814
|
return result;
|
|
568
815
|
}
|
|
569
816
|
emitProgressDone(params) {
|
|
817
|
+
const elapsedTime = Date.now() - params.startTime;
|
|
570
818
|
params.steps.push({
|
|
571
819
|
stage: 'done',
|
|
572
820
|
status: 'success',
|
|
573
|
-
elapsedTime
|
|
821
|
+
elapsedTime
|
|
574
822
|
});
|
|
823
|
+
if (params.options) {
|
|
824
|
+
this.emitProgress(params.options, {
|
|
825
|
+
stage: 'done',
|
|
826
|
+
status: 'success',
|
|
827
|
+
elapsedTime
|
|
828
|
+
});
|
|
829
|
+
}
|
|
575
830
|
}
|
|
576
831
|
}
|
|
577
832
|
exports.FunctionDeployer = FunctionDeployer;
|
|
@@ -579,6 +834,9 @@ __exportStar(require("./types"), exports);
|
|
|
579
834
|
var config_guard_2 = require("./function/config-guard");
|
|
580
835
|
Object.defineProperty(exports, "assertDeployConfig", { enumerable: true, get: function () { return config_guard_2.assertDeployConfig; } });
|
|
581
836
|
Object.defineProperty(exports, "checkDeployConfig", { enumerable: true, get: function () { return config_guard_2.checkDeployConfig; } });
|
|
837
|
+
Object.defineProperty(exports, "applyMinimalDeployConfigDefaults", { enumerable: true, get: function () { return config_guard_2.applyMinimalDeployConfigDefaults; } });
|
|
838
|
+
Object.defineProperty(exports, "getMissingDeployInputs", { enumerable: true, get: function () { return config_guard_2.getMissingDeployInputs; } });
|
|
839
|
+
Object.defineProperty(exports, "prepareMinimalDeployConfig", { enumerable: true, get: function () { return config_guard_2.prepareMinimalDeployConfig; } });
|
|
582
840
|
Object.defineProperty(exports, "isCustomImageRuntime", { enumerable: true, get: function () { return config_guard_2.isCustomImageRuntime; } });
|
|
583
841
|
Object.defineProperty(exports, "normalizeDeployConfig", { enumerable: true, get: function () { return config_guard_2.normalizeDeployConfig; } });
|
|
584
842
|
Object.defineProperty(exports, "normalizeGatewayPath", { enumerable: true, get: function () { return config_guard_2.normalizeGatewayPath; } });
|
|
@@ -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);
|