@cloudbase/manager-node 5.4.0-beta.2 → 5.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/env/index.js +46 -19
- package/lib/function/index.js +55 -37
- package/lib/log/index.js +5 -9
- package/package.json +1 -1
- package/scripts/ci/generate-changelog.js +422 -0
- package/types/env/index.d.ts +11 -1
- package/types/interfaces/tcb.interface.d.ts +64 -0
- package/types/log/index.d.ts +2 -2
- package/.claude/settings.local.json +0 -9
package/lib/env/index.js
CHANGED
|
@@ -136,18 +136,33 @@ class EnvService {
|
|
|
136
136
|
* @returns {Promise<IEnvInfoRes>}
|
|
137
137
|
*/
|
|
138
138
|
async getEnvInfo() {
|
|
139
|
-
|
|
139
|
+
var _a;
|
|
140
|
+
// 使用 DescribeEnvInfo 进行单环境查询
|
|
140
141
|
const params = {
|
|
141
142
|
EnvId: this.envId
|
|
142
143
|
};
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
const
|
|
144
|
+
const response = await this.describeEnvInfo(params);
|
|
145
|
+
const envBaseInfo = (_a = response === null || response === void 0 ? void 0 : response.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
|
|
146
|
+
// 成功获取环境信息,将 EnvBaseInfo 映射到 EnvInfo 类型
|
|
147
|
+
const envInfo = {
|
|
148
|
+
EnvId: envBaseInfo.EnvId,
|
|
149
|
+
Alias: envBaseInfo.Alias,
|
|
150
|
+
Status: envBaseInfo.Status,
|
|
151
|
+
Databases: envBaseInfo.Databases,
|
|
152
|
+
Storages: envBaseInfo.Storages,
|
|
153
|
+
Functions: envBaseInfo.Functions,
|
|
154
|
+
PackageId: envBaseInfo.PackageId || '',
|
|
155
|
+
PackageName: envBaseInfo.PackageName || '',
|
|
156
|
+
LogServices: envBaseInfo.LogServices || [],
|
|
157
|
+
CustomLogServices: envBaseInfo.CustomLogServices || [],
|
|
158
|
+
Region: envBaseInfo.Region || '',
|
|
159
|
+
Source: envBaseInfo.Source,
|
|
160
|
+
CreateTime: envBaseInfo.CreateTime,
|
|
161
|
+
UpdateTime: envBaseInfo.UpdateTime
|
|
162
|
+
};
|
|
147
163
|
return {
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
RequestId
|
|
164
|
+
EnvInfo: envInfo,
|
|
165
|
+
RequestId: response.RequestId || ''
|
|
151
166
|
};
|
|
152
167
|
}
|
|
153
168
|
/**
|
|
@@ -156,6 +171,18 @@ class EnvService {
|
|
|
156
171
|
async describeEnvs(params) {
|
|
157
172
|
return this.cloudService.request('DescribeEnvs', params);
|
|
158
173
|
}
|
|
174
|
+
/**
|
|
175
|
+
* 查询单个环境信息
|
|
176
|
+
*
|
|
177
|
+
* 用于单个环境查询时,规避 DescribeEnvs 的 backend issues。
|
|
178
|
+
* 当 EnvId 参数存在时,应优先使用本方法而非 describeEnvs。
|
|
179
|
+
*
|
|
180
|
+
* @param params 查询参数,必须包含 EnvId
|
|
181
|
+
* @returns 环境信息响应
|
|
182
|
+
*/
|
|
183
|
+
async describeEnvInfo(params) {
|
|
184
|
+
return this.cloudService.request('DescribeEnvInfo', params);
|
|
185
|
+
}
|
|
159
186
|
/**
|
|
160
187
|
* 修改环境名称
|
|
161
188
|
* @param {string} alias 环境名称
|
|
@@ -476,28 +503,28 @@ class EnvService {
|
|
|
476
503
|
async calculatePackageRenewPrice(params) {
|
|
477
504
|
var _a, _b, _c, _d, _e, _f;
|
|
478
505
|
const { envId, period = 1, currency = 'CNY' } = params;
|
|
479
|
-
const [packageRes,
|
|
506
|
+
const [packageRes, envInfoRes, billingRes] = await Promise.all([
|
|
480
507
|
this.describeBaasPackageList({
|
|
481
508
|
Source: 'qcloud',
|
|
482
509
|
PackageTypeList: ['default', 'vip', 'basic', 'trial']
|
|
483
510
|
}),
|
|
484
|
-
this.
|
|
511
|
+
this.describeEnvInfo({
|
|
485
512
|
EnvId: envId
|
|
486
513
|
}),
|
|
487
514
|
this.describeBillingInfo({
|
|
488
515
|
EnvId: envId
|
|
489
516
|
})
|
|
490
517
|
]);
|
|
491
|
-
const
|
|
518
|
+
const envBaseInfo = (_a = envInfoRes === null || envInfoRes === void 0 ? void 0 : envInfoRes.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
|
|
492
519
|
const billingInfo = (_b = billingRes.EnvBillingInfoList) === null || _b === void 0 ? void 0 : _b[0];
|
|
493
|
-
if (!
|
|
520
|
+
if (!(envBaseInfo === null || envBaseInfo === void 0 ? void 0 : envBaseInfo.EnvId) || !billingInfo) {
|
|
494
521
|
throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
|
|
495
522
|
}
|
|
496
|
-
const packageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName ===
|
|
523
|
+
const packageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName === envBaseInfo.PackageId);
|
|
497
524
|
if (!packageInfo) {
|
|
498
525
|
throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
|
|
499
526
|
}
|
|
500
|
-
const region =
|
|
527
|
+
const region = envBaseInfo.Region;
|
|
501
528
|
const extPackageData = ((_d = billingInfo.ExtPackageCode) === null || _d === void 0 ? void 0 : _d.reduce((acc, { Code, Num }) => {
|
|
502
529
|
acc[Code] = Num;
|
|
503
530
|
return acc;
|
|
@@ -530,21 +557,21 @@ class EnvService {
|
|
|
530
557
|
async calculatePackageModifyPrice(params) {
|
|
531
558
|
var _a, _b, _c, _d, _e, _f, _g;
|
|
532
559
|
const { newPackageId, envId, currency = 'CNY' } = params;
|
|
533
|
-
const [packageRes,
|
|
560
|
+
const [packageRes, envInfoRes, billingRes] = await Promise.all([
|
|
534
561
|
this.describeBaasPackageList({
|
|
535
562
|
Source: 'qcloud',
|
|
536
563
|
PackageTypeList: ['default', 'vip', 'basic', 'trial']
|
|
537
564
|
}),
|
|
538
|
-
this.
|
|
565
|
+
this.describeEnvInfo({
|
|
539
566
|
EnvId: envId
|
|
540
567
|
}),
|
|
541
568
|
this.describeBillingInfo({
|
|
542
569
|
EnvId: envId
|
|
543
570
|
})
|
|
544
571
|
]);
|
|
545
|
-
const
|
|
572
|
+
const envBaseInfo = (_a = envInfoRes === null || envInfoRes === void 0 ? void 0 : envInfoRes.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo;
|
|
546
573
|
const billingInfo = (_b = billingRes.EnvBillingInfoList) === null || _b === void 0 ? void 0 : _b[0];
|
|
547
|
-
if (!
|
|
574
|
+
if (!(envBaseInfo === null || envBaseInfo === void 0 ? void 0 : envBaseInfo.EnvId) || !billingInfo || !billingInfo.PackageId) {
|
|
548
575
|
throw new error_1.CloudBaseError(`Invalid env id: ${envId}`);
|
|
549
576
|
}
|
|
550
577
|
const oldPackageInfo = (_c = packageRes.PackageList) === null || _c === void 0 ? void 0 : _c.find(item => item.PackageName === billingInfo.PackageId);
|
|
@@ -555,7 +582,7 @@ class EnvService {
|
|
|
555
582
|
if (!newPackageInfo) {
|
|
556
583
|
throw new error_1.CloudBaseError(`Invalid package id: ${newPackageId}`);
|
|
557
584
|
}
|
|
558
|
-
const region =
|
|
585
|
+
const region = envBaseInfo.Region;
|
|
559
586
|
const newBillTags = this.parseBillTags(newPackageInfo.BillTags);
|
|
560
587
|
const oldBillTags = this.parseBillTags(oldPackageInfo.BillTags);
|
|
561
588
|
let userNumber = 0;
|
package/lib/function/index.js
CHANGED
|
@@ -342,39 +342,57 @@ class FunctionService {
|
|
|
342
342
|
return res;
|
|
343
343
|
}
|
|
344
344
|
catch (e) {
|
|
345
|
-
//
|
|
345
|
+
// 函数已存在(同名冲突)
|
|
346
346
|
const functionExist = e.code === 'ResourceInUse.FunctionName' || e.code === 'ResourceInUse.Function';
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
//
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
//
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
347
|
+
if (functionExist) {
|
|
348
|
+
// 检查同名函数是否是 CREATE_FAILED 残留
|
|
349
|
+
// 场景:上次部署因依赖安装失败留下了残留函数,用户修正参数后重新部署(不传 force)
|
|
350
|
+
// 此时应自动清理残留并重新创建,而不是报错要求用户手动删除或传 force
|
|
351
|
+
try {
|
|
352
|
+
const { Status } = await this.getFunctionDetail(funcName, codeSecret);
|
|
353
|
+
if (Status === constant_1.SCF_STATUS.CREATE_FAILED) {
|
|
354
|
+
console.warn(`[${funcName}] 检测到同名函数处于 CREATE_FAILED 状态,自动清理后重新创建...`);
|
|
355
|
+
await this.deleteFunction(funcName);
|
|
356
|
+
// 重新执行创建(递归调用,此时同名函数已删除,不会再碰到 ResourceInUse)
|
|
357
|
+
return this.createFunction(funcParam);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
catch (checkErr) {
|
|
361
|
+
// 查询或删除失败,继续走原有逻辑(force 判断)
|
|
362
|
+
console.warn(`[${funcName}] 检查残留状态失败(可忽略): ${checkErr.message}`);
|
|
363
|
+
}
|
|
364
|
+
// 已存在且状态正常,强制覆盖更新
|
|
365
|
+
if (force) {
|
|
366
|
+
// 1. 更新函数代码(通过 deployMode 区分镜像部署和代码部署)
|
|
367
|
+
const codeRes = await this.updateFunctionCode({
|
|
368
|
+
func,
|
|
369
|
+
base64Code,
|
|
370
|
+
functionPath,
|
|
371
|
+
functionRootPath,
|
|
372
|
+
codeSecret: codeSecret,
|
|
373
|
+
deployMode: isImageDeploy ? 'image' : undefined
|
|
374
|
+
});
|
|
375
|
+
// 等待函数状态正常
|
|
376
|
+
await this.waitFunctionActive(funcName, codeSecret);
|
|
377
|
+
// 2. 更新函数配置
|
|
378
|
+
const configRes = await this.updateFunctionConfig(func);
|
|
379
|
+
// 等待函数状态正常
|
|
380
|
+
await this.waitFunctionActive(funcName, codeSecret);
|
|
381
|
+
// 3. 创建函数触发器
|
|
382
|
+
const triggerRes = await this.retryCreateTrigger(funcName, func.triggers);
|
|
383
|
+
// 设置路径,创建云接入路径
|
|
384
|
+
if (func.path) {
|
|
385
|
+
await this.createAccessPath(funcName, func.path);
|
|
386
|
+
}
|
|
387
|
+
// 检查函数状态
|
|
388
|
+
await this.waitFunctionActive(funcName, codeSecret);
|
|
389
|
+
// 返回全部操作的响应值
|
|
390
|
+
return {
|
|
391
|
+
triggerRes,
|
|
392
|
+
configRes,
|
|
393
|
+
codeRes
|
|
394
|
+
};
|
|
369
395
|
}
|
|
370
|
-
// 检查函数状态
|
|
371
|
-
await this.waitFunctionActive(funcName, codeSecret);
|
|
372
|
-
// 返回全部操作的响应值
|
|
373
|
-
return {
|
|
374
|
-
triggerRes,
|
|
375
|
-
configRes,
|
|
376
|
-
codeRes
|
|
377
|
-
};
|
|
378
396
|
}
|
|
379
397
|
// 不强制覆盖,抛出错误
|
|
380
398
|
if (e.message && !force) {
|
|
@@ -399,8 +417,8 @@ class FunctionService {
|
|
|
399
417
|
async getFunctionList(limit = 20, offset = 0) {
|
|
400
418
|
// 获取Function 环境配置
|
|
401
419
|
const { namespace } = this.getFunctionConfig();
|
|
402
|
-
const res = await this.
|
|
403
|
-
|
|
420
|
+
const res = await this.tcbService.request('ListFunctions', {
|
|
421
|
+
EnvId: namespace,
|
|
404
422
|
Limit: limit,
|
|
405
423
|
Offset: offset
|
|
406
424
|
});
|
|
@@ -415,8 +433,8 @@ class FunctionService {
|
|
|
415
433
|
async listFunctions(limit = 20, offset = 0) {
|
|
416
434
|
// 获取Function 环境配置
|
|
417
435
|
const { namespace } = this.getFunctionConfig();
|
|
418
|
-
const res = await this.
|
|
419
|
-
|
|
436
|
+
const res = await this.tcbService.request('ListFunctions', {
|
|
437
|
+
EnvId: namespace,
|
|
420
438
|
Limit: limit,
|
|
421
439
|
Offset: offset
|
|
422
440
|
});
|
|
@@ -447,8 +465,8 @@ class FunctionService {
|
|
|
447
465
|
const { envId } = options;
|
|
448
466
|
while (true) {
|
|
449
467
|
try {
|
|
450
|
-
const res = await this.
|
|
451
|
-
|
|
468
|
+
const res = await this.tcbService.request('ListFunctions', {
|
|
469
|
+
EnvId: envId,
|
|
452
470
|
Limit: pageSize,
|
|
453
471
|
Offset: currentOffset
|
|
454
472
|
});
|
package/lib/log/index.js
CHANGED
|
@@ -19,7 +19,7 @@ exports.isLogServiceEnabled = isLogServiceEnabled;
|
|
|
19
19
|
const utils_1 = require("../utils");
|
|
20
20
|
__exportStar(require("./types"), exports);
|
|
21
21
|
/**
|
|
22
|
-
* 纯函数:根据
|
|
22
|
+
* 纯函数:根据 DescribeEnvInfo 返回的 LogServices 列表判断日志服务是否已开通。
|
|
23
23
|
* 已开通条件:至少一个条目有有效的 TopicId。
|
|
24
24
|
*/
|
|
25
25
|
function isLogServiceEnabled(logServices) {
|
|
@@ -36,21 +36,17 @@ class LogService {
|
|
|
36
36
|
/**
|
|
37
37
|
* 检查当前环境的日志服务是否已开通
|
|
38
38
|
*
|
|
39
|
-
* 通过
|
|
39
|
+
* 通过 DescribeEnvInfo 接口获取环境信息,判断 LogServices 字段中是否包含有效的
|
|
40
40
|
* 日志主题(TopicId 不为空)。
|
|
41
41
|
*
|
|
42
42
|
* @returns true 表示已开通,false 表示未开通或开通中
|
|
43
43
|
*/
|
|
44
44
|
async checkLogServiceEnabled() {
|
|
45
|
-
var _a;
|
|
46
|
-
const res = await this.cloudService.request('
|
|
45
|
+
var _a, _b;
|
|
46
|
+
const res = await this.cloudService.request('DescribeEnvInfo', {
|
|
47
47
|
EnvId: this.envId
|
|
48
48
|
});
|
|
49
|
-
const
|
|
50
|
-
if (!envInfo) {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
const logServices = envInfo.LogServices;
|
|
49
|
+
const logServices = (_b = (_a = res === null || res === void 0 ? void 0 : res.EnvInfo) === null || _a === void 0 ? void 0 : _a.EnvBaseInfo) === null || _b === void 0 ? void 0 : _b.LogServices;
|
|
54
50
|
if (!logServices || logServices.length === 0) {
|
|
55
51
|
return false;
|
|
56
52
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CI Changelog 生成脚本(MR 粒度)
|
|
5
|
+
*
|
|
6
|
+
* 从上一个 tag 到当前 HEAD 的 merge commit 中提取 MR 信息,
|
|
7
|
+
* 按 conventional commit 分类,生成 changelog 并追加到 changelog.md 头部。
|
|
8
|
+
*
|
|
9
|
+
* 优势:
|
|
10
|
+
* - 每个 MR 对应一条 changelog,避免零散 commit 造成条目过多
|
|
11
|
+
* - 从 MR body 或分支名智能推断变更类型
|
|
12
|
+
* - 自动提取 MR 编号,生成可跳转链接
|
|
13
|
+
*
|
|
14
|
+
* 用法: node scripts/ci/generate-changelog.js
|
|
15
|
+
* 环境变量:
|
|
16
|
+
* MANAGER_NODE_VERSION - 当前发布的版本号(必须)
|
|
17
|
+
* GIT_REPO_URL - 仓库地址(可选,用于生成 MR 链接,默认为工蜂地址)
|
|
18
|
+
* CHANGELOG_OUTPUT - changelog 输出文件路径(可选,设置后将内容写入该文件,供下游步骤读取)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const { execSync } = require('child_process')
|
|
22
|
+
const fs = require('fs')
|
|
23
|
+
const path = require('path')
|
|
24
|
+
|
|
25
|
+
const VERSION = process.env.MANAGER_NODE_VERSION
|
|
26
|
+
if (!VERSION) {
|
|
27
|
+
console.error('❌ 环境变量 MANAGER_NODE_VERSION 未设置')
|
|
28
|
+
process.exit(1)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// 仓库 URL,用于生成 MR 链接(当前 formatMR 暂未使用,预留供后续扩展)
|
|
32
|
+
// const REPO_URL = process.env.GIT_REPO_URL || 'https://git.woa.com/QBase/server-side-sdk/cloudbase-manager-node'
|
|
33
|
+
|
|
34
|
+
// 获取上一个 tag
|
|
35
|
+
// 获取上一个 tag(按版本号排序,排除当前版本)
|
|
36
|
+
// 不使用 git describe(它依赖 commit 祖先关系,在 release 分支上可能跳过中间 tag)
|
|
37
|
+
function getLastTag() {
|
|
38
|
+
try {
|
|
39
|
+
const tags = execSync('git tag --sort=-version:refname', {
|
|
40
|
+
encoding: 'utf8'
|
|
41
|
+
}).trim().split('\n').filter(Boolean)
|
|
42
|
+
|
|
43
|
+
const currentTag = `v${VERSION}`
|
|
44
|
+
for (const tag of tags) {
|
|
45
|
+
// 跳过当前版本,找到第一个有效的 semver tag
|
|
46
|
+
if (tag !== currentTag && /^v?\d+\.\d+\.\d+/.test(tag)) {
|
|
47
|
+
return tag
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return ''
|
|
51
|
+
} catch {
|
|
52
|
+
return ''
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 获取 merge commits(MR 粒度)
|
|
57
|
+
function getMergeCommits(fromTag) {
|
|
58
|
+
const range = fromTag ? `${fromTag}..HEAD` : 'HEAD'
|
|
59
|
+
const separator = '__|__'
|
|
60
|
+
const recordSeparator = '==RECORD=='
|
|
61
|
+
try {
|
|
62
|
+
// %s = subject, %h = short hash, %an = author, %b = body
|
|
63
|
+
const log = execSync(
|
|
64
|
+
`git log ${range} --merges --pretty=format:"%s${separator}%h${separator}%an${separator}%b${recordSeparator}"`,
|
|
65
|
+
{ encoding: 'utf8', maxBuffer: 10 * 1024 * 1024 }
|
|
66
|
+
).trim()
|
|
67
|
+
if (!log) return []
|
|
68
|
+
|
|
69
|
+
return log
|
|
70
|
+
.split(recordSeparator)
|
|
71
|
+
.filter((record) => record.trim())
|
|
72
|
+
.map((record) => {
|
|
73
|
+
const parts = record.trim().split(separator)
|
|
74
|
+
const subject = (parts[0] || '').trim()
|
|
75
|
+
const hash = (parts[1] || '').trim()
|
|
76
|
+
const author = (parts[2] || '').trim()
|
|
77
|
+
const body = (parts[3] || '').trim()
|
|
78
|
+
return { subject, hash, author, body }
|
|
79
|
+
})
|
|
80
|
+
} catch {
|
|
81
|
+
return []
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 从 merge commit subject 中提取 MR 编号和分支名
|
|
86
|
+
// 格式: "Merge branch '<branch>' into '<target>' (merge request !<number>)"
|
|
87
|
+
function parseMergeSubject(subject) {
|
|
88
|
+
const mrMatch = subject.match(/\(merge request !(\d+)\)/)
|
|
89
|
+
const branchMatch = subject.match(/Merge branch '([^']+)' into/)
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
mrNumber: mrMatch ? mrMatch[1] : '',
|
|
93
|
+
branch: branchMatch ? branchMatch[1] : ''
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 从分支名推断变更类型
|
|
98
|
+
// 常见: feature/xxx, fix/xxx, hotfix/xxx, refactor/xxx, docs/xxx, chore/xxx
|
|
99
|
+
function inferTypeFromBranch(branch) {
|
|
100
|
+
const branchLower = branch.toLowerCase()
|
|
101
|
+
if (branchLower.startsWith('feature/') || branchLower.startsWith('feat/')) return 'feat'
|
|
102
|
+
if (branchLower.startsWith('fix/') || branchLower.startsWith('hotfix/') || branchLower.startsWith('bugfix/')) return 'fix'
|
|
103
|
+
if (branchLower.startsWith('refactor/')) return 'refactor'
|
|
104
|
+
if (branchLower.startsWith('perf/')) return 'perf'
|
|
105
|
+
if (branchLower.startsWith('docs/')) return 'docs'
|
|
106
|
+
if (branchLower.startsWith('chore/') || branchLower.startsWith('ci/')) return 'chore'
|
|
107
|
+
if (branchLower.startsWith('revert')) return 'revert'
|
|
108
|
+
return ''
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// 从分支名提取简短描述
|
|
112
|
+
function getDescriptionFromBranch(branch) {
|
|
113
|
+
// 去掉 feature/, fix/ 等前缀
|
|
114
|
+
const desc = branch.replace(/^(feature|feat|fix|hotfix|bugfix|refactor|perf|docs|chore|ci|revert)[/-]/, '')
|
|
115
|
+
// 将 kebab-case 或 camelCase 转为可读文本
|
|
116
|
+
return desc
|
|
117
|
+
.replace(/[-_]/g, ' ')
|
|
118
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
119
|
+
.trim()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// 解析 conventional commit 格式的文本
|
|
123
|
+
function parseConventional(text) {
|
|
124
|
+
const match = text.match(/^(\w+)(?:\(([^)]*)\))?(!)?:\s*(.+)$/)
|
|
125
|
+
if (!match) return null
|
|
126
|
+
const [, type, scope = '', breaking, description] = match
|
|
127
|
+
return { type: type.toLowerCase(), scope, description, breaking: !!breaking }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// 早期过滤:基于 subject 和分支名判断是否为噪音 MR
|
|
131
|
+
function shouldFilterEarly(subject, branch) {
|
|
132
|
+
// 1. 过滤 "Merge branch 'master' into xxx" — 中间合并不是功能变更
|
|
133
|
+
// 注意:有些带 MR 编号的也是中间合并,subject 格式为
|
|
134
|
+
// "Merge branch 'feature/xxx' into 'master' (merge request !N)"
|
|
135
|
+
// 但 body 里是 "Merge branch 'master' into feature/xxx"
|
|
136
|
+
if (/Merge branch '(master|main|dev|develop)' into/i.test(subject)) return true
|
|
137
|
+
|
|
138
|
+
// 2. 分支名为纯 merge 操作的
|
|
139
|
+
if (/^merge[/-](master|main|dev)/i.test(branch)) return true
|
|
140
|
+
|
|
141
|
+
// 3. 分支名就是纯版本号的,如 "2.9.4", "v3.0.1"
|
|
142
|
+
if (/^v?\d+\.\d+(\.\d+)?(-\w+)?$/.test(branch)) return true
|
|
143
|
+
|
|
144
|
+
return false
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// 后期过滤:基于解析后的描述判断是否为噪音 MR
|
|
148
|
+
function shouldFilterByDescription(description, body) {
|
|
149
|
+
// 1. 纯 "update v" 类发版 MR(bump version)
|
|
150
|
+
if (/^update\s*v(\d|$)/i.test(description)) return true
|
|
151
|
+
if (/^bump\s*(version|v)/i.test(description)) return true
|
|
152
|
+
if (/^update\s*version/i.test(description)) return true
|
|
153
|
+
|
|
154
|
+
// 2. 描述就是版本号的,如 "2.9.4", "cli 2.11.7"
|
|
155
|
+
if (/^(cli\s+)?v?\d+\.\d+(\.\d+)?$/i.test(description.trim())) return true
|
|
156
|
+
|
|
157
|
+
// 3. 描述为纯合并操作的
|
|
158
|
+
if (/^合并\s*(master|main)\s*到/i.test(description)) return true
|
|
159
|
+
if (/^Merge\s+branch\s+'(master|main|dev|develop)'\s+into/i.test(description)) return true
|
|
160
|
+
if (/^Merge\s+(master|main)\s+(to|into)/i.test(description)) return true
|
|
161
|
+
|
|
162
|
+
// 4. body 中描述为纯合并的(fallback)
|
|
163
|
+
const bodyTrimmed = body.trim()
|
|
164
|
+
if (/^合并\s*(master|main)\s*到/i.test(bodyTrimmed)) return true
|
|
165
|
+
if (/^Merge\s+branch\s+'(master|main|dev|develop)'\s+into/i.test(bodyTrimmed)) return true
|
|
166
|
+
|
|
167
|
+
return false
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 从描述内容推断更智能的类型(优先于分支名推断)
|
|
171
|
+
function inferTypeFromDescription(description) {
|
|
172
|
+
const descLower = description.toLowerCase()
|
|
173
|
+
// 文档相关关键词
|
|
174
|
+
if (/^(文档|docs?|readme|changelog|注释|说明文档)/.test(descLower)) return 'docs'
|
|
175
|
+
if (/更新文档|补充文档|修改文档|添加文档/.test(description)) return 'docs'
|
|
176
|
+
// 重构相关
|
|
177
|
+
if (/^(重构|refactor)/.test(descLower)) return 'refactor'
|
|
178
|
+
// 性能优化
|
|
179
|
+
if (/^(优化|性能|perf)/.test(descLower)) return 'perf'
|
|
180
|
+
return ''
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// 解析一个 MR 的完整信息
|
|
184
|
+
function parseMR(mergeCommit) {
|
|
185
|
+
const { subject, hash, author, body } = mergeCommit
|
|
186
|
+
const { mrNumber, branch } = parseMergeSubject(subject)
|
|
187
|
+
|
|
188
|
+
// 跳过非 MR 的 merge(如 merge master into feature branch)
|
|
189
|
+
if (!mrNumber) return null
|
|
190
|
+
|
|
191
|
+
// 早期过滤:基于 subject 和分支名
|
|
192
|
+
if (shouldFilterEarly(subject, branch)) return null
|
|
193
|
+
|
|
194
|
+
// 优先从 body 中找 conventional commit 描述
|
|
195
|
+
let type = 'other'
|
|
196
|
+
let scope = ''
|
|
197
|
+
let description = ''
|
|
198
|
+
let breaking = false
|
|
199
|
+
|
|
200
|
+
// body 可能有多行,逐行找第一个有意义的 conventional commit 格式
|
|
201
|
+
const bodyLines = body.split('\n').map((l) => l.trim()).filter(Boolean)
|
|
202
|
+
for (const line of bodyLines) {
|
|
203
|
+
// 跳过分支名本身的重复
|
|
204
|
+
if (line === branch) continue
|
|
205
|
+
const parsed = parseConventional(line)
|
|
206
|
+
if (parsed) {
|
|
207
|
+
type = parsed.type
|
|
208
|
+
scope = parsed.scope
|
|
209
|
+
description = parsed.description
|
|
210
|
+
breaking = parsed.breaking
|
|
211
|
+
break
|
|
212
|
+
}
|
|
213
|
+
// 如果不是 conventional 格式但也不是分支名,用第一个有意义的行作为描述
|
|
214
|
+
if (!description && line.length > 3) {
|
|
215
|
+
description = line
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// 如果 body 没有提供有效信息,从分支名推断
|
|
220
|
+
if (type === 'other' && branch) {
|
|
221
|
+
const inferredType = inferTypeFromBranch(branch)
|
|
222
|
+
if (inferredType) type = inferredType
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (!description && branch) {
|
|
226
|
+
description = getDescriptionFromBranch(branch)
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (!description) {
|
|
230
|
+
description = subject
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 后期过滤:基于解析后的描述内容(过滤 "update v" 等发版 MR)
|
|
234
|
+
if (shouldFilterByDescription(description, body)) return null
|
|
235
|
+
|
|
236
|
+
// 更智能的分类:描述内容优先于分支名推断
|
|
237
|
+
// 例如 feature/ 分支但描述是"文档内容"的,应归类为 docs
|
|
238
|
+
if (type === 'feat' || type === 'fix' || type === 'other') {
|
|
239
|
+
const descType = inferTypeFromDescription(description)
|
|
240
|
+
if (descType) type = descType
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
return { type, scope, description, hash, author, mrNumber, breaking }
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// 分类 MR
|
|
247
|
+
function categorizeMRs(mergeCommits) {
|
|
248
|
+
const categories = {
|
|
249
|
+
breaking: [],
|
|
250
|
+
feat: [],
|
|
251
|
+
fix: [],
|
|
252
|
+
perf: [],
|
|
253
|
+
refactor: [],
|
|
254
|
+
revert: [],
|
|
255
|
+
docs: [],
|
|
256
|
+
chore: [],
|
|
257
|
+
other: []
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
for (const mc of mergeCommits) {
|
|
261
|
+
const parsed = parseMR(mc)
|
|
262
|
+
if (!parsed) continue // 跳过非 MR 的 merge
|
|
263
|
+
|
|
264
|
+
if (parsed.breaking) {
|
|
265
|
+
categories.breaking.push(parsed)
|
|
266
|
+
} else if (categories[parsed.type]) {
|
|
267
|
+
categories[parsed.type].push(parsed)
|
|
268
|
+
} else {
|
|
269
|
+
categories.other.push(parsed)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return categories
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// 格式化一条 MR
|
|
277
|
+
// 类型 → 中文前缀映射
|
|
278
|
+
const TYPE_PREFIX = {
|
|
279
|
+
breaking: '不兼容变更',
|
|
280
|
+
feat: '新增',
|
|
281
|
+
fix: '修复',
|
|
282
|
+
perf: '优化',
|
|
283
|
+
refactor: '重构',
|
|
284
|
+
revert: '回滚',
|
|
285
|
+
docs: '文档',
|
|
286
|
+
chore: '优化',
|
|
287
|
+
other: ''
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function formatMR(parsed) {
|
|
291
|
+
const prefix = TYPE_PREFIX[parsed.type] || ''
|
|
292
|
+
const desc = parsed.description
|
|
293
|
+
// 如果描述已经以前缀开头(如"新增 xxx"),不重复添加
|
|
294
|
+
const needPrefix = prefix && !desc.startsWith(prefix)
|
|
295
|
+
const text = needPrefix ? `${prefix} ${desc}` : desc
|
|
296
|
+
return `- ${text}`
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// 生成 changelog 内容(扁平列表格式)
|
|
300
|
+
function generateChangelog(categories) {
|
|
301
|
+
const date = new Date().toISOString().split('T')[0]
|
|
302
|
+
const lines = [`## @cloudbase/manager-node v${VERSION} 版本发布 🚀(${date})`, '']
|
|
303
|
+
|
|
304
|
+
// 按优先级排列:不兼容变更 → 新功能 → 优化/重构 → 修复 → 其他
|
|
305
|
+
const order = ['breaking', 'feat', 'perf', 'refactor', 'chore', 'revert', 'docs', 'fix', 'other']
|
|
306
|
+
|
|
307
|
+
let hasContent = false
|
|
308
|
+
for (const key of order) {
|
|
309
|
+
const items = categories[key]
|
|
310
|
+
if (items && items.length > 0) {
|
|
311
|
+
hasContent = true
|
|
312
|
+
for (const item of items) {
|
|
313
|
+
lines.push(formatMR(item))
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// 如果完全没有 MR
|
|
319
|
+
if (!hasContent) {
|
|
320
|
+
lines.push('- 版本发布')
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
lines.push('')
|
|
324
|
+
return lines.join('\n')
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
// 确保 CI 环境有完整的 git 历史和 tags
|
|
328
|
+
function ensureFullGitHistory() {
|
|
329
|
+
try {
|
|
330
|
+
// CI 通常是浅克隆,需要 unshallow 才能拿到完整历史
|
|
331
|
+
const isShallow = fs.existsSync(path.join(
|
|
332
|
+
execSync('git rev-parse --git-dir', { encoding: 'utf8' }).trim(),
|
|
333
|
+
'shallow'
|
|
334
|
+
))
|
|
335
|
+
if (isShallow) {
|
|
336
|
+
console.log('🔄 检测到浅克隆,正在获取完整历史...')
|
|
337
|
+
execSync('git fetch --unshallow', { encoding: 'utf8', stdio: 'inherit' })
|
|
338
|
+
}
|
|
339
|
+
} catch {
|
|
340
|
+
// 非浅克隆或 unshallow 失败,忽略
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
try {
|
|
344
|
+
// 确保拉取所有 tags(CI 默认可能不拉 tags)
|
|
345
|
+
console.log('🏷️ 拉取所有 tags...')
|
|
346
|
+
execSync('git fetch --tags', { encoding: 'utf8', stdio: 'inherit' })
|
|
347
|
+
} catch {
|
|
348
|
+
console.warn('⚠️ 拉取 tags 失败,将使用本地已有的 tags')
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// 主流程
|
|
353
|
+
function main() {
|
|
354
|
+
console.log(`📝 正在为 v${VERSION} 生成 changelog(MR 粒度)...`)
|
|
355
|
+
|
|
356
|
+
// CI 环境需要确保有完整历史和 tags
|
|
357
|
+
ensureFullGitHistory()
|
|
358
|
+
|
|
359
|
+
const lastTag = getLastTag()
|
|
360
|
+
if (lastTag) {
|
|
361
|
+
console.log(` 上一个 tag: ${lastTag}`)
|
|
362
|
+
} else {
|
|
363
|
+
console.log(' 未找到历史 tag,将使用所有 merge commit')
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const mergeCommits = getMergeCommits(lastTag)
|
|
367
|
+
console.log(` 找到 ${mergeCommits.length} 个 merge commit`)
|
|
368
|
+
|
|
369
|
+
const categories = categorizeMRs(mergeCommits)
|
|
370
|
+
const totalMRs = Object.values(categories).reduce((sum, arr) => sum + arr.length, 0)
|
|
371
|
+
console.log(` 有效 MR: ${totalMRs} 个`)
|
|
372
|
+
|
|
373
|
+
const newChangelog = generateChangelog(categories)
|
|
374
|
+
|
|
375
|
+
console.log('\n--- 生成的 changelog ---')
|
|
376
|
+
console.log(newChangelog)
|
|
377
|
+
console.log('--- end ---\n')
|
|
378
|
+
|
|
379
|
+
// 读取现有 changelog
|
|
380
|
+
const changelogPath = path.join(__dirname, '../../CHANGELOG.md')
|
|
381
|
+
let existingContent = ''
|
|
382
|
+
if (fs.existsSync(changelogPath)) {
|
|
383
|
+
existingContent = fs.readFileSync(changelogPath, 'utf8')
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 插入新内容到头部
|
|
387
|
+
const finalContent = newChangelog + '\n---\n\n' + existingContent
|
|
388
|
+
|
|
389
|
+
fs.writeFileSync(changelogPath, finalContent, 'utf8')
|
|
390
|
+
console.log('📄 changelog.md 已更新')
|
|
391
|
+
|
|
392
|
+
// 将本次生成的 changelog 内容单独输出到指定文件
|
|
393
|
+
// 用于 CI 流水线中传递给下游步骤(如企微通知)
|
|
394
|
+
// 用法: export CHANGELOG_OUTPUT="/path/to/changelog_content.md"
|
|
395
|
+
const outputPath = process.env.CHANGELOG_OUTPUT
|
|
396
|
+
if (outputPath) {
|
|
397
|
+
// 企微消息有 4KB 左右的大小限制,超长时截断并添加提示
|
|
398
|
+
const MAX_WEWORK_SIZE = 3800 // 预留一些余量
|
|
399
|
+
let outputContent = newChangelog
|
|
400
|
+
if (Buffer.byteLength(outputContent, 'utf8') > MAX_WEWORK_SIZE) {
|
|
401
|
+
// 按行截断,保留标题和尽量多的条目
|
|
402
|
+
const lines = outputContent.split('\n')
|
|
403
|
+
let truncated = ''
|
|
404
|
+
for (const line of lines) {
|
|
405
|
+
const next = truncated + line + '\n'
|
|
406
|
+
if (Buffer.byteLength(next, 'utf8') > MAX_WEWORK_SIZE - 100) {
|
|
407
|
+
break
|
|
408
|
+
}
|
|
409
|
+
truncated = next
|
|
410
|
+
}
|
|
411
|
+
const totalItems = newChangelog.split('\n').filter(l => l.startsWith('- ')).length
|
|
412
|
+
const keptItems = truncated.split('\n').filter(l => l.startsWith('- ')).length
|
|
413
|
+
truncated += `\n... 等共 ${totalItems} 项变更(已截断,完整内容见 changelog.md)\n`
|
|
414
|
+
outputContent = truncated
|
|
415
|
+
console.log(`⚠️ changelog 过长 (${Buffer.byteLength(newChangelog, 'utf8')} bytes),已截断为 ${keptItems}/${totalItems} 项`)
|
|
416
|
+
}
|
|
417
|
+
fs.writeFileSync(outputPath, outputContent, 'utf8')
|
|
418
|
+
console.log(`📄 changelog 内容已输出到: ${outputPath}`)
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
main()
|
package/types/env/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Environment } from '../environment';
|
|
2
2
|
import { IResponseInfo, AuthDomain, EnvInfo, LoginConfigItem, ICheckTcbServiceRes, ICreatePostpayRes, EnvBillingInfoItem, PriceResult } from '../interfaces';
|
|
3
3
|
import { CalculatePackageCreatePriceParams, CalculatePackageRenewPriceParams, CalculatePackageModifyPriceParams, DescribeHttpServiceRouteParams, DescribeHttpServiceRouteRes, CreateHttpServiceRouteParams, CreateHttpServiceRouteRes, ModifyHttpServiceRouteParams, ModifyHttpServiceRouteRes, DeleteHttpServiceRouteParams, DeleteHttpServiceRouteRes, BindCustomDomainParams, BindCustomDomainRes, DeleteCustomDomainParams, DeleteCustomDomainRes } from './type';
|
|
4
|
-
import { CreateBillingDealParams, CreateEnvParams, BaasPackageInfo, DescribeBaasPackageListParams, ModifyEnvPlanParams, RenewEnvParams, DestroyEnvParams, DescribeEnvsParams, DescribeEnvAccountCircleParams, DescribeEnvAccountCircleRes, DescribeCreditsUsageDetailParams, EnvPkgCreditsUsage } from '../interfaces/tcb.interface';
|
|
4
|
+
import { CreateBillingDealParams, CreateEnvParams, BaasPackageInfo, DescribeBaasPackageListParams, ModifyEnvPlanParams, RenewEnvParams, DestroyEnvParams, DescribeEnvsParams, DescribeEnvInfoParams, DescribeEnvInfoResponse, DescribeEnvAccountCircleParams, DescribeEnvAccountCircleRes, DescribeCreditsUsageDetailParams, EnvPkgCreditsUsage } from '../interfaces/tcb.interface';
|
|
5
5
|
type SOURCE = 'miniapp' | 'qcloud';
|
|
6
6
|
interface IDeleteDomainRes {
|
|
7
7
|
RequestId: string;
|
|
@@ -629,6 +629,16 @@ export declare class EnvService {
|
|
|
629
629
|
EnvList: EnvInfo[];
|
|
630
630
|
Total: number;
|
|
631
631
|
}>;
|
|
632
|
+
/**
|
|
633
|
+
* 查询单个环境信息
|
|
634
|
+
*
|
|
635
|
+
* 用于单个环境查询时,规避 DescribeEnvs 的 backend issues。
|
|
636
|
+
* 当 EnvId 参数存在时,应优先使用本方法而非 describeEnvs。
|
|
637
|
+
*
|
|
638
|
+
* @param params 查询参数,必须包含 EnvId
|
|
639
|
+
* @returns 环境信息响应
|
|
640
|
+
*/
|
|
641
|
+
describeEnvInfo(params: DescribeEnvInfoParams): Promise<DescribeEnvInfoResponse>;
|
|
632
642
|
/**
|
|
633
643
|
* 修改环境名称
|
|
634
644
|
* @param {string} alias 环境名称
|
|
@@ -460,3 +460,67 @@ export interface EnvPkgCreditsUsage {
|
|
|
460
460
|
ReportValue?: number;
|
|
461
461
|
OriginCredits?: number;
|
|
462
462
|
}
|
|
463
|
+
/**
|
|
464
|
+
* DescribeEnvInfo API - 查询单个环境信息
|
|
465
|
+
* 替代 DescribeEnvs 用于单环境查询,用于规避 backend issues when filtering by EnvId
|
|
466
|
+
*/
|
|
467
|
+
export interface DescribeEnvInfoParams {
|
|
468
|
+
EnvId: string;
|
|
469
|
+
}
|
|
470
|
+
export interface EnvBaseInfo {
|
|
471
|
+
EnvId?: string;
|
|
472
|
+
Alias?: string;
|
|
473
|
+
Status?: string;
|
|
474
|
+
EnvType?: string;
|
|
475
|
+
Region?: string;
|
|
476
|
+
PackageId?: string;
|
|
477
|
+
PackageName?: string;
|
|
478
|
+
Databases?: DatabasesInfo[];
|
|
479
|
+
Storages?: StorageInfo[];
|
|
480
|
+
Functions?: FunctionInfo[];
|
|
481
|
+
PayMode?: string;
|
|
482
|
+
Source?: string;
|
|
483
|
+
Tags?: Array<{
|
|
484
|
+
Key: string;
|
|
485
|
+
Value: string;
|
|
486
|
+
}>;
|
|
487
|
+
CreateTime?: string;
|
|
488
|
+
UpdateTime?: string;
|
|
489
|
+
EnvPreferences?: Record<string, any>;
|
|
490
|
+
IsAutoDegrade?: boolean;
|
|
491
|
+
IsDauPackage?: boolean;
|
|
492
|
+
PostgreSQL?: Record<string, any>;
|
|
493
|
+
LogServices?: Array<{
|
|
494
|
+
LogsetId?: string;
|
|
495
|
+
TopicId?: string;
|
|
496
|
+
TopicName?: string;
|
|
497
|
+
Region?: string;
|
|
498
|
+
}> | null;
|
|
499
|
+
CustomLogServices?: Array<{
|
|
500
|
+
CreateTime: string;
|
|
501
|
+
ClsLogsetId: string;
|
|
502
|
+
ClsTopicId: string;
|
|
503
|
+
ClsRegion: string;
|
|
504
|
+
}> | null;
|
|
505
|
+
}
|
|
506
|
+
export interface BillingInfo {
|
|
507
|
+
StartTime?: string;
|
|
508
|
+
EndTime?: string;
|
|
509
|
+
Status?: string;
|
|
510
|
+
}
|
|
511
|
+
export interface UserInfo {
|
|
512
|
+
AppId?: string;
|
|
513
|
+
Uin?: string;
|
|
514
|
+
WxAppId?: string;
|
|
515
|
+
}
|
|
516
|
+
export interface DescribeEnvInfoResponse extends IResponseInfo {
|
|
517
|
+
EnvInfo?: {
|
|
518
|
+
EnvBaseInfo?: EnvBaseInfo;
|
|
519
|
+
BillingInfo?: BillingInfo;
|
|
520
|
+
UserInfo?: UserInfo;
|
|
521
|
+
Eid?: number;
|
|
522
|
+
CircleStartTime?: string;
|
|
523
|
+
CircleEndTime?: string;
|
|
524
|
+
SiteFlag?: number;
|
|
525
|
+
};
|
|
526
|
+
}
|
package/types/log/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export interface ILogServiceEntry {
|
|
|
11
11
|
Region?: string;
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* 纯函数:根据
|
|
14
|
+
* 纯函数:根据 DescribeEnvInfo 返回的 LogServices 列表判断日志服务是否已开通。
|
|
15
15
|
* 已开通条件:至少一个条目有有效的 TopicId。
|
|
16
16
|
*/
|
|
17
17
|
export declare function isLogServiceEnabled(logServices?: ILogServiceEntry[]): boolean;
|
|
@@ -23,7 +23,7 @@ export declare class LogService {
|
|
|
23
23
|
/**
|
|
24
24
|
* 检查当前环境的日志服务是否已开通
|
|
25
25
|
*
|
|
26
|
-
* 通过
|
|
26
|
+
* 通过 DescribeEnvInfo 接口获取环境信息,判断 LogServices 字段中是否包含有效的
|
|
27
27
|
* 日志主题(TopicId 不为空)。
|
|
28
28
|
*
|
|
29
29
|
* @returns true 表示已开通,false 表示未开通或开通中
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(TCB_BASE_URL=https://tcb.pre.tencentcloudapi.woa.com npm test -- --testPathPattern=cloudApp 2>&1)",
|
|
5
|
-
"Bash(TCB_BASE_URL=https://tcb.pre.tencentcloudapi.woa.com npm test 2>&1)",
|
|
6
|
-
"Bash(TCB_BASE_URL=https://tcb.pre.tencentcloudapi.woa.com npm test -- --testPathPattern=cloudApp --testNamePattern=\"获取版本列表\")"
|
|
7
|
-
]
|
|
8
|
-
}
|
|
9
|
-
}
|